【发布时间】:2021-04-13 09:07:15
【问题描述】:
在过去的几周里,我一直在学习 PropertyGrid。我需要显示各种类的一系列对象的属性,但它们都是从类Ctrl派生的。比如有:
- Ctrl_BUTTON
- Ctrl_SQLLISTVIEW
- Ctrl_TEXTBOX
- 一共九类
派生类包含基类中没有的附加属性,并且仅适用于某些派生类。每个属性都可以是特定类型的表达式(允许用户输入稍后将评估为规范值的字符串的类)。在 PropertyGrid 中,我通过编写继承 UITypeEditor 的类 ExpressionPropertyEditor 实现了一个下拉菜单。我目前只实现了可以是布尔值或表达式的 Ctrl_TEXTBOX.ReadOnly 属性,它在用户输入表达式之前提供了一个如下所示的下拉列表:
或类似的东西,如果他们有:
当用户单击表达式条目时,将打开一个表达式编辑器。目前我的 Ctrl_TEXTBOX 类的相关部分如下所示:
public class Ctrl_TEXTBOX : Ctrl
{
private object _readOnly = false;
[DescriptionAttribute("Whether the user can change the contents.")]
[Editor(typeof(ExpressionPropertyEditor), typeof(UITypeEditor))]
public object ReadOnly
{
get
{
return _readOnly;
}
set
{
try
{
_readOnly = ExpressionHelper.BoolExp2Object(value);
}
catch (Exception ex)
{
base.Globals.Errs.Raise(ex);
throw ex;
}
}
}
}
供参考 ExpressionHelper 包含这个,简单地说:
public static class ExpressionHelper
{
public static object BoolExp2Object(object oValue)
{
try
{
switch (Helper.GetClassNameFromObject(oValue).ToLower())
{
case "expression": return oValue;
case "string":
switch (((string)oValue).ToLower())
{
case "true": return true;
case "false": return false;
default: throw new NotImplementedException();
}
case "boolean":
case "bool": return oValue;
default: throw new NotImplementedException();
}
}
catch (Exception ex)
{
throw ex;
}
}
}
我的 ExpressionPropertyEditor 实现如下所示:
public class ExpressionPropertyEditor : UITypeEditor
{
private IWindowsFormsEditorService _editorService;
private ListBox _listBox;
private Ctrl _ctrl;
public ExpressionPropertyEditor()
{
}
// Displays the UI for value selection.
public override object EditValue(ITypeDescriptorContext context, System.IServiceProvider provider, object value)
{
// coded with help from: https://stackoverflow.com/questions/5171037/display-list-of-custom-objects-as-a-drop-down-in-the-propertiesgrid
Ctrl oCtrl;
Ctrl_TEXTBOX oTextBox;
Expression oExp = null;
frmExpressionEditor2 frm;
bool bOk = false;
_editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
_listBox = new ListBox();
_listBox.SelectionMode = SelectionMode.One;
_listBox.SelectedValueChanged += EventHandler_ListBox_SelectedValueChanged;
_listBox.Items.Add(true);
_listBox.Items.Add(false);
switch (Helper.GetClassNameFromObject(context.Instance))
{
case "Ctrl_TEXTBOX":
oTextBox = (Ctrl_TEXTBOX)context.Instance;
switch (Helper.GetClassNameFromObject(oTextBox.ReadOnly))
{
case "Boolean":
case "bool":
// cos we need a way to make an expression
oExp = new Expression(oTextBox.Globals, "BOOLEAN", enumExpressionSource.Expression, Consts.EXPRESSION_PROPERTY_NAME);
_listBox.Items.Add(oExp);
break;
case "Expression":
oExp = (Expression)oTextBox.ReadOnly;
_listBox.Items.Add(oExp);
break;
default:
// this shouldn't happen really; just wrap as an expression
oExp = new Expression(oTextBox.Globals, "BOOLEAN", enumExpressionSource.Expression, Consts.EXPRESSION_PROPERTY_NAME);
_listBox.Items.Add(oExp);
break;
}
break;
}
_ctrl = (Ctrl)context.Instance;
_editorService.DropDownControl(_listBox); // this will return when EventHandler_ListBox_SelectedValueChanged calls editorService.CloseDropDown, like a modal dialog.
if (_listBox.SelectedItem == null) // no selection, return the passed-in value as is
return value;
if (Helper.GetClassNameFromObject(_listBox.SelectedItem) == "Expression")
{
frm = new frmExpressionEditor2();
if (!frm.EditExpression(_ctrl.Globals, _ctrl.Server, _ctrl.App, _ctrl.Frm, ref oExp, ref bOk)) throw new Exception("Could not open expression editor.");
}
return _listBox.SelectedItem;
}
private void EventHandler_ListBox_SelectedValueChanged(object sender, EventArgs e)
{
_editorService.CloseDropDown();
}
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.DropDown;
}
public override bool IsDropDownResizable
{
get { return true; }
}
}
我的问题是:我如何概括 ExpressionPropertyEditor 以便我可以在 Ctrl 的任何派生类上使用它,用于我想要包含 Expressions 的任何布尔属性?目前它被锁定为 Ctrl_TEXTBOX.ReadOnly。如果无法做到这一点,我必须创建几十个包含相同逻辑的类,只更改名称 - 不利于代码重用。
【问题讨论】:
-
context.Instance 和 context.PropertyDescriptor 的组合应该足以知道正在编辑什么类中的什么属性。接下来,由派生类覆盖的 Ctrl 上的虚拟方法可以帮助执行特定于类+属性的任务。此外,您可以将编辑器声明为 Ctrl 的嵌套类,以访问私有成员和受保护成员。
-
如果你想让他们继续回答,你应该回答他们。
-
很抱歉西蒙,我会试着跳到你的时间表。我目前正在考虑做出 Rubidium 友好建议的一些更改,并且会在我有,嗯,对反馈有用的东西时进行反馈。
-
@Rubidium 37,非常感谢。我现在有一个通用方法,它适用于 Ctrl 子类上的任何布尔值。花了我一段时间,因为我不太熟悉反射方法,我必须使用这些方法来获取值,而不必将 context.Instance 紧密绑定到特定类型。我还没有将它嵌套在 Ctrl 中(可能并不真的需要)。我将在下面发布修改后的代码。再次感谢。
标签: c# generics propertygrid