Hello
I'm currently working on a custom graphical editor under Visual Studio. This editor uses the component model of the Framework with its services by implementing a DesignSurface.
All works well except some data persistense scenari. My selected component has a property like this one :
Private _Children As New List(Of Child)<DesignerSerializationVisibility(DesignerSerializationVisibility.Content)> _ Public ReadOnly Property Children() As List(Of Child) Get Return _Children End Get End Property
When I edit this property inside the property window, I got the standard CollectionEditor run, and I can edit properly my collection. I valid the CollectionForm, and then, I don't see any undo inside VS, whereas all the other kind of properties works well (with SerializationVisibility set to Visible) for the basic undo process provided by the default UndoEngine.
I go deeper and discover that the EditValue method of the CollectionEditor class receive a provider parameter that can't reach the IComponentChangeService created by the DesignSurface. From there, I've found a way to acces this service by calling the GetService(GetType(IComponentChangeService)) from the Instance provided by the context parameter, but framework's editors don't intend to access this service this way...
As I think this is not like that it should work, I certainly have a main issue into my Package implementation. The problem is that the context and the provider parameters of the EditValue method are provided by the PropertyGrid in the property window of VS. So, it means that my package is unable to expose the DesignSurface services in order to make them reachable to this PropertyGrid, and it causes a persistense issue when I edit some properties (the Content ones I think).
I tried to read all possible documentation on how to manage services in a Package. I've even found a way to expose an IPropertyValueUIService at top level of the VS global service tree (Maybe not a good way, but an effective one), but I can't find a way to do that for services (especially IComponentChangeService) required by the PropertyGrid to make my component model work well...
All my experiments make me think that the PropertyGrid inside VS can only access the top level services (the promoted ones), but I guess I can't promote a service like an IComponentChangeService because it will bring a conflict if I open two or more instances of my custom editor inside VS.
So is there anyone who can tell me how the native Windows Form Editor can make its IComponentChangeService (among the other services provided by my DesignSurface) reachable by the VS PropertyGrid ?
My package declaration (I didn't paste all the attributes...) :
Public NotInheritable Class MyPackage Inherits Package #Region "Constructor" Public Sub New() ' The IPropertyValueUIService should be created in the global service provider to be accessed by the VS Property Window CType(Me, IServiceContainer).AddService(GetType(IPropertyValueUIService), AddressOf CreateService, True) End Sub #End Region #Region "Initialization" Private _Factory As MyEditorFactory Protected Overrides Sub Initialize() MyBase.Initialize() _Factory = New MyEditorFactory(Me) MyBase.RegisterEditorFactory(_Factory) End Sub #End Region #Region "Services" Private Function CreateService(ByVal Container As IServiceContainer, ByVal ServiceType As Type) As Object If GetType(IPropertyValueUIService).Equals(ServiceType) Then Return New MyPropertyValueUIService Return Nothing End Function #End Region #Region "Dispose" Protected Overrides Sub Dispose(ByVal disposing As Boolean) Try ' Remove the IPropertyValueUIService service CType(Me, IServiceContainer).RemoveService(GetType(IPropertyValueUIService), True) ' Dispose EditorFactory If Not _Factory Is Nothing Then _Factory.Dispose() Finally MyBase.Dispose(disposing) End Try End Sub #End Region End Class
My editor factory declaration :
Public NotInheritable Class MyEditorFactory Implements IVsEditorFactory Implements System.IServiceProvider Implements IDisposable #Region "Constructor" Public Sub New(ByVal Package As MyPackage) If Package Is Nothing Then Throw New ArgumentNullException("Package") _Package = Package End Sub #End Region #Region "Package" Private _Package As MyPackage #End Region #Region "IVsEditorFactory Members" Private _ServiceProvider As ServiceProvider Private _trs As ITypeResolutionService Private _tds As ITypeDiscoveryService Private Function SetSite(ByVal psp As Microsoft.VisualStudio.OLE.Interop.IServiceProvider) As Integer Implements IVsEditorFactory.SetSite _ServiceProvider = New ServiceProvider(psp) Return VSConstants.S_OK End Function Public Function GetService(ByVal serviceType As Type) As Object Implements System.IServiceProvider.GetService If GetType(ITypeResolutionService).Equals(serviceType) AndAlso Not _trs Is Nothing Then Return _trs If GetType(ITypeDiscoveryService).Equals(serviceType) AndAlso Not _tds Is Nothing Then Return _tds If _ServiceProvider Is Nothing Then Return Nothing Return _ServiceProvider.GetService(serviceType) End Function Private Function MapLogicalView(ByRef rguidLogicalView As Guid, <System.Runtime.InteropServices.Out()> ByRef pbstrPhysicalView As String) As Integer Implements IVsEditorFactory.MapLogicalView pbstrPhysicalView = Nothing If VSConstants.LOGVIEWID_Primary = rguidLogicalView OrElse VSConstants.LOGVIEWID_Designer = rguidLogicalView Then Return VSConstants.S_OK ElseIf VSConstants.LOGVIEWID_Code = rguidLogicalView Then pbstrPhysicalView = "Code" Return VSConstants.S_OK Else Return VSConstants.E_NOTIMPL End If End Function Private Function Close() As Integer Implements IVsEditorFactory.Close Return VSConstants.S_OK End Function Private Function CreateEditorInstance(ByVal grfCreateDoc As UInteger, ByVal pszMkDocument As String, ByVal pszPhysicalView As String, ByVal pvHier As IVsHierarchy, ByVal itemid As UInteger, ByVal punkDocDataExisting As System.IntPtr, <System.Runtime.InteropServices.Out()> ByRef ppunkDocView As System.IntPtr, <System.Runtime.InteropServices.Out()> ByRef ppunkDocData As System.IntPtr, <System.Runtime.InteropServices.Out()> ByRef pbstrEditorCaption As String, <System.Runtime.InteropServices.Out()> ByRef pguidCmdUI As Guid, <System.Runtime.InteropServices.Out()> ByRef pgrfCDW As Integer) As Integer Implements IVsEditorFactory.CreateEditorInstance ' Initialize out parameters ppunkDocView = IntPtr.Zero ppunkDocData = IntPtr.Zero pguidCmdUI = Constants.FactoryGuid pgrfCDW = 0 pbstrEditorCaption = Nothing ' Validate input parameters If (grfCreateDoc And (VSConstants.CEF_OPENFILE Or VSConstants.CEF_SILENT)) = 0 Then Return VSConstants.E_INVALIDARG Dim pi As ProjectItem = GetProjectItem(pvHier, itemid) If Not pi Is Nothing Then If pszPhysicalView = "Code" Then ... Else ' If the Designer DocData already exists, VS ask user to confirm for a Designer reloading If Not punkDocDataExisting = IntPtr.Zero Then Return VSConstants.VS_E_INCOMPATIBLEDOCDATA ' Update services Dim dts As DynamicTypeService = GetService(GetType(DynamicTypeService)) If Not dts Is Nothing Then ' Provide the right type resolution and discovery services _trs = dts.GetTypeResolutionService(pvHier) _tds = dts.GetTypeDiscoveryService(pvHier) End If ' Create the document (editor) Dim ds As New DesignSurface(Me) ds.BeginLoad(New MyDesignerLoader(pi)) Dim NewEditor As New MyEditorPane(ds) ppunkDocView = Marshal.GetIUnknownForObject(NewEditor) ppunkDocData = Marshal.GetIUnknownForObject(NewEditor) pbstrEditorCaption = "" End If End If Return VSConstants.S_OK End Function #End Region #Region "Dispose" Public Sub Dispose() Implements IDisposable.Dispose If Not _ServiceProvider Is Nothing Then _ServiceProvider.Dispose() End Sub #End Region End Class
Thanks for any help on that poorly documented part !