如何在 PropertyGrid 中一键更改布尔属性

How do I change boolean properties with one click in PropertyGrid

我们有一个 windows 表单 PropertyGrid 用于显示所有属性。我们在 Boolean 属性 上绘制了一个复选框,它会根据值自行选中并取消选中。这一切都很好。

问题是,用户想通过单击更改复选框值,而 属性 网格通过双击更改它,我想不出一种方法来处理点击或更改 属性 当 属性 类型为布尔值时单击的值。

如何通过单击更改 属性 值?

PropertyGrid 内部有一些方法,当您单击其 PropertyGridView 内部控件时,我们可以通过反射使用它们来获取鼠标下方的 GridItem

在下面的代码中,我处理了鼠标点击其 PropertyGridView 控件并检查鼠标位置下的项目是否为布尔值 属性,我反转了它的值。该事件将针对 属性 的标签以及 属性 编辑器的图标区域触发:

PropertyGrid

using System;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
public class ExPropertyGrid : PropertyGrid
{
    protected override void OnHandleCreated(EventArgs e)
    {
        base.OnHandleCreated(e);
        var grid = this.Controls[2];
        grid.MouseClick += grid_MouseClick;
    }
    void grid_MouseClick(object sender, MouseEventArgs e)
    {
        var grid = this.Controls[2];
        var flags = BindingFlags.Instance | BindingFlags.NonPublic;
        var invalidPoint = new Point(-2147483648, -2147483648);
        var FindPosition = grid.GetType().GetMethod("FindPosition", flags);
        var p = (Point)FindPosition.Invoke(grid, new object[] { e.X, e.Y });
        GridItem entry = null;
        if (p != invalidPoint) {
            var GetGridEntryFromRow = grid.GetType()
                                          .GetMethod("GetGridEntryFromRow", flags);
            entry = (GridItem)GetGridEntryFromRow.Invoke(grid, new object[] { p.Y });
        }
        if (entry != null && entry.Value != null) {
            object parent;
            if (entry.Parent != null && entry.Parent.Value != null)
                parent = entry.Parent.Value;
            else
                parent = this.SelectedObject;
            if (entry.Value != null && entry.Value is bool) {
                entry.PropertyDescriptor.SetValue(parent,!(bool)entry.Value);
                this.Refresh();
            }
        }
    }
}

在PropertyGrid中绘制CheckBox

public class MyBoolEditor : UITypeEditor
{
    public override bool GetPaintValueSupported
        (System.ComponentModel.ITypeDescriptorContext context)
    { return true; }
    public override void PaintValue(PaintValueEventArgs e)
    {
        var rect = e.Bounds;
        rect.Inflate(1, 1);
        ControlPaint.DrawCheckBox(e.Graphics, rect, ButtonState.Flat |
            (((bool)e.Value) ? ButtonState.Checked : ButtonState.Normal));
    }
}

Class 截图中使用

public class Model
{
    public int Property1 { get; set; }
    [Editor(typeof(MyBoolEditor), typeof(UITypeEditor))]
    public bool Property2 { get; set; }

    [TypeConverter(typeof(ExpandableObjectConverter))]
    public Model Property3 { get; set; }
}

最佳答案使用反射获取鼠标下的GridItem。但是,我看不出这样做有什么意义,因为请求一个专用的 GridItem 就足够了。这是我对 MouseClick 的实现:

 void grid_MouseClick(object sender, MouseEventArgs e)
        {
            GridItem entry = SelectedGridItem;
            if (entry != null && entry.Value != null && entry.Value is bool b)
            {
                var obj = SelectedObjects.Length == 1 ? SelectedObject : SelectedObjects;
                entry.PropertyDescriptor.SetValue(obj, !b);
            }
        }

我想发表评论,但代表还不够高。 接受的答案效果很好。但是,如前所述,代码不会触发 PropertyValueChanged 事件。

添加对 OnPropertyValueChanged 的​​调用会触发 PropertyValueChanged 事件。

entry.PropertyDescriptor.SetValue(parent, !(bool)entry.Value);
this.Refresh();  
base.OnPropertyValueChanged(null);

然后在 PropertyValueChanged 事件代码中就可以访问已经更改的自定义对象了。

要将更改的 属性 传达回表单,请在自定义对象中创建一些属性,并将 Browsable 设置为 false,这样它们就不会出现在 PropertyGrid 中。

[Browsable(false)]
public string changedParent { get; set; }
[Browsable(false)]
public string changedLabel { get; set; }
[Browsable(false)]
public string changedValue { get; set; }

在表单顶部 class 创建这个静态 属性

public partial class Form1 : Form
{
    private static Form1 form = null;

在 Form1 的构造函数中 link 形成这个。

public Form1()
        {
        InitializeComponent();
        ..
        ..
        form = this;

在触发 OnPropertyValueChanged 之前返回 grid_MouseClick 保存更改的 属性 信息。

entry.PropertyDescriptor.SetValue(parent, !(bool)entry.Value);
this.Refresh();
                        
form.sh.changedParent = entry.Parent.Label;
form.sh.changedLabel = entry.Label;
form.sh.changedValue = entry.Value.ToString();
base.OnPropertyValueChanged(null);

现在,在 PropertyValueChanged 事件代码中,您可以确定更改了哪个 属性。

         form.customobject.changedParent
         form.customobject.changedLabel
         form.customobject.changedValue