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

1

4

I think there is a simple solution for my stupid question but I just can't solve it today.

I have a User Control that has a MaskedTextBox Control in itself. I have also exposed a few of its properties for the user to modify.

One of these properties is Mask property which I want to expose with the ability of starting an editor with predefined values like in a normal MaskedTextBox control.

So I created a public property InputMask and set up everything so that it can work but after showing the editor, I get an error dialog which contains this error:

Object reference not set to an instance of an object

If i don't use editor and copy a mask or set it trough code works without problems.

Here is a code sample:

...
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; }
}
Cloaca answered 30/3, 2016 at 11:39 Comment(1)
It's not that simple in this case. In normal cases it's enough to register ui type editor and you don't need to do anything extra. But in MaskPropertyEditor case, when editing the property, the editor expect the the property belong to a MaskedTextBox. So you should exactly edit the Mask property of the MaskedTextBox .Galvan
G
3

In normal cases it's enough to register UI type editor and you don't need to do anything extra. But in MaskPropertyEditor case, when editing the property, the editor expect the the property belong to a MaskedTextBox and converts ITypeDescriptorContext.Instance to MaskedTextBox and since our editing Mask property belongs to our UserControl which is not a masked text box, a null reference exception will throw.

To solve the problem, you need to create a custom UITypeEditor and override EditValue and edit Mask property of the private MaskedTextBox field. To do so, we need to create an instance of ITypeDescriptorContext containing the MaskedTextBox and pass it to EditValue method of the editor.

Here is the implementations.

UserControl

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; }
    }
}

Editor

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 Implementation

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);
    }
}

The project may need to be reloaded before Visual Studio can recognize the new UITypeEditor

Galvan answered 30/3, 2016 at 16:31 Comment(1)
Thank you very much. It works great. I'am more of a Photoshop person (Designer over 10 year) than a C# developer but thanks to you helping me a couple of times I designed and implemented a nice UI for a windows forms application. Thank you.Cloaca

© 2022 - 2024 — McMap. All rights reserved.