Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic PropertyGrid properties

So for this project I'm working on, we've decided to use the .NET PropertyGrid control. The propertygrid gets populated with an object that is built during run time, based off of what item is selected by the user in a ListView control.

So, if they select the first item in the ListView such as "Base", the PropertyGrid will show the properties for that component, such as its dimensions. Then they select "Top" and it will show color in the PropertyGrid. Either way, every item in the list is one single "component" object.

Basically, when a ListView item is selected, a loop iterates through a data set to find what properties are associated with that selected component object, then gets thrown into the propertybag class which is displayed in the propertygrid.

What I'm trying to figure out, since these components and properties are all one class, how can I dynamically determine which properties should be a displayed as a drop down menu, or image box, or a text-field.

I'm using Visual Studios 2010 / C#.NET, and for the dynamic property generation I'm using the apparently-popular "Property Bag" class I found on CodeProject by Tony Allowatt. The only thing I can think of would be to maybe add an extra column to the database for the properties and use that to tell the PropertyBag what data type to add? It seems to be a popular topic, but I'm having difficulties figuring out how to do it in conjunction with a dynamically built object.

like image 978
sab669 Avatar asked May 21 '12 16:05

sab669


2 Answers

Its not an answer per se, but I too have been working up to building such a beast. Here is stackoverflow's greatest hits on the topic...

How to modify PropertyGrid at runtime (add/remove property and dynamic types/enums)

How to display a dynamic object in property grid?

PropertyGrid and Dynamic Types of Objects

At first I though I actually needed dynamic objects based on the Expando object, for me it turns out not to be the case. You might want to make sure you to don't fall into that trap.

In my case what I really needed was a collection of custom objects that can have a variable set of properties added to them. Where each property is an instantiation of one of three custom types (stringType, rangeType, or enumType). Once I realized the "dynamic" properties were not going to be of arbitrary class types the project became an easy twist on the code discussed in the three stackoverflow examples. With the question How to modify PropertyGrid at runtime (add/remove property and dynamic types/enums) being almost a direct example of what I am ending up with.

Hope my ramblings help you find your path...

like image 165
Keith Vinson Avatar answered Oct 12 '22 16:10

Keith Vinson


For wpf this may solve whole problem : link to source 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;
        }
    }
}

for viewmodel

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

and xaml

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

like image 45
Mahdi Khalili Avatar answered Oct 12 '22 16:10

Mahdi Khalili