Excel催化剂开源第9波-VSTO开发图片插入功能,图片带事件

2021-08-19 15:03:31 浏览数 (1)

图片插入功能,这个是Excel插件的一大刚需,但目前在VBA接口里开发,如果用Shapes.AddPicture方法插入的图片,没法对其添加事件,且图片插入后需等比例调整纵横比例特别麻烦,特别是对于插入的多个图片非统一的纵横尺寸比时。

例如一个很经典的需求是将插入的缩略图放大操作,没法点击、双击之类的事件响应对应的放大操作。

在VBA的方法中,还有一个方式,用窗体控件Image,可以实现图片插入后有事件关联。

窗体控件Image方法插入

但很遗憾的是,这个方法中插入的图片,当没有点选图片时,图片会有些模糊的情况。具体可对比下图左右部分。这个模糊的问题,还是老大难,难于达到完美。如果换成是VSTO的宿主项PictureBox,就完美解决了。

两种不同窗体控件方式插入图片

例如下图中的,用鼠标右键事件,调出上下文菜单。和Excel催化剂中的双击图片进行图片下载本地并调用Windows图片查看器实现图片的放大功能。

可以关联事件的插入图片方法

双击图片事件

保留有正确的原始图片纵横比例

笔者觉得这是个接近完美的解决方案(用窗体控件的方式插入图片,用户不能直接选择图片、移动图片,会有种奇怪的感觉)。

具体实现

使用VSTO的PictureBox容器,将其图片属性设置成对应的图片对象即可,此处传入的图片对象是Image对象,不必像VBA方法里的只能传入文件全路径,可以方便某些数据库等方式存储二进制图片格式时,直接调用。

代码语言:javascript复制
 public static string AddPictureBoxToVstoWorkSheet(Excel.Range dstRange, Image img, string fileExt, bool hasBordersMargin, bool isLookupFromRangValue, int offsetRow, int offsetCol)
        {
            string picName = Guid.NewGuid().ToString();
            Excel.Worksheet actSht = dstRange.Worksheet;
            Worksheet vstoActSht = Globals.Factory.GetVstoObject(actSht);
            PictureBox pictureBox = new PictureBox();
            pictureBox.Image = img;
            pictureBox.SizeMode = PictureBoxSizeMode.Zoom;

            pictureBox.MouseDoubleClick  = PictureBox_MouseDoubleClick;

            AddContextMenuToPictureBox(pictureBox);

            pictureBox.Name = picName;
            ControlSite controlSite;
            if (hasBordersMargin)
            {
                controlSite = vstoActSht.Controls.AddControl(
                       control: pictureBox,
                       top: dstRange.Top   2,
                       left: dstRange.Left   2,
                       height: dstRange.Height - 4,
                       width: dstRange.Width - 4,
                       name: picName);
            }
            else
            {
                controlSite = vstoActSht.Controls.AddControl(
                      control: pictureBox,
                      range: dstRange,
                      name: picName);
            }



            string srcFileName = string.Empty;
            Excel.Range srcFileNameRange = null;
            if (isLookupFromRangValue)
            {
                //取回图片文件名的单元格,可拿到文件名,我初始的单元格地址,为后面有可能插入过行、列来重新定位。
                srcFileNameRange = dstRange.Offset[-offsetRow, -offsetCol];
                srcFileName = srcFileNameRange == null ? "" : srcFileNameRange.Value2.ToString();
            }

            Common.vstoPicInfos.Add(
                            new Entity.VstoPicInfo()
                            {
                                PicName = picName,
                                IsLookupFromRangValue = isLookupFromRangValue,
                                PicOleName = controlSite.Name,
                                PicSht = actSht,
                                SrcFileNameRange = srcFileNameRange,
                                DstRange = dstRange,
                                SrcFileName = srcFileName,
                                ImageExt = fileExt,
                                PictureBoxImage = img
                            });
            return picName;
        }

其中核心代码有: 得到一个VSTO的WorkSheet对象和新建一个PictureBox对象。

代码语言:javascript复制
  Excel.Worksheet actSht = dstRange.Worksheet;
            Worksheet vstoActSht = Globals.Factory.GetVstoObject(actSht);
            PictureBox pictureBox = new PictureBox();
            pictureBox.Image = img;
            pictureBox.SizeMode = PictureBoxSizeMode.Zoom;

通过方法vstoActSht.Controls.AddControl进行创建一个Picturebox到工作表中。具体各参数的意义和VBA的AddPicture方法类似,可自行查阅文档。

代码语言:javascript复制
   controlSite = vstoActSht.Controls.AddControl(
                       control: pictureBox,
                       top: dstRange.Top   2,
                       left: dstRange.Left   2,
                       height: dstRange.Height - 4,
                       width: dstRange.Width - 4,
                       name: picName);

通过以上的方法,即可创建了一个PictureBox对象容器,并且图片是我们传入的图片。这当中可以绑定一些PictureBox事件和上下文菜单等,如:

代码语言:javascript复制
pictureBox.MouseDoubleClick  = PictureBox_MouseDoubleClick;

        private static void AddContextMenuToPictureBox(PictureBox pictureBox)
        {
            ContextMenuStrip contextMenuStrip = new ContextMenuStrip();
            string[] btnsText = { "复制另存为", "移动至其他区域", "删除图片" };
            foreach (var btnText in btnsText)
            {
                ToolStripButton toolStripButton = new ToolStripButton(btnText);
                toolStripButton.Tag = pictureBox;
                toolStripButton.Click  = PictureBoxToolStripButton_Click;
                contextMenuStrip.Items.Add(toolStripButton);
            }

            pictureBox.ContextMenuStrip = contextMenuStrip;
        }

通过此方法插入的PictureBox,保存关闭后,Excel文件因无法在关闭状态下存储PictureBox对象,将会将其转换为OLE对象存储,设置过的事件将失效,Excel催化剂用了复杂的手段来恢复它,下篇其他技术时再进行介绍

结语

图片插入这样一个刚需功能,在Excel催化剂上已经将其做到极致化的体验,也是有别于传统方式所实现的,在用户体验上,相信有对比后,也会喜欢上Excel催化剂这样的突破性的方式。

此篇已经对核心的技术及代码完成开源公开化,有兴趣的朋友们,可以一试。

0 人点赞