在设计时显示MultilineStringEditor以编辑编辑控件的行?

时间:2017-04-13 18:37:38

标签: c# .net vb.net winforms windows-forms-designer

我在 this C# article 之后了解如何创建ActionList和Action Items,但本文仅关注DesignerActionPropertyItem类型的操作项。

我想创建一个DesignerActionMethodItem类型的项来调用一个方法,该方法必须打开MultilineStringEditor来编辑控件的文本行,就像我们在一个操作中看到的一样。默认 RichTextBox 控件:

enter image description here

有人可以解释一下如何在C#或VB.NET中执行此操作吗?

我坚持要传递给UITypeEditor.EditValue()方法的值,我认为这是调用/显示编辑器的方法,但我不确定我必须传递给哪个值第一个参数(它接受IServiceProviderITypeDescriptorContext)。我看到 this related answer ,但我认为应该存在比创建实现IServiceProviderITypeDescriptorContext的类更直接/更简单的方法...因为我会运行特定的UITypeEditorMultilineStringEditor)。

这是我到目前为止所得到的;当我点击"编辑文字行..."行动项没有任何反应,任何例外,什么都不是;我不确定这是好信号还是坏信号,因为如果我尝试将其他类型的值传递给UITypeEditor.EditValue()方法的第一个参数,那么当我点击我的时候,我得到了无效类型转换的例外情况自定义操作项。

C#代码版本:

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代码版本:

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

1 个答案:

答案 0 :(得分:1)

您要查找的编辑器是StringArrayEditor,用于编辑string[]属性。它也是System.Design的内部。

要使用设计师动词在设计时显示StringArrayEditor,您需要:

  1. 在控件中定义string[] Lines属性,用于获取或设置Text属性中不同的文本行。实际上,我们将编辑此属性,导致Text属性被编辑。
  2. 为了能够显示Lines属性的默认编辑器,请创建一个实现ITypeDescriptorContextIServiceProviderIWindowsFormsEditorService接口的类。这样,在获得Lines属性的编辑器后,您可以调用其显示所需编辑器的EditValue方法。
  3. 创建一个包含显示编辑器的方法的自定义ActionList。在此方法中,您应该使用在上一步中创建的类来显示属性的编辑器,并在编辑属性后,设置已编辑的属性值。
  4. 为您的控件创建自定义设计器并覆盖其ActionLists以返回自定义操作列表。
  5. 使用Designer属性装饰控件,为控件注册自定义设计器。
  6. 示例

    要使示例正常工作,只需将以下代码复制并粘贴到Windows窗体应用程序中的文件即可。别忘了添加对System.Design汇编的引用。构建项目后,您可以在表单上删除MyControl的实例并使用智能标记窗口,您可以单击Edit Text Lines...链接以显示StringArrayEditor对话框以编辑{{ 1}}(以及Line)属性。这是代码:

    Text
    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); } } ITypeDescriptorContextIServiceProvider接口的更强大的实现:

    然后在您的自定义IWindowsFormsEditorService课程中,您可以通过以下方式显示ActionList属性编辑器:

    Lines

    这与EditorServiceContext.EditValue(this.designer, base.Component, "Lines"); 中使用的方式完全相同。