使 Propertygrid 在运行时显示绑定 属性 符号

Make Propertygrid show bound property symbol at runtime

当您使用 DataBindings 将控件的 属性 绑定到数据源时,属性 网格将在该 属性 对应的网格项中显示一个紫色或黑色的小符号。

即使您将 PropertyGrid 放在窗体上并将其 SelectedObject 属性 设置为具有绑定 属性 的控件,窗体上的 PropertyGrid 也会显示该符号。

但仅在设计时。

是否有一种(简单的)方法可以使完全相同的 PropertyGrid 在运行时显示此符号?

它由Visual Studio 设计器内部的东西处理。但您也可以将此功能添加到 PropertyGrid:

您需要实施 IPropertyValueUIService 并使用反射,将服务实例分配给应显示字形的网格条目。此实现有一个方法 GetPropertyUIValueItems,可用于提供该字形和工具提示以显示在 PropertyGrid 中的 属性 标签附近。这些值将在 属性 网格条目的 PaintLabel 方法中使用。

然后创建继承的PropertyGrid并覆盖OnSelectedObjectsChangedOnPropertySortChanged。在那些呈现数据绑定集合中 属性 的每个 属性 网格条目项的方法中,将已实现 IPropertyValueUIService 的实例设置为 pvSvc private 属性 的 PropertyGrid 并附加一个事件处理程序,当 PropertyGrid 请求有关 属性 的附加信息时将调用该事件处理程序。通过使用 GetPropertyUIValueItems 附加事件处理程序,您可以 return 您将要在 属性.

前面显示的工具提示和图像

例子

您可以在此处下载完整示例:

你可以找到主要的类实现如下。

PropertyValueUIService

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing.Design;

namespace PropertyGridDataBindingGlyph
{
    public class PropertyValueUIService : IPropertyValueUIService
    {
        private PropertyValueUIHandler handler;
        private ArrayList items;

        public event EventHandler PropertyUIValueItemsChanged;
        public void NotifyPropertyValueUIItemsChanged()
        {
            PropertyUIValueItemsChanged?.Invoke(this, EventArgs.Empty);
        }
        public void AddPropertyValueUIHandler(PropertyValueUIHandler newHandler)
        {
            handler += newHandler ?? throw new ArgumentNullException("newHandler");
        }
        public PropertyValueUIItem[] GetPropertyUIValueItems(ITypeDescriptorContext context, PropertyDescriptor propDesc)
        {
            if (propDesc == null)
                throw new ArgumentNullException("propDesc");
            if (this.handler == null)
                return new PropertyValueUIItem[0];
            lock (this)
            {
                if (this.items == null)
                    this.items = new ArrayList();
                this.handler(context, propDesc, this.items);
                int count = this.items.Count;
                if (count > 0)
                {
                    PropertyValueUIItem[] propertyValueUiItemArray = new PropertyValueUIItem[count];
                    this.items.CopyTo((Array)propertyValueUiItemArray, 0);
                    this.items.Clear();
                    return propertyValueUiItemArray;
                }
            }
            return null;
        }
        public void RemovePropertyValueUIHandler(PropertyValueUIHandler newHandler)
        {
            handler -= newHandler ?? throw new ArgumentNullException("newHandler");

        }
    }
}

ExPropertyGrid

using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Linq;
using System.Reflection;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace PropertyGridDataBindingGlyph
{
    public class ExPropertyGrid : PropertyGrid
    {
        Bitmap dataBitmap;
        public ExPropertyGrid()
        {
            dataBitmap = new Bitmap(typeof(ControlDesigner).Assembly
                 .GetManifestResourceStream("System.Windows.Forms.Design.BoundProperty.bmp"));
            dataBitmap.MakeTransparent();
        }
        protected override void OnSelectedObjectsChanged(EventArgs e)
        {
            base.OnSelectedObjectsChanged(e);
            this.BeginInvoke(new Action(() => { ShowGlyph(); }));
        }
        protected override void OnPropertySortChanged(EventArgs e)
        {
            base.OnPropertySortChanged(e);
            this.BeginInvoke(new Action(() => { ShowGlyph(); }));
        }
        private void ShowGlyph()
        {
            var grid = this.Controls[2];
            var field = grid.GetType().GetField("allGridEntries",
            System.Reflection.BindingFlags.NonPublic |
            System.Reflection.BindingFlags.Instance | BindingFlags.FlattenHierarchy);
            var value = field.GetValue(grid);
            if (value == null)
                return;
            var entries = (value as IEnumerable).Cast<GridItem>().ToList();
            if (this.SelectedObject is Control)
            {
                ((Control)this.SelectedObject).DataBindings.Cast<Binding>()
                    .ToList().ForEach(binding =>
                    {
                        var item = entries.Where(x => x.PropertyDescriptor?.Name == binding.PropertyName).FirstOrDefault();
                        var pvSvcField = item.GetType().GetField("pvSvc", BindingFlags.NonPublic |
                            BindingFlags.Instance | BindingFlags.FlattenHierarchy);
                        IPropertyValueUIService pvSvc = new PropertyValueUIService();
                        pvSvc.AddPropertyValueUIHandler((context, propDesc, valueUIItemList) =>
                        {
                            valueUIItemList.Add(new PropertyValueUIItem(dataBitmap, (ctx, desc, invokedItem) => { }, GetToolTip(binding)));
                        });
                        pvSvcField.SetValue(item, pvSvc);
                    });
            }
        }
        private static string GetToolTip(Binding binding)
        {
            var value = "";
            if (binding.DataSource is ITypedList)
                value = ((ITypedList)binding.DataSource).GetListName(new PropertyDescriptor[] { });
            else if (binding.DataSource is Control)
                value = ((Control)binding.DataSource).Name;
            else if (binding.DataSource is Component)
                value = ((Component)binding.DataSource).Site?.Name;

            if (string.IsNullOrEmpty(value))
                value = "(List)";
            return value + " - " + binding.BindingMemberInfo.BindingMember;
        }
    }
}