Quantcast
Channel: Visual Studio Integrate forum
Viewing all articles
Browse latest Browse all 4410

Visual Studio custom editor package : offer an IComponentChangeService to the VS property window

$
0
0

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 !


Viewing all articles
Browse latest Browse all 4410


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>