上下文
我一直在为自定义ASP.Net Web控件的自定义集合编辑器/设计器工作。 Web控件暴露了一个奇怪的层次结构,因此自定义编辑器似乎是正确的做法,使开发人员更容易。
构建ASPX代码并使用Web控件工作。换句话说,PersistChildren
和ParseChildren
之类的事情得到了解决。
Web控件中属性的签名如下所示:
[PersistenceMode(PersistenceMode.InnerProperty)]
[Themeable(false)]
[Browsable(false)]
public virtual DimensionsCollection Dimensions { get; internal set; }
请注意,该物业不公开;如果是公开的,设计师中的各种事情都会出错。 DimensionsCollection
是一个只继承List<Dimension>
的类。 Dimension
类本身并不华丽,只是具有某些属性的东西。
仅仅因为我认为它看起来很酷,我希望能够从设计器中的动作修改属性。为此,我实施了ControlDesigner
课程并添加了ActionList
。其中一个动作是链接按钮打开编辑器:
var editor = new Editors.DimensionEditor(control.Dimensions);
if (editor.ShowDialog() == DialogResult.OK)
{ /* SEE BELOW */ }
编辑器本身是一个窗口表单,它以List<Dimension>
作为构造函数参数并修改集合。
问题
当我使用此代码时,我可以看到编辑器工作,并且控件集合在“设计器”视图中更新。如果我多次打开编辑器,状态会发生变化,这意味着内存中的某个位置会由编辑器更新。
但是,如果我转到ASPX代码,我可以看到Dimensions不再存在了。所以,简而言之,问题是我不得不告诉Visual Studio将属性写入/序列化/持久化到ASPX文件。 (简单...... )
奇怪的是,我无法在任何地方找到如何做到这一点......即使正常CollectionEditor
似乎能够做到这一点(不幸的是我无法继承)
我试过的一些事情
对于其他属性,我注意到你必须使用这样的东西,但这似乎不起作用。代码是在标记为“参见下方”的点处输入的,或者在某些情况下输入到设计者调用的辅助调用中:
PropertyDescriptor pd = TypeDescriptor.GetProperties(base.Component)["Dimensions"];
// use setter with internal property -> no effect
// this.OnComponentChanged(this, new ComponentChangedEventArgs(this.Component, pd, null, newdim)); -> no effect
// use getter to obtain list -> populate that using another list that's created in the editor
我能理解为什么它不起作用;显然有人必须告诉Visual Studio该属性已经改变......我只是不知道该怎么做。
答案 0 :(得分:0)
在网上显然没有解释如何做到这一点的消息来解决这个问题确实很痛苦。
基本上,您希望使用OnComponentChanging / Changed方法通知设计人员。显然,设计师将交易用于其余的逻辑。 (我的猜测是它与撤销/重做行为有关)。对于普通类型,当您使用PropertyDescriptor
时,这会自动完成,对于集合,它显然不会包装集合,这意味着您必须手动执行此操作。
要解决此问题,您需要在UITypeEditor
或DesignerActionList
类中创建一个这样的小方法:
private void ChangeAction(List<Dimension> newDimensions)
{
IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost;
PropertyDescriptor pd = TypeDescriptor.GetProperties(typeof(MyControl))["Dimensions"];
var dimensions = (DimensionsCollection)pd.GetValue(control);
var trans = host.CreateTransaction();
IComponentChangeService ccs = (IComponentChangeService)GetService(typeof(IComponentChangeService));
ccs.OnComponentChanging(control, pd);
dimensions.Clear();
dimensions.AddRange(newDimensions);
ccs.OnComponentChanged(control, pd, null, dimensions);
trans.Commit();
}
如果您要实施UITypeEditor
,请确保使用context.Instance
中的EditValue
作为控件,并使用给定的provider
来查找服务。