C#PropertyGrid UITypeEditor与对象和属性无关
在过去的几周里,我一直在学习PropertyGrid。我需要显示不同类的一系列对象的属性,但它们都是从类Ctrl派生的。例如,有:C#PropertyGrid UITypeEditor与对象和属性无关,c#,generics,propertygrid,C#,Generics,Propertygrid,在过去的几周里,我一直在学习PropertyGrid。我需要显示不同类的一系列对象的属性,但它们都是从类Ctrl派生的。例如,有: Ctrl_按钮 Ctrl\u SQLLISTVIEW Ctrl\u文本框 总共九节课 派生类包含不在基类中且仅适用于某些派生类的附加属性。每个属性都可以是特定类型的表达式(一个允许用户输入字符串的类,该字符串稍后将计算为规范值)。在PropertyGrid中,我通过编写继承UITypeEditor的类ExpressionPropertyEdit实现了一个下拉列表
- Ctrl_按钮
- Ctrl\u SQLLISTVIEW
- Ctrl\u文本框
- 总共九节课
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;
}
}
}
}
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; }
}
}
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 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;
}
}
}
}
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; }
}
}
我的问题是:如何对ExpressionPropertyEdit进行泛化,以便在Ctrl的任何派生类上使用它,以获得我想要包含表达式的任何布尔属性?当前它被锁定为Ctrl\u TEXTBOX.ReadOnly。如果这是不可能的,我必须创建几十个包含相同逻辑的类,只更改名称-这不利于代码重用。多亏了@Rubidium 37上面的注释,我修改了EditValue方法,这样它就不需要紧密绑定到Ctrl的子类,并且可以使用任何属性名称。然后,我在System.Reflection中使用PropertyInfo类来获取属性的当前值,而不必硬编码属性名称 只发布更新的EditValue方法
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 = null;
Expression oExp = null;
frmExpressionEditor2 frm = null;
bool bOk = false;
System.Reflection.PropertyInfo oPropInfo = null;
string cPropertyName = "";
object oCurrentValue = null;
try
{
oCtrl = (Ctrl)context.Instance;
_editorService = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
// setup a listbox with the possible values the boolean can take, true, false, and an expression
_listBox = new ListBox();
_listBox.SelectionMode = SelectionMode.One;
_listBox.SelectedValueChanged += EventHandler_ListBox_SelectedValueChanged;
//_listBox.DrawItem += EventHandler_ListBox_DrawItem; // do this if you have loads of time and want a pretty fx next to the Expression
_listBox.Items.Add(true);
_listBox.Items.Add(false);
// we either want to show Consts.EXPRESSION_PROPERTY_NAME, or if the value of the property is currently an expression, show that instead
cPropertyName = context.PropertyDescriptor.Name;
oPropInfo = context.Instance.GetType().GetProperty(cPropertyName);
oCurrentValue = oPropInfo.GetValue(context.Instance, null); // this returns the current value of the property
switch (Helper.GetClassNameFromObject(oCurrentValue))
{
case "Boolean":
case "bool": // just show the default expression string
oExp = new Expression(oCtrl.Globals, "BOOLEAN", enumExpressionSource.Expression, Consts.EXPRESSION_PROPERTY_NAME);
_listBox.Items.Add(oExp);
break;
case "Expression": // show the current value of the boolean expression
oExp = (Expression)oCurrentValue;
_listBox.Items.Add(oExp);
break;
default: // this shouldn't happen, so reset to default expression string
oExp = new Expression(oCtrl.Globals, "BOOLEAN", enumExpressionSource.Expression, Consts.EXPRESSION_PROPERTY_NAME);
_listBox.Items.Add(oExp);
break;
}
// show the list
_editorService.DropDownControl(_listBox); // this will return when EventHandler_ListBox_SelectedValueChanged calls editorService.CloseDropDown, like a modal dialog.
// no selection, return the passed-in value as is
if (_listBox.SelectedItem == null) return value;
// and if necessary, allow the user to edit the expression
if (Helper.GetClassNameFromObject(_listBox.SelectedItem) == "Expression")
{
frm = new frmExpressionEditor2();
if (!frm.EditExpression(oCtrl.Globals, oCtrl.Server, oCtrl.App, oCtrl.Frm, ref oExp, ref bOk)) throw new Exception("Could not open expression editor.");
return oExp;
}
return _listBox.SelectedItem;
}
catch (Exception ex)
{
oCtrl.Globals.Errs.Raise(ex);
return null;
}
}
context.Instance和context.PropertyDescriptor的组合应该足以知道正在编辑的类中的属性。接下来,Ctrl上的虚拟方法(由派生类重写)可以帮助执行类+属性特定任务。此外,您可以将编辑器声明为Ctrl的嵌套类,以访问私有和受保护的成员。如果希望其他人继续回答,您应该回答他们。非常抱歉,Simon,我将尝试跳转并跳到您的时间表。我目前正在考虑对Rubidium提出的建议进行一些修改,当我得到一些有用的反馈时,我会予以反馈。@Rubidium 37,非常感谢。我现在有了一个通用方法,它可以处理Ctrl子类上的任何布尔值。我花了一些时间,因为我不太熟悉反射方法,我必须使用反射方法来获取值,而不必将context.Instance紧密绑定到特定类型。我还没有将它嵌套在Ctrl中(可能真的不需要)。我将在下面发布修改后的代码。再次感谢。