打开 OpenFileDialog 以更改 PropertyGrid 控件中 属性 的值?

Open a OpenFileDialog to change the value of a property in a PropertyGrid control?

在 Windows 表单中,我在 PropertyGid 控件中表示自定义 class,您可以在下图中注意到它具有各种字符串属性:

问题是我对当前更改字符串属性值的行为并不完全满意。对于那些需要文件或目录路径的属性,如 "Target" 或 "Working Directory" 属性,我想知道是否有可能实现一个 TypeConverter / Type Descriptor 来打开 OpenFileDialog 当单击 属性 网格中字段右侧的向下箭头时。也就是通过一个OpenFileDialog到select一个文件或文件夹,而不是直接在属性格子里写路径,但是还是让我想直接写路径的选项这样做。

也许 .NET Framework class 库已经提供了我请求的 TypeConverter / TypeDescriptor?。如果没有,这可能吗?以及如何开始这样做?

或者任何其他能够打开 OpenFileDialog 以更改 PropertyGrid 控件中特定 属性 的值的想法?

在我的应用程序中,我有一个采用图标文件路径的 属性,另一个可以采用文件或文件夹的 属性,以及另一个采用文件夹路径的 属性 .

所以,我不得不为这些属性中的每一个编写变体...

最简单的,如果你对FolderBrowserDialog的外观和限制感到满意,那就直接在EditorAttribute中指定System.Windows.Forms.Design.FolderNameEditor class class。否则,Ooki.Dialogs 是一个很好的 open-source 库,可以替代 modern-look 对话框。

第二个最简单的是选择图标文件路径的编辑器:

''' <summary>
''' Provides a user interface for selecting a icon file name.
''' </summary>
''' <seealso cref="FileNameEditor"/>
Friend Class IconFileNameEditor : Inherits FileNameEditor

#Region " Constructors "

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

#End Region

#Region " Private Methods "

    ''' <summary>
    ''' Initializes the open file dialog when it is created.
    ''' </summary>
    ''' <param name="ofd">
    ''' The <see cref="OpenFileDialog"/> to use to select a file name.
    ''' </param>
    Protected Overrides Sub InitializeDialog(ByVal dlg As OpenFileDialog)
        MyBase.InitializeDialog(dlg)

        With dlg
            .Multiselect = False
            .RestoreDirectory = True
            .DereferenceLinks = True
            .Filter = "Icon Files (*.ico;*.icl;*.exe;*.dll)|*.ico;*.icl;*.exe;*.dll|Icons|*.ico|Libraries|*.dll|Programs|*.exe"
            .FilterIndex = 1
            .SupportMultiDottedExtensions = True
        End With
    End Sub

#End Region

End Class

为了选择文件路径或文件夹路径,并搜索已经完成的事情,open-source 为了避免向我的项目添加外部依赖,我采用了自定义 FileFolderDialog class 在 this 文章中提供,我设法像这样编写编辑器:

''' <summary>
''' Provides a user interface for selecting a file or folder name.
''' </summary>
''' <seealso cref="UITypeEditor"/>
Public Class FileOrFolderNameEditor : Inherits UITypeEditor

#Region " Constructors "

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

#End Region

#Region " Public Methods"

    ''' <summary>
    ''' Gets the editor style used by the <see cref="UITypeEditor.EditValue(IServiceProvider, Object)"/> method.
    ''' </summary>
    ''' <param name="context">
    ''' An <see cref="ITypeDescriptorContext"/> that can be used to gain additional context information.
    ''' </param>
    ''' <returns>
    ''' A <see cref="UITypeEditorEditStyle"/> value that indicates the style of editor used 
    ''' by the <see cref="UITypeEditor.EditValue(IServiceProvider, Object)"/> method. 
    ''' <para></para>
    ''' If the <see cref="UITypeEditor"/> does not support this method, 
    ''' then <see cref="UITypeEditor.GetEditStyle"/> will return <see cref="UITypeEditorEditStyle.None"/>.
    ''' </returns>
    Public Overrides Function GetEditStyle(ByVal context As ITypeDescriptorContext) As UITypeEditorEditStyle
        Return UITypeEditorEditStyle.Modal
    End Function

    ''' <summary>
    ''' Edits the specified object's value using the editor style indicated by the <see cref="UITypeEditor.GetEditStyle"/> method.
    ''' </summary>
    ''' <param name="context">
    ''' An <see cref="ITypeDescriptorContext"/> that can be used to gain additional context information.
    ''' </param>
    ''' <param name="provider">
    ''' An <see cref="IServiceProvider"/> that this editor can use to obtain services.
    ''' </param>
    ''' <param name="value">
    ''' The object to edit.
    ''' </param>
    ''' <returns>
    ''' The new value of the object. 
    ''' <para></para>
    ''' If the value of the object has not changed, this should return the same object it was passed.
    ''' </returns>
    Public Overrides Function EditValue(ByVal context As ITypeDescriptorContext, ByVal provider As IServiceProvider, ByVal value As Object) As Object

        Using dlg As New OpenFileOrFolderDialog()
            If (dlg.ShowDialog = DialogResult.OK) Then
                Return dlg.SelectedPath
            End If
        End Using

        Return MyBase.EditValue(context, provider, value)

    End Function

#End Region

End Class

非常简单。

有内置的 FileNameEditor and FolderNameEditor UI 类型编辑器可以让你选择文件名和文件夹名,例如:

using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
using System.Windows.Forms.Design;
public class MyClass
{
    [Editor(typeof(FileNameEditor), typeof(UITypeEditor))]
    public string FilePath { get; set; }

    [Editor(typeof(FolderNameEditor), typeof(UITypeEditor))]
    public string FolderPath { get; set; }
}

如果您想自定义 FileNameEditor 以仅显示 txt 文件,您可以覆盖其 InitializeDialog 方法:

public class MyFileNameEditor : FileNameEditor
{
    protected override void InitializeDialog(OpenFileDialog openFileDialog)
    {
        base.InitializeDialog(openFileDialog);
        openFileDialog.Filter = "text files (*.txt)|*.txt";
    }
}