Home Forums WPF controls Other WPF controls PropertyGrid : Dictionary Not Displaying Values using ICustomTypeDescriptor

Viewing 5 posts - 1 through 5 (of 5 total)
  • Author
    Posts
  • Krush
    Participant
    Post count: 3
    #44724 |

    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;
                }
            }
        }
    }
    Fawzi [Xceed]
    Member
    Post count: 722

    Hi,

    Your code will work if you modify your “object GetPropertyOwner( PropertyDescriptor pd )” method to return “this” instead of “this.dictionary”. The indexer of this class already return the dictionary wanted key value.

    When the PropertyGrid tries to do a binding between DescriptorPropertyDefinitionBase.Value and a SelectedObject.(PropertyItem.Name) (read DictionaryPropertyGridAdapter.Test when GetPropertyOwner returns this) it will work because DictionaryPropertyGridAdapter will return dictionary[“Test”].

    When the PropertyGrid tries to do a binding between DescriptorPropertyDefinitionBase.Value and a SelectedObject.(PropertyItem.Name) (read dictionary.Test when GetPropertyOwner returns dictionary) it will not work because Test is not a property from dictionary.

    Just make sure to not use dots in your dictionary indexes. With an index like “Test.With.Dots”, the binding will try to search for a “With” in object “Test” and won’t find it.

    Krush
    Participant
    Post count: 3

    Thanks that worked. Now I just need to see if I can set attributes on these so I can change the display names to include dots as that’s what my variables are known to be named as.

    rajeshdhiman
    Participant
    Post count: 3

    Hi Krush

    How did you do that dot notation in dictionary keys.. i ran into same situation…

    eg: this.Variables[“Test.With.Dots”] = 200.5;

    Thanks in Advance

    Krush
    Participant
    Post count: 3

    I added:

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

    to DictionaryPropertyDescriptor which allows me to set the displayed named differently than the actual name.

    For example:

    this.Variables[“Test.With.Dots”] = 200.5; becomes this.Variables[“Test_With_Dots”] = 200.5;

    I made a helper that does this:

    
            private void AddVariable(
                string name,
                object value,
                string category = null,
                string displayName = null,
                string description = null)
            {
                this.variables[name] = value;
                this.variableAttributes[name] = new DictionaryPropertyGridAdapter<string, object>.PropertyAttributes
                                                {
                                                    Category = category ?? "",
                                                    DisplayName = displayName ?? "",
                                                    Description = description ?? "",
                                                    IsReadOnly = false
                                                };
            }
    

    And here’s my updated DictionaryPropertyGridAdapter.cs

    // --------------------------------------------------------------------------------------------------------------------
    // <copyright file="DictionaryPropertyGridAdapter.cs" company="Lost Minions">
    //   Copyright (c) Lost Minions. All rights reserved.
    // </copyright>
    // <summary>
    //   Defines the StressVariables type.
    // </summary>
    // --------------------------------------------------------------------------------------------------------------------
    
    namespace EmptyWPF_CSharp.Utility
    {
        using System;
        using System.Collections;
        using System.Collections.Generic;
        using System.ComponentModel;
        using System.Runtime.CompilerServices;
    
        using EmptyWPF_CSharp.Properties;
    
        [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;
                }
            }
        }
    }
Viewing 5 posts - 1 through 5 (of 5 total)

You must be logged in to reply to this topic.