8

因此,对于我正在进行的这个项目,我们决定使用 .NET PropertyGrid 控件。根据用户在 ListView 控件中选择的项目,propertygrid 将使用在运行时构建的对象进行填充。

因此,如果他们选择 ListView 中的第一项,例如“Base”,PropertyGrid 将显示该组件的属性,例如其尺寸。然后他们选择“顶部”,它将在 PropertyGrid 中显示颜色。无论哪种方式,列表中的每一项都是一个“组件”对象。

基本上,当一个 ListView 项目被选中时,一个循环遍历数据集以查找与该选定组件对象相关联的属性,然后被扔到显示在 propertygrid 中的 propertybag 类中。

我想弄清楚的是,由于这些组件和属性都是一个类,我如何动态确定哪些属性应该显示为下拉菜单、图像框或文本字段。

我正在使用 Visual Studios 2010 / C#.NET,对于动态属性生成,我使用的是 Tony Allowatt 在 CodeProject 上发现的显然很流行的“Property Bag”类。我唯一能想到的可能是在数据库中添加一个额外的属性列,并用它来告诉 PropertyBag 要添加什么数据类型?这似乎是一个热门话题,但我很难弄清楚如何与动态构建的对象结合使用。

4

3 回答 3

7

它本身并不是一个答案,但我也一直在努力打造这样一个野兽。这是stackoverflow关于该主题的最大热门......

如何在运行时修改 PropertyGrid(添加/删除属性和动态类型/枚举)

如何在属性网格中显示动态对象?

PropertyGrid 和对象的动态类型

起初我虽然实际上需要基于 Expando 对象的动态对象,但对我来说事实并非如此。你可能想确保你不会落入那个陷阱。

在我的情况下,我真正需要的是一个自定义对象的集合,这些对象可以添加一组可变的属性。其中每个属性都是三种自定义类型(stringType、rangeType 或 enumType)之一的实例化。一旦我意识到“动态”属性不会是任意类类型,该项目就变成了对三个 stackoverflow 示例中讨论的代码的简单改动。问题如何在运行时修改 PropertyGrid(添加/删除属性和动态类型/枚举)几乎是我最终得到的直接示例。

希望我的杂谈能帮助你找到你的道路......

于 2012-05-25T22:16:18.223 回答
2

对于 wpf,这可能会解决整个问题:链接到源https://xceed.com/forums/topic/propertygrid-dictionary-not-displaying-values-using-icustomtypedescriptor/

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;

[RefreshProperties(RefreshProperties.All)]
public class DictionaryPropertyGridAdapter<TKey, TValue> : ICustomTypeDescriptor, INotifyPropertyChanged
{
    #region Fields

    private readonly IDictionary<TKey, PropertyAttributes> propertyAttributeDictionary;

    private readonly IDictionary<TKey, TValue> propertyValueDictionary;

    #endregion

    #region Constructors and Destructors

    public DictionaryPropertyGridAdapter(
        IDictionary<TKey, TValue> propertyValueDictionary,
        IDictionary<TKey, PropertyAttributes> propertyAttributeDictionary = null)
    {
        this.propertyValueDictionary = propertyValueDictionary;
        this.propertyAttributeDictionary = propertyAttributeDictionary;
    }

    #endregion

    #region Events

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion

    public AttributeCollection GetAttributes()
    {
        return TypeDescriptor.GetAttributes(this, true);
    }

    public string GetClassName()
    {
        return TypeDescriptor.GetClassName(this, true);
    }

    public string GetComponentName()
    {
        return TypeDescriptor.GetComponentName(this, true);
    }

    public TypeConverter GetConverter()
    {
        return TypeDescriptor.GetConverter(this, true);
    }

    public EventDescriptor GetDefaultEvent()
    {
        return TypeDescriptor.GetDefaultEvent(this, true);
    }

    public PropertyDescriptor GetDefaultProperty()
    {
        return null;
    }

    public object GetEditor(Type editorBaseType)
    {
        return TypeDescriptor.GetEditor(this, editorBaseType, true);
    }

    public EventDescriptorCollection GetEvents(Attribute[] attributes)
    {
        return TypeDescriptor.GetEvents(this, attributes, true);
    }

    public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        ArrayList properties = new ArrayList();
        foreach (var kvp in this.propertyValueDictionary)
        {
            properties.Add(
                new DictionaryPropertyDescriptor(
                    kvp.Key,
                    this.propertyValueDictionary,
                    this.propertyAttributeDictionary));
        }

        PropertyDescriptor[] props = (PropertyDescriptor[])properties.ToArray(typeof(PropertyDescriptor));

        return new PropertyDescriptorCollection(props);
    }

    public object GetPropertyOwner(PropertyDescriptor pd)
    {
        return this;
    }

    EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
    {
        return TypeDescriptor.GetEvents(this, true);
    }

    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
    {
        return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]);
    }

    //[NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public class PropertyAttributes
    {
        public string Category { get; set; }

        public string Description { get; set; }

        public string DisplayName { get; set; }

        public bool IsReadOnly { get; set; }
    }

    internal class DictionaryPropertyDescriptor : PropertyDescriptor
    {
        #region Fields

        private readonly IDictionary<TKey, PropertyAttributes> attributeDictionary;

        private readonly TKey key;

        private readonly IDictionary<TKey, TValue> valueDictionary;

        #endregion

        #region Constructors and Destructors

        internal DictionaryPropertyDescriptor(
            TKey key,
            IDictionary<TKey, TValue> valueDictionary,
            IDictionary<TKey, PropertyAttributes> attributeDictionary = null)
            : base(key.ToString(), null)
        {
            this.valueDictionary = valueDictionary;
            this.attributeDictionary = attributeDictionary;
            this.key = key;
        }

        #endregion

        public override string Category => this.attributeDictionary?[this.key].Category ?? base.Category;

        public override Type ComponentType => null;

        public override string Description => this.attributeDictionary?[this.key].Description ?? base.Description;

        public override string DisplayName => this.attributeDictionary?[this.key].DisplayName ?? base.DisplayName;

        public override bool IsReadOnly => this.attributeDictionary?[this.key].IsReadOnly ?? false;

        public override Type PropertyType => this.valueDictionary[this.key].GetType();

        public override bool CanResetValue(object component)
        {
            return false;
        }

        public override object GetValue(object component)
        {
            return this.valueDictionary[this.key];
        }

        public override void ResetValue(object component)
        {
        }

        public override void SetValue(object component, object value)
        {
            this.valueDictionary[this.key] = (TValue)value;
        }

        public override bool ShouldSerializeValue(object component)
        {
            return false;
        }
    }
}

对于视图模型

class classViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    private IDictionary<string, object> Variables { get; set; } = new ConcurrentDictionary<string, object>();
    private DictionaryPropertyGridAdapter<string, object> _selectedObj;
    public DictionaryPropertyGridAdapter<string, object> SelectedObj
    {
        get
        {

            this.Variables["Bool"] = false;
            this.Variables["Int"] = 200;
            this.Variables["Float"] = 200.5;
            this.Variables["String"] = "help";
            _selectedObj = new DictionaryPropertyGridAdapter<string, object>(this.Variables);


            return _selectedObj;
        }
        set {
            _selectedObj = value;
            OnPropertyChanged(nameof(this.SelectedObj));
        }
    }
}

和 xml

<Window x:Class="testPropertyGrid.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:testPropertyGrid"
    xmlns:wpftoolkit="http://schemas.xceed.com/wpf/xaml/toolkit"
    mc:Ignorable="d"
    Title="MainWindow" Height="350" Width="525">
<Window.Resources>
    <local:classViewModel x:Key="ViewModel"/>
</Window.Resources>
<Grid>
    <wpftoolkit:PropertyGrid Name="propertyGrid1" SelectedObject="{Binding SelectedObj ,Source={StaticResource ViewModel}}">

    </wpftoolkit:PropertyGrid>
</Grid>

于 2018-07-03T06:37:03.553 回答
1

我相信此链接将为您提供体面的输入来解决您的问题。希望它有帮助。

编辑以包含链接的内容:

动态属性声明:

dynamic employee = new BusinessObject(); 
employee.FirstName = "John"; 
employee.LastName = "Doe"; 

支持动态属性的类:

public class BusinessObject : DynamicObject 
{
     private readonly IDictionary<string, object> dynamicProperties = 
         new Dictionary<string, object>();

     public override bool TryGetMember(GetMemberBinder binder, out object result)
     {
          var memberName = binder.Name;
          return dynamicProperties.TryGetValue(memberName, out result);
     }

     public override bool TrySetMember(SetMemberBinder binder, object value)
     {
          var memberName = binder.Name;
          dynamicProperties[memberName] = value;
          return true;
     }
}
于 2015-10-15T10:53:05.403 回答