PropertyGrid 替代方案

发布于 2024-09-16 04:42:55 字数 568 浏览 7 评论 0原文

我喜欢 PropertyGrid,至少喜欢它背后的概念 - 使用反射和属性来编辑对象,而无需编写太多 UI 代码。

不过,我的兴奋很快就消失了,随 WinForms 一起提供的默认 PropertyGrid 简直太糟糕了。嗯,对于编辑简单的对象等来说很好,但也仅此而已。

  • 它不会为类型为“Object”的动态属性显示适当的 UITypeEditors。
  • 一旦您的对象包含集合,您就可以使用所谓的 CollectionEditor 来编辑它们。但是,它不会触发 PropertyValueChanged 事件。因此,一旦您需要添加撤消功能,您就完蛋了。
  • 而且我还没有找到一种优雅的方法来为 CollectionEditor 添加验证。
  • 如果选择了多个对象,那么实现撤消也会出现问题,因为在这种情况下,PropertyValueChanged 事件参数 ChangedItem 为 null。

我很快发现自己编写了一些 hack 来解决这些问题,但结果却不太令人满意。

你会怎么办? 至少前三个问题是否有一个优雅的解决方案? 有替代的属性网格吗?最好是免费的没有 PInvokes?

I love PropertyGrid, well, at least the concept behind it - use reflection and attributes to edit your objects without writing much UI code.

My excitement died out pretty quickly though, the default PropertyGrid shipping with WinForms flat-out sucks. Well, it's fine for editing simple objects and such, but that's as far as it goes.

  • It doesn't display appropriate UITypeEditors for dynamic properties which have type "Object".
  • As soon as your objects contain collections, you might be able to edit them with so called CollectionEditor. However, it won't fire PropertyValueChanged event. So once you need to add undo functionality, you're screwed.
  • And I still haven't found an elegant way to add validation for CollectionEditor.
  • It's also problematic to implement undo if you have multiple objects selected, because in that case PropertyValueChanged event args ChangedItem is null.

I soon found myself writing hacks to address those issues with less than agreeable results.

What would you do?
Is there an elegant solution to at least the first three issues?
Is there an alternative propertygrid? Preferably free & without PInvokes?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

披肩女神 2024-09-23 04:42:55

PropertyGrid 的许多优雅来自于它的简单性。最重要的是,它的设计目的是与 Visual Studio 很好地配合,我希望看到它主要用于自定义 UITypeEditor 和扩展,而不是应用程序代码中。

想必您附加到 PropertyGrid 的对象是您自己设计的类?我发现,为了充分利用属性网格,您必须用属性大量装饰您的类和成员。

您可能会发现编写自己的 CollectionEditor 子类(以及其他类型的编辑器)并使用 [Editor] 属性将它们附加到类成员 - 如果您可以附加的话,您可能会发现一些乐趣将此属性添加到您的动态属性中,您可以强制使用特定的编辑器。

我能想到的向CollectionEditor添加验证的唯一方法是重写CreateCollectionForm()方法,返回您自己的CollectionEditor.CollectionForm的自定义子类的实例。您有机会从这里触发更改事件。

不幸的是,我所能做的就是点头并同意关于实施撤消的主张。您可能必须通过克隆或序列化“备份”受影响的对象才能实现撤消。

我已经看到了内置属性网格控件的替代方案,但它们的存在主要是为了提供不同的视觉样式。

A lot of the PropertyGrid's elegance comes from its simplicity. Above all else, it's designed to play nice with Visual Studio, and i'd expect to see it used primarily in custom UITypeEditors and extensions, rather than in application code.

Presumably the objects you are attaching to the PropertyGrid are classes of your own design? I've found that, in order to make good use of the property grid, you have to heavily decorate your classes and members with attributes.

You may find some joy in writing your own subclasses of CollectionEditor (and other types of editors) and attaching them to class members using the [Editor] attribute - if you can attach this attribute to your dynamic properties, you can force the use of a particular editor.

The only way I can think of adding validation to CollectionEditor is to override the CreateCollectionForm() method, returning an instance of your own, custom subclass of CollectionEditor.CollectionForm. There's a chance you will be able to fire the change events from here.

Unfortunately all I can do is nod and agree with the assertion about implementing undo. You might have to resort to 'backing up' the affected objects via cloning or serialization in order to implement undo.

I've seen alternatives to the built-in property grid control, but they exist mainly to offer different visual styles.

○愚か者の日 2024-09-23 04:42:55

如果有人感兴趣 - 这是 PropertyValueChanged 问题的解决方法,如果 CollectionEditor 的 PropertyValueChanged 已被触发,则通过调用 System.Object 的 MemberwiseClone 函数来模拟更改...

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

If someone is interested - here is a workaround for the PropertyValueChanged problem that simulates a change by invoking the MemberwiseClone function of System.Object if the CollectionEditor's PropertyValueChanged had been fired ...

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;
    }
}
野味少女 2024-09-23 04:42:55

Visualhint 出售可能有帮助的属性网格替代品。由于我从未在实际项目中使用过它,所以我不知道它的效果如何。

Visualhint sells a replacement for the property grid that may help. As I have never used it in a real project, I don't know how well it works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文