简体   繁体   中英

Display a MultilineStringEditor at design-time to edit the lines of an edit-control?

I'm following this C# article to learn how to create an ActionList and Action Items, however the article is only focused to action items of type DesignerActionPropertyItem ...

I would like to create an item of type DesignerActionMethodItem to call a method that must open a MultilineStringEditor to edit the text lines of the control, just the same action item that we can see in a default RichTextBox control:

在此输入图像描述

Someone could explain me how to do this in C# or VB.NET?.

I'm stuck on the values to pass to the UITypeEditor.EditValue() method, I think that is the method that invokes/displays the editor, but I'm not sure which value I must pass to the first parameter (it accepts an IServiceProvider or ITypeDescriptorContext ). I seen this related answer but I think there should exist a more direct/easier way than creating a class that implements IServiceProvider and ITypeDescriptorContext ... since I will to run a specific UITypeEditor ( MultilineStringEditor ).


This is what I got so far; when I click on the "Edit Text Lines..." action item nothing happens, any exception, just nothing of nothing; I'm not sure whether that is a good or bad signal, because if I try to pass other kind of values to the first parameter of UITypeEditor.EditValue() method then I got exceptions of invalid type casting when I click on my custom action item.

C# code version:

public class MyControlActionList : DesignerActionList {

    private DesignerActionUIService designerActionUISvc;

    public new MyControl Component {
        get { return (MyControl)base.Component; }
    }

    public MyControlActionList(MyControl component) : base(component) {
        // Cache a reference to DesignerActionUIService, so the DesigneractionList can be refreshed.
        this.designerActionUISvc = (DesignerActionUIService)GetService(typeof(DesignerActionUIService));
    }

    public override DesignerActionItemCollection GetSortedActionItems() {
        DesignerActionItemCollection items = new DesignerActionItemCollection();
        items.Add(new DesignerActionMethodItem(this, "EditTextLines", "Edit Text Lines...", "Behavior", "Opens the Lines collection editor", false));
        return items;
    }

    public void EditTextLines(){
        PropertyDescriptor pd = TypeDescriptor.GetProperties(this.Component)("Text");
        MultilineStringEditor editor = (MultilineStringEditor)pd.GetEditor(typeof(UITypeEditor));

        editor.EditValue((IServiceProvider)this.GetService(typeof(MultilineStringEditor)), this.Component.Text);

    }

}

VB.NET code version:

Public Class MyControlActionList : Inherits DesignerActionList

    Private designerActionUISvc As DesignerActionUIService

    Public Shadows ReadOnly Property Component As MyControl
        Get
            Return DirectCast(MyBase.Component, MyControl)
        End Get
    End Property

    Public Sub New(ByVal component As MyControl)
        MyBase.New(component)
        ' Cache a reference to DesignerActionUIService, so the DesigneractionList can be refreshed.
        Me.designerActionUISvc = DirectCast(GetService(GetType(DesignerActionUIService)), DesignerActionUIService)
    End Sub

    Public Overrides Function GetSortedActionItems() As DesignerActionItemCollection
        Dim items As New DesignerActionItemCollection()
        items.Add(New DesignerActionMethodItem(Me, "EditTextLines", "Edit Text Lines...", "Behavior", "Opens the Lines collection editor", False))
        Return items
    End Function

    Public Sub EditTextLines()

        Dim pd As PropertyDescriptor = TypeDescriptor.GetProperties(Me.Component)("Text")
        Dim editor As MultilineStringEditor = DirectCast(pd.GetEditor(GetType(UITypeEditor)), MultilineStringEditor)

        editor.EditValue(CType(Me.GetService(GetType(MultilineStringEditor)), IServiceProvider), Me.Component.Text)

    End Sub

End Class

The editor which you are looking for is StringArrayEditor which is used to edit string[] properties. It also is internal to System.Design .

To show StringArrayEditor at design-time using a designer verb you need to:

  1. Define a string[] Lines property in your control which gets or sets different lines of texts in Text property. In fact we will edit this property which cause the Text property getting edited.
  2. To be able to show the default editor for Lines property, create a class implementing ITypeDescriptorContext , IServiceProvider and IWindowsFormsEditorService interfaces. This way, after getting the editor of Lines property, you can call its EditValue method which shows the desired editor.
  3. Create a custom ActionList containing a method to show the editor. In this method, you should use the class which you created in previous step to show the editor for the property and after editing the property, set the edited value for property.
  4. Create a custom designer for your control and override its ActionLists to return your custom action list.
  5. Decorate the control with Designer attribute, registering your custom designer for the control.

Example

To make the example working, it's enough to copy and paste following code to a file in your Windows Forms application. Don't forget to add reference to System.Design assembly. After you build the project, you can drop an instance of MyControl on a form and using the smart tag window, you can click on Edit Text Lines... link to show StringArrayEditor dialog to edit the Line (and consequently Text ) property. Here is the code:

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing.Design;
using System.Linq;
using System.Windows.Forms;
using System.Windows.Forms.Design;

[Designer(typeof(MyControlDesigner))]
public class MyControl : Control
{
    public string[] Lines
    {
        get
        {
            return this.Text.Split(new string[] { Environment.NewLine },
                StringSplitOptions.None);
        }
        set
        {
            if (value != null)
                this.Text = string.Join(Environment.NewLine, value);
        }
    }
}
public class MyControlDesigner : ControlDesigner
{
    public override DesignerActionListCollection ActionLists
    {
        get
        {
            var list = new DesignerActionListCollection();
            list.Add(new MyControlActionList(this));
            return list;
        }
    }

}
public class MyControlActionList : DesignerActionList
{
    MyControlDesigner designer;
    MyControl Control { get { return (MyControl)designer.Control; } }
    public MyControlActionList(MyControlDesigner designer) : base(designer.Component)
    {
        this.designer = designer;
    }
    public override DesignerActionItemCollection GetSortedActionItems()
    {
        DesignerActionItemCollection items = new DesignerActionItemCollection();
        items.Add(new DesignerActionMethodItem(this, "EditTextLines",
           "Edit Text Lines...", "Behavior", "Opens the Lines collection editor", false));
        return items;
    }
    public void EditTextLines()
    {
        var linesPropertyDescriptor = TypeDescriptor.GetProperties(this.Control)["Lines"];
        var context = new TypeDescriptionContext(this.Control, linesPropertyDescriptor);
        var editor =(UITypeEditor)linesPropertyDescriptor.GetEditor(typeof(UITypeEditor));
        var lines = (this.Control).Lines;
        var result = (string[])editor.EditValue(context, context, lines);
        if (!result.SequenceEqual(lines))
            linesPropertyDescriptor.SetValue(this.Control, result);
    }
}
public class TypeDescriptionContext : ITypeDescriptorContext, IServiceProvider,
    IWindowsFormsEditorService
{
    private Control component;
    private PropertyDescriptor editingProperty;
    public TypeDescriptionContext(Control component, PropertyDescriptor property)
    {
        this.component = component;
        editingProperty = property;
    }
    public IContainer Container { get { return component.Container; } }
    public object Instance { get { return component; } }
    public void OnComponentChanged()
    {
        var svc = (IComponentChangeService)this.GetService(
            typeof(IComponentChangeService));
        svc.OnComponentChanged(component, editingProperty, null, null);
    }
    public bool OnComponentChanging() { return true; }
    public PropertyDescriptor PropertyDescriptor { get { return editingProperty; } }
    public object GetService(Type serviceType)
    {
        if ((serviceType == typeof(ITypeDescriptorContext)) ||
            (serviceType == typeof(IWindowsFormsEditorService)))
            return this;
        return component.Site.GetService(serviceType);
    }
    public void CloseDropDown() { }
    public void DropDownControl(Control control) { }
    DialogResult IWindowsFormsEditorService.ShowDialog(Form dialog)
    {
        IUIService service = (IUIService)(this.GetService(typeof(IUIService)));
        return service.ShowDialog(dialog);
    }
}

Note

You can find a more robust implementation of ITypeDescriptorContext , IServiceProvider and IWindowsFormsEditorService interfaces which I posed in response to this question:

Then in your custom ActionList class, you can show Lines property editor this way:

EditorServiceContext.EditValue(this.designer, base.Component, "Lines");

That's exactly the same way which is used in RichTextBoxDesigner .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM