【问题标题】:Display a MultilineStringEditor at design-time to edit the lines of an edit-control?在设计时显示 MultilineStringEditor 以编辑编辑控件的行?
【发布时间】:2017-04-13 18:37:38
【问题描述】:

我正在关注 this C# article 来学习如何创建 ActionList 和 Action Items,但是本文只关注 DesignerActionPropertyItem...

类型的 action items

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

谁能解释我如何在 C# 或 VB.NET 中做到这一点?

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


这是我目前得到的;当我单击“编辑文本行...”操作项时,没有任何反应,任何异常,什么都没有;我不确定这是一个好信号还是坏信号,因为如果我尝试将其他类型的值传递给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

【问题讨论】:

  • ElektroStudios,只是根据我自己在该领域的冒险经历提出的一些建议。您正在努力寻找好的示例,而这些示例在互联网上很难找到。Reference Source 上提供了有关 .Net 类的一些信息,但设计器代码大多不完整。然而,如果你给自己一个像ILSpy 这样的反编译器,你可以很容易地看到 MS 是如何做到的。您需要的东西在 System.Design.dll 中。给一个人一个汉堡,他就有饭吃;教他如何宰牛,他就会有很多肉。
  • Plutonix 我认为您混淆了编辑器类名称,或者我可能不太了解您,我为 RichTextbox 文本获得的默认编辑器是 MultilineStringEditor 类;然后回答您的问题:我正在尝试使用该编辑器,因为它是 RichTextBox 的默认编辑器。 StringCollectionEditor 类用于编辑字符串集合(例如ListBox.Items),而不是用于“原始”文本。 @TnTinMn 感谢您的建议!

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


【解决方案1】:

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

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

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

示例

要使示例正常运行,只需将以下代码复制并粘贴到 Windows 窗体应用程序中的文件中即可。不要忘记添加对System.Design 程序集的引用。构建项目后,您可以将MyControl 的实例拖放到表单上并使用智能标记窗口,您可以单击Edit Text Lines... 链接以显示StringArrayEditor 对话框以编辑Line(因此@987654344 @) 财产。代码如下:

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

注意

您可以找到我在回答这个问题时提出的ITypeDescriptorContextIServiceProviderIWindowsFormsEditorService 接口的更强大的实现:

然后在您自定义的ActionList 类中,您可以这样显示Lines 属性编辑器:

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

这与RichTextBoxDesigner中使用的方式完全相同。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2011-02-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-07
  • 2010-11-25
  • 2010-09-18
相关资源
最近更新 更多