PropertyGrid的替代方案

8
我喜欢PropertyGrid,至少是它背后的概念 - 利用反射和属性来编辑对象,而不需要编写太多UI代码。
然而,我的兴奋很快就消失了,WinForms默认的PropertyGrid非常糟糕。虽然对于编辑简单的对象等方面还可以,但只有到此为止。
  • 它不会为类型为“Object”的动态属性显示适当的UITypeEditors。
  • 一旦您的对象包含集合,您可能能够使用所谓的CollectionEditor进行编辑。但是,它不会触发PropertyValueChanged事件。因此,一旦您需要添加撤销功能,您就会陷入困境。
  • 我仍然没有找到一种优雅的方法来为CollectionEditor添加验证。
  • 如果选择了多个对象,则实现撤销也会出现问题,因为在这种情况下,PropertyValueChanged事件参数ChangedItem为空。
很快我就发现自己在编写hack来解决这些问题,但效果并不理想。
你会怎么做? 是否有一个优雅的解决方案至少解决前三个问题? 是否有另一种属性网格?最好是免费且没有PInvokes?

就属性更改而言,对于集合,如果某些内容更改了集合实例的引用,我会感到担忧。请仔细考虑。 - leppie
3个回答

5
大量的PropertyGrid的优雅之处来自于其简单性。最重要的是,它被设计成与Visual Studio友好协作,“我期望看到它主要在自定义UITypeEditor和扩展中使用,而不是在应用程序代码中使用。”
假设您附加到PropertyGrid上的对象是您自己设计的类?我发现,为了充分利用属性网格,您必须大量地使用属性装饰器来修饰您的类和成员。
您可能会在编写自己的CollectionEditor子类(以及其他类型的编辑器)时找到一些乐趣,并使用[Editor]属性将它们连接到类成员 - 如果可以将此属性连接到您的动态属性,可以强制使用特定的编辑器。
我能想到的唯一方法是覆盖CreateCollectionForm()方法,返回您自己的自定义CollectionEditor.CollectionForm子类的实例,从而向CollectionEditor添加验证。这里有一个机会让您触发更改事件。
不幸的是,我只能点头同意关于实现撤销的断言。您可能需要通过克隆或序列化“备份”受影响的对象,以实现撤销。
我已经看到了内置属性网格控件的替代方案,但它们主要存在于提供不同的视觉样式。

2
如果有人感兴趣 - 这里有一个解决PropertyValueChanged问题的方法,它通过调用System.Object的MemberwiseClone函数模拟更改,如果CollectionEditor的PropertyValueChanged已被触发...
public class FixedCollectionEditor : CollectionEditor
{        
    bool modified;

    public FixedCollectionEditor(Type type)
        : base(type)
    { }

    public override object EditValue(System.ComponentModel.ITypeDescriptorContext context, IServiceProvider provider, object value)
    {            
        value = base.EditValue(context, provider, value);
        if (value != null && modified)
        {
            value = value.GetType()
                .GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic)
                .Invoke(value, new object[] { });                
        }
        modified = false;
        return value;
    }

    protected override CollectionForm CreateCollectionForm()
    {
        CollectionForm collectionForm = base.CreateCollectionForm();

        foreach (Control table in collectionForm.Controls)
        {
            if (!(table is TableLayoutPanel)) { continue; }
            foreach (Control c1 in table.Controls)
            {
                if (c1 is PropertyGrid)
                {
                    PropertyGrid propertyGrid = (PropertyGrid)c1;
                    propertyGrid.PropertyValueChanged += new PropertyValueChangedEventHandler(GotModifiedHandler);
                }
                if (c1 is TableLayoutPanel)
                {
                    foreach (Control c2 in c1.Controls)
                    {
                        if (!(c2 is Button)) { continue; }
                        Button button = (Button)c2;
                        if (button.Name == "addButton" || button.Name == "removeButton")
                        {
                            button.Click += new EventHandler(GotModifiedHandler);
                            if (button.ContextMenuStrip != null)
                            {
                                button.ContextMenuStrip.ItemClicked += new ToolStripItemClickedEventHandler(GotModifiedHandler);
                            }
                        }
                    }
                }
            }
        }
        return collectionForm;
    }

    void GotModifiedHandler(object sender, EventArgs e)
    {
        modified = true;
    }
}

1

Visualhint销售一个替代属性网格的工具,可能会有所帮助。但由于我从未在实际项目中使用过它,无法确定其效果如何。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接