如何对用户控件中公开的属性使用内置编辑器 - Mask 属性 编辑器问题

How to use built-in editors for a exposed properties in User Controls - Mask Property Editor Issue

我认为我的愚蠢问题有一个简单的解决方案,但我今天无法解决它。

我有一个用户控件,它本身有一个 MaskedTextBox 控件。我也公开了它的一些属性供用户修改。

其中一个属性是 Mask 属性,我想展示它能够像在普通 MaskedTextBox 控件中一样使用预定义值启动编辑器。

所以我创建了一个 public 属性 InputMask 并设置了所有内容以便它可以工作但是在显示编辑器之后,我得到一个包含以下错误的错误对话框:

Object reference not set to an instance of an object

如果我不使用编辑器并复制掩码或通过代码设置它,则没有问题。

这是一个代码示例:

...
MaskedTextBox maskedtextbox;
myUserControl()
{
    ...
    maskedtextbox = new MaskedTextBox(){
        some stuff...
    };
}

[DefaultValue("")]
[Editor("System.Windows.Forms.Design.MaskPropertyEditor, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", typeof(UITypeEditor))]
[Localizable(true)]
[MergableProperty(false)]
[RefreshProperties(RefreshProperties.Repaint)]
public string InputMask
{
    get { return this.maskedtextbox.Mask; }
    set { this.maskedtextbox.Mask = value; }
}

一般情况下,注册UI类型编辑器即可,无需额外操作。但是在 MaskPropertyEditor 的情况下,当编辑 属性 时,编辑器期望 属性 属于 MaskedTextBox 并将 ITypeDescriptorContext.Instance 转换为 MaskedTextBox 并且由于我们的编辑 Mask 属性 属于我们的 UserControl 而不是屏蔽文本框,因此将抛出空引用异常。

要解决此问题,您需要创建自定义 UITypeEditor 并覆盖 EditValue 并编辑私有 MaskedTextBox 字段的 Mask 属性。为此,我们需要创建一个包含 MaskedTextBoxITypeDescriptorContext 实例,并将其传递给编辑器的 EditValue 方法。

这是实现。

用户控件

public partial class UserControl1 : UserControl
{
    MaskedTextBox maskedTextBox;
    public UserControl1()
    {
        InitializeComponent();
        maskedTextBox = new MaskedTextBox();
    }

    [Editor(typeof(MaskEditor), typeof(UITypeEditor))]
    public string Mask
    {
        get { return maskedTextBox.Mask; }
        set { maskedTextBox.Mask = value; }
    }
}

编辑器

public class MaskEditor : UITypeEditor
{
    public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
    {
        return UITypeEditorEditStyle.Modal;
    }
    public override object EditValue(ITypeDescriptorContext context, 
                                     IServiceProvider provider, object value)
    {
        var field = context.Instance.GetType().GetField("maskedTextBox",
                       System.Reflection.BindingFlags.NonPublic | 
                       System.Reflection.BindingFlags.Instance);
        var maskedTextBox = (MaskedTextBox)field.GetValue(context.Instance);
        var maskProperty = TypeDescriptor.GetProperties(maskedTextBox)["Mask"];
        var tdc = new TypeDescriptionContext(maskedTextBox, maskProperty);
        var editor = (UITypeEditor)maskProperty.GetEditor(typeof(UITypeEditor));
        return editor.EditValue(tdc, provider, value);
    }
}

ITypeDescriptionContext 实现

public class TypeDescriptionContext : ITypeDescriptorContext
{
    private Control editingObject;
    private PropertyDescriptor editingProperty;
    public TypeDescriptionContext(Control obj, PropertyDescriptor property)
    {
        editingObject = obj;
        editingProperty = property;
    }
    public IContainer Container
    {
        get { return editingObject.Container; }
    }
    public object Instance
    {
        get { return editingObject; }
    }
    public void OnComponentChanged()
    {
    }
    public bool OnComponentChanging()
    {
        return true;
    }
    public PropertyDescriptor PropertyDescriptor
    {
        get { return editingProperty; }
    }
    public object GetService(Type serviceType)
    {
        return editingObject.Site.GetService(serviceType);
    }
}

可能需要重新加载项目,然后 Visual Studio 才能识别新的 UITypeEditor