Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >VsxHowTo-把Windows Forms Designer作为自己的编辑器(1)

VsxHowTo-把Windows Forms Designer作为自己的编辑器(1)

作者头像
明年我18
发布于 2019-09-18 06:36:02
发布于 2019-09-18 06:36:02
88000
代码可运行
举报
文章被收录于专栏:明年我18明年我18
运行总次数:0
代码可运行

有时候我们想实现一个表单设计器,在这个设计器实现拖动控件、设置属性等功能。VS内置的WinForm Designer无疑是最好的选择,那么,我们怎样才能把它作为自己的编辑器呢?

首先,我们来看一下VS编辑器的结构,下图摘自LearnVSXNow

从上图可以看出,要实现一个编辑器,实现需要Editor Factory、Document Data和Document View。其中,我们不需要再实现Document View了,因为我们要重用VS的Winform Designer,它就是Document View,我们的目的就是把它调用出来。

另外,我们只实现单视图的编辑器。

首先,我们先来创建一个VSPackage项目,项目名称为“WinFormDesigner”,不用添加ToolWindow和Menu。接下来就要实现Document Data和Editor Factory了。

实现Document Data

添加一个DocumentData的类,这一次我们只让它实现IVsPersistDocData接口,其他两个接口IPersistFileFormat和IOleCommandTarget我们以后再实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.VisualStudio.Shell.Interop;using Microsoft.VisualStudio; namespace Company.WinFormDesigner{    class DocumentData : IVsPersistDocData    {        private Guid _factoryGuid = typeof(DocumentEditorFactory).GUID;         #region IVsPersistDocData 成员         int IVsPersistDocData.Close()        {            return VSConstants.S_OK;        }         int IVsPersistDocData.GetGuidEditorType(out Guid pClassID)        {            pClassID = _factoryGuid;            return VSConstants.S_OK;        }         int IVsPersistDocData.IsDocDataDirty(out int pfDirty)        {            pfDirty = 0;            return VSConstants.S_OK;        }         int IVsPersistDocData.IsDocDataReloadable(out int pfReloadable)        {            pfReloadable = 1;            return VSConstants.S_OK;        }         int IVsPersistDocData.LoadDocData(string pszMkDocument)        {            return VSConstants.S_OK;        }         int IVsPersistDocData.OnRegisterDocData(uint docCookie, IVsHierarchy pHierNew, uint itemidNew)        {            return VSConstants.S_OK;        }         int IVsPersistDocData.ReloadDocData(uint grfFlags)        {            return VSConstants.S_OK;        }         int IVsPersistDocData.RenameDocData(uint grfAttribs, IVsHierarchy pHierNew, uint itemidNew, string pszMkDocumentNew)        {            return VSConstants.S_OK;        }         int IVsPersistDocData.SaveDocData(VSSAVEFLAGS dwSave, out string pbstrMkDocumentNew, out int pfSaveCanceled)        {            pbstrMkDocumentNew = null;            pfSaveCanceled = 0;                       return VSConstants.S_OK;        }         int IVsPersistDocData.SetUntitledDocPath(string pszDocDataPath)        {            return VSConstants.S_OK;        }         #endregion    }}

从代码里可以看到,DocumentData这个类只是简单的实现了IVsPersistDocData接口,所有的方法只是简单的返回VSConstants.S_OK,并没有真正实现诸如LoadDocData和SaveDocData这样的方法。这是因为这篇文章的目的是如何重用WinForm Designer,而暂不涉及文件的读取和存储,我会在后续的文章里逐步完善DocumentData。

实现Editor Factory

添加类DocumentEditorFactory,并实现IVsEditorFactory接口。我们的目的,是要在IVsEditorFactory.CreateEditorInstance方法里,调出VS的form designer,并赋值给out参数ppunkDocView。

在这里我们需要利用Microsoft.VisualStudio.Designer.Interfaces.IVSMDDesignerService接口(要使用该接口,要添加对Microsoft.VisualStudio.Designer.Interfaces程序集的引用)的CreateDesigner方法,该方法接受两个参数,第一个参数是Microsoft.VisualStudio.OLE.Interop.IServiceProvider,第二个参数是DesignerLoader,所以,我们先要添加一个DesignerLoader的类,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.ComponentModel.Design.Serialization;using System.Windows.Forms; namespace Company.WinFormDesigner{    class DesignerLoader : BasicDesignerLoader    {               protected override void PerformFlush(IDesignerSerializationManager serializationManager)        {                    }         protected override void PerformLoad(IDesignerSerializationManager serializationManager)        {            LoaderHost.Container.Add(new UserControl());        }    }}

我们的DesignerLoader类也只是“稍微实现”了一下,只是在PerformLoad的时候往LoaderHost里加了一个UserControl。这样LoaderHost的RootComponent就是一个UserControl了,在设计器加载的时候就会加载UserControl的RootDesigner。这其实也是我们重用WinForm Designer的最关键的一步,我们其他的代码都是为了这句服务的,因为VS加载什么设计器,是由DesignerHost的RootComponent的RootDesigner决定的,不清楚的同学可以google一下IRootDesigner。

有了DesignerLoader之后,就可以实现DocumentEditorFactory了,该类的实现如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
using System;using System.IO;using System.Runtime.InteropServices;using System.Windows.Forms;using Microsoft.VisualStudio;using Microsoft.VisualStudio.Designer.Interfaces;using Microsoft.VisualStudio.OLE.Interop;using Microsoft.VisualStudio.Shell;using Microsoft.VisualStudio.Shell.Interop;using Microsoft.VisualStudio.TextManager.Interop;using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider;using IServiceProvider = System.IServiceProvider;using System.ComponentModel.Design; namespace Company.WinFormDesigner{    class DocumentEditorFactory : IVsEditorFactory    {        private IServiceProvider serviceProvider;         #region IVsEditorFactory 成员         public int Close()        {            return VSConstants.S_OK;        }         public int CreateEditorInstance(            uint grfCreateDoc,            string pszMkDocument,            string pszPhysicalView,            IVsHierarchy pvHier,            uint itemid,            IntPtr punkDocDataExisting,            out IntPtr ppunkDocView,            out IntPtr ppunkDocData,            out string pbstrEditorCaption,            out Guid pguidCmdUI,            out int pgrfCDW)        {            // Initialize out parameters            ppunkDocView = IntPtr.Zero;            ppunkDocData = IntPtr.Zero;            pguidCmdUI = Guid.Empty;            pgrfCDW = 0;            pbstrEditorCaption = string.Empty;             // Validate inputs            if ((grfCreateDoc & (VSConstants.CEF_OPENFILE | VSConstants.CEF_SILENT)) == 0)                return VSConstants.E_INVALIDARG;              try            {                var designerService = serviceProvider.GetService(typeof(IVSMDDesignerService)) as IVSMDDesignerService;                var oleServiceProvider = serviceProvider.GetService(typeof(IOleServiceProvider)) as IOleServiceProvider;                var designerLoader = new DesignerLoader();                 IVSMDDesigner designer = designerService.CreateDesigner(oleServiceProvider, designerLoader);                 object designerView = designer.View;                pguidCmdUI = designer.CommandGuid;                ppunkDocView = Marshal.GetIUnknownForObject(designerView);                 var data = new DocumentData();                ppunkDocData = Marshal.GetIUnknownForObject(data);            }                        finally            {                if (ppunkDocView == IntPtr.Zero)                {                    if (punkDocDataExisting != ppunkDocData && ppunkDocData != IntPtr.Zero)                    {                        Marshal.Release(ppunkDocData);                        ppunkDocData = IntPtr.Zero;                    }                }            }                         return VSConstants.S_OK;        }          public int MapLogicalView(ref Guid rguidLogicalView, out string pbstrPhysicalView)        {            pbstrPhysicalView = null; // --- Initialize out parameter             // --- We support only a single physical view            if (VSConstants.LOGVIEWID_Primary == rguidLogicalView)            {                // --- Primary view uses NULL as physicalView                return VSConstants.S_OK;            }            else            {                // --- You must return E_NOTIMPL for any unrecognized logicalView values                return VSConstants.E_NOTIMPL;            }        }         public int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp)        {            serviceProvider = new Microsoft.VisualStudio.Shell.ServiceProvider(psp);            return VSConstants.S_OK;        }         #endregion            }}

由于我们只需要做单视图的设计器,所以在MapLogicalView方法里,只在rguidLogicalView参数为VSConstants.LOGVIEWID_Primary的时候返回VSConstants.S_OK。

在CreateEditorInstance方法里,利用serviceProvider得到IVSMDDesignerService和Microsoft.VisualStudio.OLE.Interop.IServiceProvider的实例,接着创建了一个DesignerLoader的实例,然后就调用IVSMDDesignerService.CreateDesigner方法创建了一个IVSMDDesigner的对象,该对象的View属性,就是我们要的Winform Designer。最后,把View和DocumentData对象的指针赋给相应的out参数。

注册Editor Factory

注册DocumentEditorFactory的方法和注册其他Editor Factory的方法一样,即在Package初始化的时候,调用RegisterEditorFactory方法。并为Package类添加一个ProvideEditorExtension的Attribute声明:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
using System;using System.Diagnostics;using System.Globalization;using System.Runtime.InteropServices;using Microsoft.VisualStudio.Shell; namespace Company.WinFormDesigner{       [PackageRegistration(UseManagedResourcesOnly = true)]    [DefaultRegistryRoot("Software\\Microsoft\\VisualStudio\\9.0")]    [InstalledProductRegistration(false, "#110", "#112", "1.0", IconResourceID = 400)]    [ProvideLoadKey("Standard", "1.0", "WinFormDesigner", "Company", 1)]    [Guid(GuidList.guidWinFormDesignerPkgString)]    //将EditorFactory和文件扩展名关联起来    [ProvideEditorExtension(typeof(DocumentEditorFactory), ".form", 100)]    public sealed class WinFormDesignerPackage : Package    {        public WinFormDesignerPackage()        {            Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));        }         #region Package Members         protected override void Initialize()        {            Trace.WriteLine (string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString()));            base.Initialize();             //注册Editor Factory            RegisterEditorFactory(new DocumentEditorFactory());         }         #endregion    }}

在这里,我们把DocumentEditorFactory好*.form文件关联了起来。

测试我们的设计器

新建一个文本文件,并把扩展名改为.form,然后用vs Experimental hive打开,可以看到VS加载了Winform设计器:

但这个设计器是有问题的,例如拖动控件到设计器后,控件没有自动命名;文档窗口也不会随着修改而自动加入*的标记;不能undo/redo;当然,最大的问题,不能保存数据。

让我们在后续文章里逐步完善这个设计器。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2010-07-19 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
VsxHowTo -- 把Windows Forms Designer作为自己的编辑器(2)
我们在上一篇文章里利用Windows Forms Designer做了一个简单的表单设计器,但这个设计器还存在一些问题,比如控件不能自动命名;文档窗口不会自动加入dirty标记;不能undo/redo和copy/paste;不能保存和读取数据等等。这一篇我们来逐一解决这些问题。
明年我18
2019/09/18
4250
VsxHowTo -- 把Windows Forms Designer作为自己的编辑器(3)
在前两篇里,我向大家介绍了如何把vs的windows forms designer作为自己的自定义编辑器,这这篇文章里我再介绍一些大家可能关心的和设计器相关的其他问题。
明年我18
2019/09/18
8300
VsxHowTo -- 把Windows Forms Designer作为自己的编辑器(3)
(翻译)LearnVSXNow! #16- 创建简单的编辑器-2
(LearnVSXNow又开始继续翻译了,为了提高翻译速度,不再对每句话进行翻译,并且会用自己的理解来代替不好翻译的句子。理解不一定正确,见谅。)
明年我18
2019/09/18
8660
(翻译)LearnVSXNow! #16- 创建简单的编辑器-2
VS 2008 Package 备忘
今天研究了一下vs 2008 package的部署,即怎样放置package中的dll、ProjectTemplate和ItemTemplate,趁着热乎劲先记录下来,免得以后忘记。
明年我18
2019/09/18
7600
VS 2008 Package 备忘
自己动手把 VTK 封装成 Windows Forms 控件
虽然 Kitware 提供了 ActiViz 作为 vtk 的 .Net 库,但这是一个收费软件,并且在调试模式下一直存在程序退出时资源无法释放的问题,于是自己动手做了 vtk 的 .Net 封装库。
秦建辉
2024/08/16
3713
(翻译)LearnVSXNow!-#5 VSX的基本概念
在前几篇文章中,我们只是通过创建和“分析”三个非常小的、由VSPackage向导生成的package来管中窥豹地见识了一下VSX。这些例子有助于我们熟悉创建小的package的基本步骤。但是,我们必须更深入一些, 看一下Visual Studo IDE是怎样工作的,以及它是怎样集成package的。
明年我18
2019/09/18
9270
Msbuild的三种利用方式
碎碎念:最近一直没更新文章,团队的小伙伴都到了找实习的时候,所以都在忙着找工作,恰巧又碰上国赛,耽搁了几天,不过目前都已尘埃落定,有几个去了甲方,都是三大运营商,还有的去了国内的一线乙方,对于我们这个普本来说成绩已然不错,继续加油。ps:有需要发招聘的老板可以私聊我哦,后台回复合作即可。
鸿鹄实验室
2021/07/06
1K0
Msbuild的三种利用方式
Windows查看硬盘总字节数
Get-PhysicalDisk命令虽然好,但是不适用低版本系统,比如Server2008R2和Win7
Windows技术交流
2023/12/27
3090
WPF实现基础控件之托盘的示例代码分享
using System.Windows.Controls.Primitives;
用户7718188
2022/11/06
7540
WPF 使用 Silk.NET 进行 DirectX 渲染入门
本文告诉大家如何使用 dotnet 基金会新开源的 Silk.NET 库调用 DirectX 进行渲染的方法。此库是对 DirectX 的底层基础封装,用上了 dotnet 和 C# 的各个新特性,相对来说基础性能较好,也许后续可以考虑作为 SharpDx 的代替
林德熙
2021/12/27
3.1K0
WPF 使用 Silk.NET 进行 DirectX 渲染入门
(翻译)LearnVSXNow! #10 创建我们第一个工具集-重用代码
我们在第6和第7篇创建的Calculate小工具窗还有很多可以改进的地方,所以在这篇文章里,我们不会开发新的功能,而是重构我们的代码,封装出可以重用的类和方法。
明年我18
2019/09/18
4170
(翻译)LearnVSXNow! #10 创建我们第一个工具集-重用代码
使用C#编写.NET分析器-第三部分
这是在Datadog公司任职的Kevin Gosse大佬使用C#编写.NET分析器的系列文章之一,在国内只有很少很少的人了解和研究.NET分析器,它常被用于APM(应用性能诊断)、IDE、诊断工具中,比如Datadog的APM,Visual Studio的分析器以及Rider和Reshaper等等。之前只能使用C++编写,自从.NET NativeAOT发布以后,使用C#编写变为可能。
InCerry
2023/08/31
2480
使用C#编写.NET分析器-第三部分
VisualStudio 扩展开发 安装 Visual Studio SDK添加菜单增加选项传到商店获取工程所有项目升级 2017
本文主要:如何开发一个 visual Studio 扩展,其实扩展也叫插件。 那么就是如何开发一个 vs插件。 本文也记录了我调试 VisualStudio 半个月过程遇到的坑。
林德熙
2018/09/19
6.6K0
VisualStudio 扩展开发
            安装 Visual Studio SDK添加菜单增加选项传到商店获取工程所有项目升级 2017
WPF 打开资源管理器且选中某个文件
本文将和大家介绍如何在 Windows 系统上使用 SHOpenFolderAndSelectItems 方法打开资源管理器且选中给定的文件
林德熙
2024/11/17
1080
c# 调用Microsoft XPS Document Writer打印机,将Pdf文件转换成Xps文件「建议收藏」
最近碰到个项目,其中有个需要将pdf文件转换成xps文件的功能,xps文件还算是新东西,所以基本没啥了解,通过一段时间的调查,
全栈程序员站长
2022/08/19
2.5K0
C# 使用openxml解析PPTX中的文本内容
      本文讨论的仅针对微软Office 2007以后的(OOXML定义)PowerPoint文档,Office 2007以前的用二进制格式定义的(ppt格式)文档不在本文讨论范围。
czwy
2023/10/22
5240
C# 使用openxml解析PPTX中的文本内容
C# Windows服务开发
我要开发一个系统服务,服务的作用是定时检测并关闭其他应用的弹窗,但是开发后却发现,服务在运行是压根获取不到任何窗口。
码客说
2022/05/23
1.3K0
C# Windows服务开发
dotnet 读 WPF 源代码笔记 启动欢迎界面 SplashScreen 的原理
本文是我在读 WPF 源代码做的笔记。在 WPF 中的启动界面,为了能让 WPF 的启动界面显示足够快,需要在应用的 WPF 主机还没有启动完成之前就显示出启动图,此时的启动图需要自己解析图片同时也需要自己创建显示窗口
林德熙
2020/12/22
1.1K0
windows logon API
using System; using System.Collections.Generic; using System.Text; using System.ComponentModel; using System.Security; using System.Security.Principal; using System.Runtime; using System.Runtime.InteropServices; using System.Web; using System.Web.Se
阿新
2018/04/12
9700
文本库 聊聊行首光标的行为
本文将告诉大家什么是 RealTimeStylus 以及如何从零开始不使用 WPF 框架提供的功能从 RealTimeStylus 获取到触摸信息
林德熙
2023/04/07
1.4K0
文本库 聊聊行首光标的行为
推荐阅读
相关推荐
VsxHowTo -- 把Windows Forms Designer作为自己的编辑器(2)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验