Currently I can only get the names of the dynamic properties to display not the values, the values result in the follow errors:
System.Windows.Data Error: 40 : BindingExpression path error: 'Test' property not found on 'object' ''ConcurrentDictionary<code>2' (HashCode=47097466)'. BindingExpression:Path=Test.With.Dots; DataItem='ConcurrentDictionary</code>2' (HashCode=47097466); target element is 'DescriptorPropertyDefinition' (HashCode=52822235); target property is 'Value' (type 'Object')
System.Windows.Data Error: 40 : BindingExpression path error: 'Test2' property not found on 'object' ''ConcurrentDictionary<code>2' (HashCode=47097466)'. BindingExpression:Path=Test2.With.Dots; DataItem='ConcurrentDictionary</code>2' (HashCode=47097466); target element is 'DescriptorPropertyDefinition' (HashCode=57448811); target property is 'Value' (type 'Object')
System.Windows.Data Error: 40 : BindingExpression path error: 'Test' property not found on 'object' ''ConcurrentDictionary<code>2' (HashCode=47097466)'. BindingExpression:Path=Test; DataItem='ConcurrentDictionary</code>2' (HashCode=47097466); target element is 'DescriptorPropertyDefinition' (HashCode=25770419); target property is 'Value' (type 'Object')
System.Windows.Data Error: 40 : BindingExpression path error: 'Test2' property not found on 'object' ''ConcurrentDictionary<code>2' (HashCode=47097466)'. BindingExpression:Path=Test2; DataItem='ConcurrentDictionary</code>2' (HashCode=47097466); target element is 'DescriptorPropertyDefinition' (HashCode=24589509); target property is 'Value' (type 'Object')
MainWindow.xaml
<Window
x:Class="WpfApplication1.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:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
Title="MainWindow"
Width="525"
Height="350"
mc:Ignorable="d">
<Grid>
<xctk:PropertyGrid x:Name="PropertyGrid" />
</Grid>
</Window>
MainWindow.xaml.cs
namespace WpfApplication1
{
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
#region Constructors and Destructors
public MainWindow()
{
this.InitializeComponent();
this.Variables["Test"] = false;
this.Variables["Test2"] = 200;
this.Variables["Test.With.Dots"] = 200.5;
this.Variables["Test2.With.Dots"] = "help";
this.PropertyGrid.SelectedObject = new DictionaryPropertyGridAdapter<string, object>(this.Variables);
}
#endregion
public IDictionary<string, object> Variables { get; set; } = new ConcurrentDictionary<string, object>();
}
}
DictionaryPropertyGridAdapter.cs
namespace WpfApplication1
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Runtime.CompilerServices;
[RefreshProperties(RefreshProperties.All)]
public class DictionaryPropertyGridAdapter<T, U> : ICustomTypeDescriptor, INotifyPropertyChanged
{
#region Fields
private readonly IDictionary<T, U> dictionary;
#endregion
#region Constructors and Destructors
public DictionaryPropertyGridAdapter(IDictionary<T, U> dictionary)
{
this.dictionary = dictionary;
}
#endregion
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
[Browsable(false)]
public U this[T key]
{
get
{
return this.dictionary[key];
}
set
{
this.dictionary[key] = value;
}
}
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 e in this.dictionary)
{
properties.Add(new DictionaryPropertyDescriptor(this.dictionary, e.Key));
}
PropertyDescriptor[] props = (PropertyDescriptor[])properties.ToArray(typeof(PropertyDescriptor));
return new PropertyDescriptorCollection(props);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this.dictionary;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public class DictionaryPropertyDescriptor : PropertyDescriptor
{
#region Fields
private readonly IDictionary<T, U> dictionary;
private readonly T key;
#endregion
#region Constructors and Destructors
internal DictionaryPropertyDescriptor(IDictionary<T, U> dictionary, T key)
: base(key.ToString(), null)
{
this.dictionary = dictionary;
this.key = key;
}
#endregion
public override Type ComponentType => null;
public override bool IsReadOnly => false;
public override Type PropertyType => this.dictionary[this.key].GetType();
public override bool CanResetValue(object component)
{
return false;
}
public override object GetValue(object component)
{
return this.dictionary[this.key];
}
public override void ResetValue(object component)
{
}
public override void SetValue(object component, object value)
{
this.dictionary[this.key] = (U)value;
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
}
}