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:
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. 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. 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. ActionLists
to return your custom action list. 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.