0

语境

我一直在为自定义 ASP.Net Web 控件开发自定义集合编辑器/设计器。Web 控件公开了一个奇怪的层次结构,因此自定义编辑器似乎是正确的做法,可以让开发人员更轻松。

构建 ASPX 代码并使用 Web 控件可以正常工作。换句话说,事情就像PersistChildrenParseChildren被照顾。

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 代码,我可以看到维度不再存在。因此,简而言之,问题是我必须以某种方式告诉 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 该属性已更改...我只是不知道该怎么做。

4

1 回答 1

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

如果您正在实现 a UITypeEditor,请确保使用context.InstancefromEditValue作为控件并使用 givenprovider来查找服务。

于 2013-06-26T06:50:03.233 回答