您的位置:首页 > 其它

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

2014-01-14 10:22 411 查看

VsxHowTo-把WindowsFormsDesigner作为自己的编辑器(1)

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

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





从上图可以看出,要实现一个编辑器,实现需要EditorFactory、DocumentData和DocumentView。其中,我们不需要再实现DocumentView了,因为我们要重用VS的WinformDesigner,它就是DocumentView,我们的目的就是把它调用出来。

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

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

实现DocumentData

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

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSystem.Text;

usingMicrosoft.VisualStudio.Shell.Interop;

usingMicrosoft.VisualStudio;


namespaceCompany.WinFormDesigner

{

classDocumentData:IVsPersistDocData

{

privateGuid_factoryGuid=typeof(DocumentEditorFactory).GUID;


#regionIVsPersistDocData成员


intIVsPersistDocData.Close()

{

returnVSConstants.S_OK;

}


intIVsPersistDocData.GetGuidEditorType(outGuidpClassID)

{

pClassID=_factoryGuid;

returnVSConstants.S_OK;

}


intIVsPersistDocData.IsDocDataDirty(outintpfDirty)

{

pfDirty=0;

returnVSConstants.S_OK;

}


intIVsPersistDocData.IsDocDataReloadable(outintpfReloadable)

{

pfReloadable=1;

returnVSConstants.S_OK;

}


intIVsPersistDocData.LoadDocData(stringpszMkDocument)

{

returnVSConstants.S_OK;

}


intIVsPersistDocData.OnRegisterDocData(uintdocCookie,IVsHierarchypHierNew,uintitemidNew)

{

returnVSConstants.S_OK;

}


intIVsPersistDocData.ReloadDocData(uintgrfFlags)

{

returnVSConstants.S_OK;

}


intIVsPersistDocData.RenameDocData(uintgrfAttribs,IVsHierarchypHierNew,uintitemidNew,stringpszMkDocumentNew)

{

returnVSConstants.S_OK;

}


intIVsPersistDocData.SaveDocData(VSSAVEFLAGSdwSave,outstringpbstrMkDocumentNew,outintpfSaveCanceled)

{

pbstrMkDocumentNew=null;

pfSaveCanceled=0;

returnVSConstants.S_OK;

}


intIVsPersistDocData.SetUntitledDocPath(stringpszDocDataPath)

{

returnVSConstants.S_OK;

}


#endregion

}

}


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

实现EditorFactory

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

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

usingSystem;

usingSystem.Collections.Generic;

usingSystem.Linq;

usingSystem.Text;

usingSystem.ComponentModel.Design.Serialization;

usingSystem.Windows.Forms;


namespaceCompany.WinFormDesigner

{

classDesignerLoader:BasicDesignerLoader

{

protectedoverridevoidPerformFlush(IDesignerSerializationManagerserializationManager)

{

}


protectedoverridevoidPerformLoad(IDesignerSerializationManagerserializationManager)

{

LoaderHost.Container.Add(newUserControl());

}

}

}


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

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

usingSystem;

usingSystem.IO;

usingSystem.Runtime.InteropServices;

usingSystem.Windows.Forms;

usingMicrosoft.VisualStudio;

usingMicrosoft.VisualStudio.Designer.Interfaces;

usingMicrosoft.VisualStudio.OLE.Interop;

usingMicrosoft.VisualStudio.Shell;

usingMicrosoft.VisualStudio.Shell.Interop;

usingMicrosoft.VisualStudio.TextManager.Interop;

usingIOleServiceProvider=Microsoft.VisualStudio.OLE.Interop.IServiceProvider;

usingIServiceProvider=System.IServiceProvider;

usingSystem.ComponentModel.Design;


namespaceCompany.WinFormDesigner

{

classDocumentEditorFactory:IVsEditorFactory

{

privateIServiceProviderserviceProvider;


#regionIVsEditorFactory成员


publicintClose()

{

returnVSConstants.S_OK;

}


publicintCreateEditorInstance(

uintgrfCreateDoc,

stringpszMkDocument,

stringpszPhysicalView,

IVsHierarchypvHier,

uintitemid,

IntPtrpunkDocDataExisting,

outIntPtrppunkDocView,

outIntPtrppunkDocData,

outstringpbstrEditorCaption,

outGuidpguidCmdUI,

outintpgrfCDW)

{

//Initializeoutparameters

ppunkDocView=IntPtr.Zero;

ppunkDocData=IntPtr.Zero;

pguidCmdUI=Guid.Empty;

pgrfCDW=0;

pbstrEditorCaption=string.Empty;


//Validateinputs

if((grfCreateDoc&(VSConstants.CEF_OPENFILE|VSConstants.CEF_SILENT))==0)

returnVSConstants.E_INVALIDARG;



try

{

vardesignerService=serviceProvider.GetService(typeof(IVSMDDesignerService))asIVSMDDesignerService;

varoleServiceProvider=serviceProvider.GetService(typeof(IOleServiceProvider))asIOleServiceProvider;

vardesignerLoader=newDesignerLoader();


IVSMDDesignerdesigner=designerService.CreateDesigner(oleServiceProvider,designerLoader);


objectdesignerView=designer.View;

pguidCmdUI=designer.CommandGuid;

ppunkDocView=Marshal.GetIUnknownForObject(designerView);


vardata=newDocumentData();

ppunkDocData=Marshal.GetIUnknownForObject(data);

}

finally

{

if(ppunkDocView==IntPtr.Zero)

{

if(punkDocDataExisting!=ppunkDocData&&ppunkDocData!=IntPtr.Zero)

{

Marshal.Release(ppunkDocData);

ppunkDocData=IntPtr.Zero;

}

}

}


returnVSConstants.S_OK;

}



publicintMapLogicalView(refGuidrguidLogicalView,outstringpbstrPhysicalView)

{

pbstrPhysicalView=null;//---Initializeoutparameter


//---Wesupportonlyasinglephysicalview

if(VSConstants.LOGVIEWID_Primary==rguidLogicalView)

{

//---PrimaryviewusesNULLasphysicalView

returnVSConstants.S_OK;

}

else

{

//---YoumustreturnE_NOTIMPLforanyunrecognizedlogicalViewvalues

returnVSConstants.E_NOTIMPL;

}

}


publicintSetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProviderpsp)

{

serviceProvider=newMicrosoft.VisualStudio.Shell.ServiceProvider(psp);

returnVSConstants.S_OK;

}


#endregion

}

}


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

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

注册EditorFactory

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

usingSystem;

usingSystem.Diagnostics;

usingSystem.Globalization;

usingSystem.Runtime.InteropServices;

usingMicrosoft.VisualStudio.Shell;


namespaceCompany.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)]

publicsealedclassWinFormDesignerPackage:Package

{

publicWinFormDesignerPackage()

{

Trace.WriteLine(string.Format(CultureInfo.CurrentCulture,"Enteringconstructorfor:{0}",this.ToString()));

}


#regionPackageMembers


protectedoverridevoidInitialize()

{

Trace.WriteLine(string.Format(CultureInfo.CurrentCulture,"EnteringInitialize()of:{0}",this.ToString()));

base.Initialize();


//注册EditorFactory

RegisterEditorFactory(newDocumentEditorFactory());


}


#endregion

}

}


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

测试我们的设计器

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





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

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

代码下载:WinFormDesigner.rar

作者:明年我18

出处:http://www.cnblogs.com/default

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接,否则保留追究法律责任的权利。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: