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.
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...
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>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With