This is a migrated thread and some comments may be shown as answers.

Need Popup for DefineEnum based Enums

3 Answers 109 Views
PropertyGrid
This is a migrated thread and some comments may be shown as answers.
John
Top achievements
Rank 1
Veteran
John asked on 09 Jun 2020, 06:06 AM

Hello,

I cannot get a C# enumeration property based on dynamically created DefineEnum call to show up as popup on a PropertyGrid.  I've tried several things but nothing worked.  Also, I am dynamically creating the PropertyDefinition items instead of statically defining anything via XAML.

Here is the basic code to load the property definitions:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    PropertyDefinitionCollection properties = EditorPropertyGrid.PropertyDefinitions;
 
    List<KeyValueBase> testList = new List<KeyValueBase>();
 
    TestEnumKeyValue first = new TestEnumKeyValue() { Key = "Static Enum Prop", Value = TestEnum.test3 };
    testList.Add(first);
    properties.Add(new PropertyDefinition()
    {
        DisplayName = first.Key,
        Binding = new Binding("Value") { Source = first }
    });
 
    DynamicEnumBuilder _enums = new DynamicEnumBuilder();
    _enums.BuildDictionaries();
    Type dynamicEnum = _enums.getEnum("Employee", "Status");
     
    EnumKeyValue second = new EnumKeyValue() { Key = "Dynamic Enum Prop", Value = Enum.Parse(dynamicEnum, "PartTime")};
    testList.Add(second);
    properties.Add(new PropertyDefinition()
    {
        DisplayName = second.Key,
        Binding = new Binding("Value") { Source = second }
    });
 
    // ************************
 
    EditorPropertyGrid.Item = testList;
}

Here are the key value pair definitions:

enum TestEnum
{
    test1,
    test2,
    test3
}
 
class KeyValueBase
{
 
}
 
class TestEnumKeyValue : KeyValueBase
{
    public string Key { get; set; }
    public TestEnum Value { get; set; }
}
 
class EnumKeyValue : KeyValueBase
{
    public string Key { get; set; }
    public object Value { get; set; }
}

 

Here is the XAML:

<Grid x:Name="EditorGrid">
    <telerik:RadPropertyGrid x:Name="EditorPropertyGrid" Margin="10,10,10,10" DescriptionPanelVisibility="Collapsed" SearchBoxVisibility="Collapsed" AutoGeneratePropertyDefinitions="False" NestedPropertiesVisibility="Visible" AutoGenerateBindingPaths="False" SortAndGroupButtonsVisibility="Collapsed" />
</Grid>

 

And here is how I generate the dynamic enum:

class DynamicEnumBuilder
    {
        public Dictionary<string, Dictionary<string, Type>> _enumDictionary = new Dictionary<string, Dictionary<string, Type>>();
 
        public void BuildDictionaries()
        {
            Dictionary<string, Dictionary<string, List<ValuePair>>> _rawDictionary = new Dictionary<string, Dictionary<string, List<ValuePair>>>();
            _rawDictionary["Employee"] = new Dictionary<string, List<ValuePair>>();
            _rawDictionary["Employee"]["Status"] = new List<ValuePair>();
            _rawDictionary["Employee"]["Status"].Add(new ValuePair() { Name = "FullTime", StoredValue = "1" });
            _rawDictionary["Employee"]["Status"].Add(new ValuePair() { Name = "PartTime", StoredValue = "2" });
            _rawDictionary["Employee"]["Status"].Add(new ValuePair() { Name = "Retired", StoredValue = "3" });
            BuildValueDictionary(_rawDictionary);
        }
 
        private void BuildValueDictionary(Dictionary<string, Dictionary<string, List<ValuePair>>> _rawDictionary)
        {
            // Get the current application domain for the current thread.
            AppDomain currentDomain = AppDomain.CurrentDomain;
 
            //// Create a dynamic assembly in the current application domain,
            //// and allow it to be executed ONLY, NOT to be saved on disk!
            AssemblyName aName = new AssemblyName("TempAssembly");
            AssemblyBuilder ab = currentDomain.DefineDynamicAssembly(
                aName, AssemblyBuilderAccess.Run);
 
            // Define a dynamic module in "TempAssembly" assembly. For a single-
            // module assembly, the module has the same name as the assembly.
            ModuleBuilder mb = ab.DefineDynamicModule(aName.Name);
 
            foreach(var fieldEntry in _rawDictionary)
            {
                Dictionary<string, Type> valueTableDic = null;
                if (!_enumDictionary.ContainsKey(fieldEntry.Key))
                {
                    valueTableDic = new Dictionary<string, Type>();
                    _enumDictionary.Add(fieldEntry.Key, valueTableDic);
                }
                else
                {
                    valueTableDic = _enumDictionary[fieldEntry.Key];
                }
 
                Dictionary<string, List<ValuePair>> rawEnumDict = fieldEntry.Value;
                foreach (var fieldEnum in rawEnumDict)
                {
                    // Define a public enumeration with the name "Elevation" and an
                    // underlying type of Integer.
                    EnumBuilder eb = mb.DefineEnum(fieldEnum.Key+"Enum", TypeAttributes.Public, typeof(int));
 
                    // build the enumerations
                    List<ValuePair> values = fieldEnum.Value;
                    foreach (var valuePair in values)
                    {
                        eb.DefineLiteral(valuePair.Name, int.Parse(valuePair.StoredValue));
                    }
                    // Create the type and save the assembly.
                    Type dynamicEnum = eb.CreateType();
 
                    // now put it in the value dictionary
                    valueTableDic[fieldEnum.Key] = dynamicEnum;
                }
            }
        }
 
        public Type getEnum(String tablename, String fieldname)
        {
            Type result = null;
            if (_enumDictionary.ContainsKey(tablename))
            {
                Dictionary<string, Type> fieldDict = _enumDictionary[tablename];
                if (fieldDict.ContainsKey(fieldname))
                {
                    result = fieldDict[fieldname];
                }
            }
            return result;
 
        }
    }

 

Basically, I want the dynamic Enum to have a popup like the static Enum, instead of PropertyGrid rendering it as a string.

Any help will be deeply appreciated!

Thanks in advance!

3 Answers, 1 is accepted

Sort by
0
Martin Ivanov
Telerik team
answered on 12 Jun 2020, 10:26 AM

Hello John

This scenario is not supported by RadPropertyGrid. To achieve your requirement, you can define a custom EditorTemplate with a RadComboBox inside and bind it to a collection containing the values of the dynamic enum.

I hope this helps.

Regards,
Martin Ivanov
Progress Telerik

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.
0
John
Top achievements
Rank 1
Veteran
answered on 12 Jun 2020, 04:03 PM

Hello Martin,

First thank you very much for your response!  Appreciate it...

Well, I tried the Editor Template route, but ran into another roadblock with the way I did it.  For some unknown reason, when I try to apply my template against the PropertyDefinition, I get the following run-time exception:

A TwoWay or OneWayToSource binding cannot work on the read-only property 'IndexerProperty' of type 'System.Collections.ObjectModel.ObservableCollection`1[System.Object]'.

 

If you look at the code below, the XAML engine is apparently trying to access a property on the list of PropertyDefinition instead of the class I tried to make a source to for some unknown reason.

Can anyone either (a) tell me what is wrong with the code or (b) suggest an alternative to bind the dynamic Enum to the data template Combobox?  Thanks much in advance

Here are the modifications I made:

First the XAML:

<Grid x:Name="EditorGrid">
    <Grid.Resources>
        <DataTemplate x:Key="ComboBoxTemplate">
            <telerik:RadComboBox ItemsSource="{Binding Items, Mode=OneWay}" SelectedValue="{Binding Item}"/>
        </DataTemplate>
    </Grid.Resources>
    <telerik:RadPropertyGrid x:Name="EditorPropertyGrid" Margin="10,10,10,10" DescriptionPanelVisibility="Collapsed" SearchBoxVisibility="Collapsed" SortAndGroupButtonsVisibility="Collapsed" NestedPropertiesVisibility="Visible" AutoGeneratePropertyDefinitions="False" AutoGenerateBindingPaths="False" />
</Grid>

 

Next the modified model:

class EnumKeyValue : KeyValueBase
{
    public string Key { get; set; }
    public int Value { get; set; }
    public Type EnumType { get; set; }
    public List<string> Items
    {
        get
        {
            return Enum.GetNames(EnumType).ToList();
        }
    }
    public string Item
    {
        get
        {
            return Enum.GetName(EnumType, Value);
        }
        set
        {
            Value = int.Parse(value);
        }
    }
}
 
class ParentKeyValue : KeyValueBase
{
    public string Key { get; set; }
    public string Value { get; set; }
    public List<KeyValueBase> ChildList { get; set; }
}

 

And finally the code behind to generate the property grid items:

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    PropertyDefinitionCollection properties = EditorPropertyGrid.PropertyDefinitions;
 
    ObservableCollection<object> testList = new ObservableCollection<object>();
 
    TestEnumKeyValue Test1 = new TestEnumKeyValue() { Key = "Static Enum Prop", Value = TestEnum.test3 };
    testList.Add(Test1);
    properties.Add(new PropertyDefinition()
    {
        DisplayName = Test1.Key,
        Binding = new Binding("Value") { Source = Test1 }
    });
 
    DynamicEnumBuilder _enums = new DynamicEnumBuilder();
    _enums.BuildDictionaries();
    Type dynamicEnum = _enums.getEnum("Employee", "Status");
 
    EnumKeyValue enum1 = new EnumKeyValue() { Key = "Dynamic Enum Prop", Value = 2, EnumType = dynamicEnum };
    testList.Add(enum1);
    properties.Add(new PropertyDefinition()
    {
        DisplayName = enum1.Key,
        Binding = new Binding("Value") { Source = enum1 },
        EditorTemplate = EditorGrid.Resources["ComboBoxTemplate"] as DataTemplate
    });
 
    ParentKeyValue item = new ParentKeyValue() { ChildList = new List<KeyValueBase>() };
    testList.Add(item);
    PropertyDefinition parentPropertyDefinition = new PropertyDefinition()
    {
        DisplayName = item.Key,
        Binding = new Binding("Value") { Source = item }
    };
    properties.Add(parentPropertyDefinition);
    PropertyDefinitionCollection nestedProperties = parentPropertyDefinition.NestedProperties;
 
    TestEnumKeyValue Test1_1 = new TestEnumKeyValue() { Key = "Static Enum Prop", Value = TestEnum.test3 };
    testList.Add(Test1_1);
    nestedProperties.Add(new PropertyDefinition()
    {
        DisplayName = Test1_1.Key,
        Binding = new Binding("Value") { Source = Test1_1 }
    });
    EditorPropertyGrid.Item = testList;
 
    EnumKeyValue enum1_1 = new EnumKeyValue() { Key = "Dynamic Enum Prop", Value = 2, EnumType = dynamicEnum };
    testList.Add(enum1_1);
    nestedProperties.Add(new PropertyDefinition()
    {
        DisplayName = enum1_1.Key,
        Binding = new Binding("Value") { Source = enum1_1 }
    });
}

 

Any help will be deeply appreciated...

 

 

0
Martin Ivanov
Telerik team
answered on 17 Jun 2020, 11:14 AM

Hello John,

The error appears because the SelectedValue binding is trying to use the "Item" as an argument for the indexer of the ObservableCollection<object> which is the data context of the DataTemplate. Also, the binding to the ItemsSource won't work because it points to a property called Items which is not presented on the data context of the template.

To populate the RadComboBox, you can change the binding:

<DataTemplate x:Key="ComboBoxTemplate">
	<telerik:RadComboBox Background="Red" ItemsSource="{Binding [1].Items}" SelectedValue="{Binding [1].Item}" />
</DataTemplate>

Note that the context of the template is actually the value of the Item property of RadPropertyGrid.

I also prepared a small example based on your code and attached it here.

Regards,
Martin Ivanov
Progress Telerik

Progress is here for your business, like always. Read more about the measures we are taking to ensure business continuity and help fight the COVID-19 pandemic.
Our thoughts here at Progress are with those affected by the outbreak.
Tags
PropertyGrid
Asked by
John
Top achievements
Rank 1
Veteran
Answers by
Martin Ivanov
Telerik team
John
Top achievements
Rank 1
Veteran
Share this question
or