winforms 获取C#或VB.NET中的IWindowsFormsEditorService的默认示例

mu0hgdu0  于 2022-11-16  发布在  C#
关注(0)|答案(1)|浏览(157)

在C#或VB.NET中,在Windows窗体的.NET Framework 4.x下,我希望编写一个通用函数,以便在运行时为指定的控件属性类型调用默认的UI编辑器。
示例(不完整代码):

public T EditValue<T>(Component component, string propertyName, T value) {

    PropertyDescriptor propDescriptor = 
        TypeDescriptor.GetProperties(component)[propertyName];

    UITypeEditor editor = 
        (UITypeEditor)propDescriptor.GetEditor(typeof(UITypeEditor));

    IWindowsFormsEditorService serviceProvider = ??????;

    object result = editor.EditValue(serviceProvider, serviceProvider, value);
    return (T)result;
}

(当然,函数定义也可以指定ExtensionAttribute以简化函数调用。)
示例用法如下:编辑Control.Font属性:

TextBox ctrl = this.TextBox1;
Font value = EditValue(ctrl, nameof(ctrl.Font), ctrl.Font);

ctrl.Font = value;

或者编辑ListBox中的项目:

ListBox ctrl = this.ListBox1;
ListBox.ObjectCollection value = EditValue(ctrl, nameof(ctrl.Items), ctrl.Items);

我需要帮助来确定这是否是实现这种功能的正确方法(也许所有这些都可以通过反射或其他方式更容易地完成?),还需要帮助来获得默认的IServiceProvider/IWindowsFormsEditorService示例,以便能够编辑我传递给上述代码的函数的控件。
我研究并发现了这个答案,它演示了如何定义一个实现IServiceProvider/IWindowsFormsEditorService接口的新类:
https://stackoverflow.com/a/3816585/1248295
因此,在上面的代码中,我可以替换以下代码行:

IWindowsFormsEditorService serviceProvider = ??????;

为此:

RuntimeServiceProvider serviceProvider = new RuntimeServiceProvider();

它的工作原理是:

但我想问的是,窗体、组件或控件类型中是否已经定义了一个类,它已经实现了IServiceProvider/IWindowsFormsEditorService用于此目的,这样我就可以示例化它或通过Reflection检索它来示例化它,从而简化我的代码,以避免仅为定义RuntimeServiceProvider类而编写更多代码。
我想知道,是否可以在不需要定义RuntimeServiceProvider之类的自定义类来实现IWindowsFormsEditorService的情况下简化操作。

tzxcd3kk

tzxcd3kk1#

由于无法找到其他更简单的方法,我不得不定义一个类来实现ITypeDescriptorContextIServiceProviderIWindowsFormsEditorService。在本例中,我定义了三个类,只是因为我更喜欢单独的实现,这些实现在将来单独使用时可能更有用,但所有这些实现都可以在一个类中定义,如@Reza的C#示例。
这不是一个可以接受的答案,但它是一个有效的解决方案,因为缺少更简单的东西。
我们开始吧:

服务提供程序.vb

Imports System.Windows.Forms.Design

#Region " ServiceProvider "

Namespace DevCase.Core.Design.Services

    ''' <summary>Provides a simple implementation of <see cref="IServiceProvider"/> 
    ''' that provides a mechanism for retrieving a service object;
    ''' that is, an object that provides custom support to other objects.</summary>
    ''' <seealso cref="IServiceProvider"/>
    Public Class ServiceProvider : Implements IServiceProvider

#Region " Constructors "

        ''' <summary>Initializes a new instance of the <see cref="ServiceProvider"/> class.</summary>
        Public Sub New()
            MyBase.New()
        End Sub

#End Region

#Region " IServiceProvider Implementation "

        ''' <summary>Gets the service object of the specified type.</summary>
        ''' <param name="serviceType">An object that specifies the type of service object to get.</param>
        ''' <returns>A service object of type <paramref name="serviceType"/>. 
        ''' -or- null if there is no service object of type <paramref name="serviceType"/>.</returns>
        Public Overridable Function GetService(serviceType As Type) As Object Implements IServiceProvider.GetService
            If serviceType Is GetType(IWindowsFormsEditorService) Then
                Return New DevCase.Core.Design.Services.WindowsFormsEditorService()
            End If

            Return Nothing
        End Function

#End Region

    End Class

End Namespace

#End Region

Windows窗体编辑器服务.vb

Imports System.Drawing.Design
Imports System.Windows.Forms
Imports System.Windows.Forms.Design

#Region " WindowsFormsEditorService "

Namespace DevCase.Core.Design.Services

    ''' <summary> Provides a simple implementation of <see cref="IWindowsFormsEditorService"/> interface 
    ''' for a <see cref="UITypeEditor"/> to display Windows Forms or to display a control 
    ''' in a drop-down area from a property grid control in design mode or runtime mode.</summary>
    ''' <seealso cref="IWindowsFormsEditorService"/>
    Public Class WindowsFormsEditorService : Implements IWindowsFormsEditorService

#Region " Constructors "

        ''' <summary>Initializes a new instance of the <see cref="WindowsFormsEditorService"/> class.</summary>
        Public Sub New()
            MyBase.New()
        End Sub

#End Region

#Region " IWindowsFormsEditorService Implementation "

        ''' <summary>Closes any previously opened drop down control area.</summary>
        Protected Overridable Sub CloseDropDown() Implements IWindowsFormsEditorService.CloseDropDown
        End Sub

        ''' <summary>Displays the specified control in a drop down area 
        ''' below a value field of the property grid that provides this service.</summary>
        ''' <param name="control">The drop down list  <see cref="System.Windows.Forms.Control"/> to open.</param>
        Protected Overridable Sub DropDownControl(control As Control) Implements IWindowsFormsEditorService.DropDownControl
        End Sub

        ''' <summary>Shows the specified <see cref="Form"/>.</summary>
        ''' <param name="dialog">The <see cref="Form"/> to display.</param>
        ''' <returns>A <see cref="DialogResult"/> indicating the result code returned by the <see cref="Form"/>.</returns>
        Public Overridable Function ShowDialog(dialog As Form) As DialogResult Implements IWindowsFormsEditorService.ShowDialog
            Dim result As DialogResult = dialog.ShowDialog()
            Return result
        End Function

#End Region

    End Class

End Namespace

#End Region

类型描述符上下文.vb

Imports System.ComponentModel
Imports System.ComponentModel.Design

#Region " TypeDescriptorContext "

Namespace DevCase.Core.Design.Services

    ''' <summary>Provides a simple implementation of <see cref="ITypeDescriptorContext"/> interface
    ''' that defines contextual information about a <see cref="Component"/>, 
    ''' such as its <see cref="IContainer"/> and <see cref="ComponentModel.PropertyDescriptor"/>.</summary>
    ''' <seealso cref="ITypeDescriptorContext"/>
    Public Class TypeDescriptorContext : Implements ITypeDescriptorContext

#Region " Constructors "

        ''' <summary>Initializes a new instance of the <see cref="TypeDescriptorContext"/> class.</summary>
        ''' <param name="component">The <see cref="Component"/> that will be associated with this <see cref="TypeDescriptor"/></param>
        ''' <param name="[property]">The <see cref="ComponentModel.PropertyDescriptor"/> that will be associated with this <see cref="TypeDescriptorContext"/>.</param>
        Public Sub New(component As Component, [property] As PropertyDescriptor)
            MyBase.New()
            Me.Instance = component
            Me.PropertyDescriptor = [property]
        End Sub

        ''' <summary>Initializes a new instance of the <see cref="TypeDescriptorContext"/> class.</summary>
        ''' <param name="component">The <see cref="Component"/> that will be associated with this <see cref="TypeDescriptor"/></param>
        Public Sub New(component As Component)
            Me.New(component, [property]:=Nothing)
        End Sub

        ''' <summary>Initializes a new instance of the <see cref="TypeDescriptorContext"/> class.</summary>
        Public Sub New()
            Me.New(component:=Nothing, [property]:=Nothing)
        End Sub

#End Region

#Region " ITypeDescriptorContext Implementation "

        ''' <summary>Gets the <see cref="Component"/> that is associated with this <see cref="TypeDescriptor"/>.</summary>
        Public ReadOnly Property Instance As Object Implements ITypeDescriptorContext.Instance

        ''' <summary>Gets the container that is associated with this <see cref="TypeDescriptor"/>.</summary>
        Public ReadOnly Property Container As IContainer Implements ITypeDescriptorContext.Container
            Get
                Return DirectCast(Me.Instance, Component).Container
            End Get
        End Property

        ''' <summary>Gets the <see cref="ComponentModel.PropertyDescriptor"/> that is associated with this <see cref="TypeDescriptor"/>.</summary>
        Public ReadOnly Property PropertyDescriptor As PropertyDescriptor Implements ITypeDescriptorContext.PropertyDescriptor

        ''' <summary>Raises the<see cref="IComponentChangeService.ComponentChanged"/> event.</summary>
        Public Overridable Sub OnComponentChanged() Implements ITypeDescriptorContext.OnComponentChanged
        End Sub

        ''' <summary>Raises the<see cref="IComponentChangeService.ComponentChanging"/> event. </summary>
        Public Overridable Function OnComponentChanging() As Boolean Implements ITypeDescriptorContext.OnComponentChanging
            Return True ' True to keep changes; otherwise, False.
        End Function

#End Region

#Region " IServiceProvider Implementation "

        ''' <summary>Gets the service object of the specified type.</summary>
        ''' <param name="serviceType">An object that specifies the type of service object to get. </param>
        ''' <returns> A service object of type <paramref name="serviceType"/>. 
        ''' -or- null if there is no service object of type <paramref name="serviceType"/>.</returns>
        Public Overridable Function GetService(serviceType As Type) As Object Implements IServiceProvider.GetService
            Dim serviceProvider As New DevCase.Core.Design.Services.ServiceProvider
            Return serviceProvider.GetService(serviceType)
        End Function

#End Region

    End Class

End Namespace

#End Region

最后是Component类和派生类(如Control、UserControl或Form)的方法扩展模块:

组件扩展.vb

Imports System.ComponentModel.Design
Imports System.Drawing.Design
Imports System.Reflection
Imports System.Windows.Forms.Design

Imports DevCase.Core.Design.Services

#Region " Component Extensions "

Namespace DevCase.Extensions.ComponentExtensions

    ''' <summary>Contains custom extension methods to use with <see cref="System.ComponentModel.Component"/>.</summary>
    <HideModuleName>
    Public Module ComponentExtensions

#Region " Public Methods "

        ''' <summary>Invokes the default <see cref="UITypeEditor"/> to edit the specified property.</summary>
        ''' <param name="component">The source <see cref="System.ComponentModel.Component"/>.</param>
        ''' <param name="[property]">The <see cref="PropertyDescriptor"/> to edit.</param>
        ''' <returns>The resulting value returned by the invoked <see cref="UITypeEditor"/>.</returns>
        <DebuggerStepThrough>
        <Extension>
        <EditorBrowsable(EditorBrowsableState.Always)>
        Public Function InvokeUITypeEditor(Of T)(component As Component, [property] As PropertyDescriptor) As T

            If [property] Is Nothing Then
                Throw New NullReferenceException(NameOf([property]))
            End If

            If [property].PropertyType IsNot GetType(T) Then
                Throw New ArgumentException("Type missmatch.", NameOf(T))
            End If

            Dim result As T
            Dim site As ISite = component.Site

            If site?.DesignMode Then
                Dim designerHost As IDesignerHost = DirectCast(site.GetService(GetType(IDesignerHost)), IDesignerHost)
                Dim designer As IDesigner = designerHost.GetDesigner(component)
                Dim editorServiceContext As Type = (From [type] As Type In GetType(ControlDesigner).Assembly.DefinedTypes Where [type].Name = "EditorServiceContext").Single()
                Dim editValue As MethodInfo = editorServiceContext.GetMethod("EditValue", BindingFlags.Static Or BindingFlags.Public)
                result = DirectCast(editValue.Invoke(Nothing, {designer, component, [property].Name}), T)

            Else
                Dim serviceProvider As IServiceProvider = New ServiceProvider()
                Dim typeDescriptorContext As ITypeDescriptorContext = New TypeDescriptorContext()
                Dim editor As UITypeEditor = DirectCast([property].GetEditor(GetType(UITypeEditor)), UITypeEditor)
                result = DirectCast(editor.EditValue(typeDescriptorContext, serviceProvider, [property].GetValue(component)), T)

            End If

            Return result

        End Function

        ''' <summary>Invokes the default <see cref="UITypeEditor"/> to edit the specified property.</summary>
        ''' <param name="component">The source <see cref="System.ComponentModel.Component"/>.</param>
        ''' <param name="propertyName">The name of the property to edit.</param>
        ''' <returns>The resulting value returned by the invoked <see cref="UITypeEditor"/>. </returns>
        <DebuggerStepThrough>
        <Extension>
        <EditorBrowsable(EditorBrowsableState.Always)>
        Public Function InvokeUITypeEditor(Of T)(component As Component, propertyName As String) As T

            Dim [property] As PropertyDescriptor = TypeDescriptor.GetProperties(component)(propertyName)
            Return InvokeUITypeEditor(Of T)(component, [property])

        End Function

#End Region

    End Module

End Namespace

#End Region

导入此名称空间后:

Imports DevCase.Extensions.ComponentExtensions

它的使用非常简单:

Dim lb As ListBox = Me.ListBox1
Dim propName As String = NameOf(lb.Items)

Dim value As ListBox.ObjectCollection = 
    lb.InvokeUITypeEditor(Of ListBox.ObjectCollection)(propName)

或者这样:

Dim lb As ListBox = Me.ListBox1

Dim propDescriptor As PropertyDescriptor = 
    TypeDescriptor.GetProperties(lb)(NameOf(lb.Items)) 

Dim value As ListBox.ObjectCollection = 
    lb.InvokeUITypeEditor(Of ListBox.ObjectCollection)(propDescriptor)

相关问题