Adding dynamic properties to a class that is bound to UI in WPF app via DataTemplate (dynamic properties that are specific to an instance)

WPF-Learner 106 Reputation points
2021-05-22T12:59:52.047+00:00

I have a WPF application. I have A Person Class that looks as following:

 public class Person  
{  
    public Person(string id, string name, int age)  
    {  
        Id = id;  
        Name = name;  
        Age = age;  
    }  

    public string Id { set; get; }  

    public string Name { set; get; }  

    public int Age { set; get; }  


}  

In my view model, I have an

public ObservableCollection<Person> People { get; set; }  

And my View looks as following:

    <Grid>  
    <ItemsControl x:Name="DynamicPeople" ItemsSource="{Binding Path=People}">  
        <ItemsControl.ItemTemplate>  
            <DataTemplate>  
                <StackPanel Background="Gray" Margin="6">  
                    <StackPanel Orientation="Horizontal">  
                        <Label Content="Id"/>  
                        <Label Content="{Binding Path=Id}"/>  
                    </StackPanel>  
                    <StackPanel Orientation="Horizontal">  
                        <Label Content="Name"/>  
                        <Label Content="{Binding Path=Name}"/>  
                    </StackPanel>  
                    <StackPanel Orientation="Horizontal">  
                        <Label Content="Age"/>  
                        <Label Content="{Binding Path=Age}"/>  
                    </StackPanel>  
                    <Button Width="120" Height="60">Add New Property</Button>  
                </StackPanel>  
            </DataTemplate>  
        </ItemsControl.ItemTemplate>  
    </ItemsControl>  
</Grid>  

</Window>

When running the app, we get:

98814-capture8.png

Now, the requirement is that upon clicking on the Add New Property button, a pop up will open, and there the user will fill in the Property name, Property type and Property Value. Once the user clicks "Apply" at the popup, we are back to the shown view, and the new property should be added to data template. For example, if the user has filled in at the popup for Adam:

Property name: occupation

Property type: string

Property value: "Teacher"

Then, upon clicking on the Apply button at the popup, we will get:

98815-capture5.png

Note that it's like a property of:

public string Occupation { get; set; }  

was added to the Person class, but only to the first instance of Adam.

I have a few ideas, but what is the best approach for implementing something like this?

I think about declaring a model class called DynamicProperty:

    public class DynamicProperty  
{  
    public string Name { set; get; }  

    public Type Type { set; get; }  

    public object Value  { set; get; }  
}  

Then, I would add to the person class:

public ObservableCollection<DynamicProperty> DynamicProperties { get; set; }  

This would represent the dynamic properties that as I've mentioned that are specific to an instance of class person.

Then I'm not sure how to edit the data Template to display the newly added properties in a similar way to the existing one (like Name & Age).

I don't like something about my approach, maybe you have any better ideas? Thank you!

Developer technologies Windows Presentation Foundation
Developer technologies C#
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,341 Reputation points
    2021-05-22T19:51:58.473+00:00

    Hi,
    you can add a list of DynamicProperty to yor Person class and show items like in following demo:

    XAML:

    <Window x:Class="WpfApp1.Window065"  
            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:WpfApp065"  
            mc:Ignorable="d"  
            Title="Demo 15709958 Windows-WPF" Height="450" Width="800">  
      <Window.Resources>  
        <local:ViewModel x:Key="vm"/>  
      </Window.Resources>  
      <Grid DataContext="{StaticResource vm}">  
        <ItemsControl x:Name="DynamicPeople" ItemsSource="{Binding Path=People}">  
          <ItemsControl.ItemTemplate>  
            <DataTemplate>  
              <StackPanel Background="Gray" Margin="6">  
                <StackPanel Orientation="Horizontal">  
                  <Label Content="Id"/>  
                  <Label Content="{Binding Path=Id}"/>  
                </StackPanel>  
                <StackPanel Orientation="Horizontal">  
                  <Label Content="Name"/>  
                  <Label Content="{Binding Path=Name}"/>  
                </StackPanel>  
                <StackPanel Orientation="Horizontal">  
                  <Label Content="Age"/>  
                  <Label Content="{Binding Path=Age}"/>  
                </StackPanel>  
                <ItemsControl ItemsSource="{Binding DynamicProperties}">  
                  <ItemsControl.ItemTemplate>  
                    <DataTemplate>  
                      <StackPanel Background="Gray" Orientation="Horizontal">  
                          <Label Content="{Binding Name}"/>  
                          <Label Content="{Binding Value}"/>  
                        </StackPanel>  
                      </DataTemplate>  
                    </ItemsControl.ItemTemplate>  
                </ItemsControl>  
                <Button Width="120" Height="60"  
                        Command="{Binding Source={StaticResource vm}}"  
                        CommandParameter="{Binding}">Add New Property</Button>  
              </StackPanel>  
            </DataTemplate>  
          </ItemsControl.ItemTemplate>  
        </ItemsControl>  
      </Grid>  
    </Window>  
    

    And code:

    using System;  
    using System.Collections.ObjectModel;  
    using System.ComponentModel;  
    using System.Windows;  
    using System.Windows.Data;  
    using System.Windows.Input;  
      
    namespace WpfApp065  
    {  
      public class ViewModel : ICommand  
      {  
        public ViewModel()  
        {  
          ObservableCollection<Person> col = new ObservableCollection<Person>();  
          for (int i = 1; i < 10; i++) col.Add(new Person() { Id = i, Name = $"Name {i}", Age = 20 + i * 3 });  
          cvs.Source = col;  
        }  
        CollectionViewSource cvs = new CollectionViewSource();  
        public ICollectionView People { get => cvs.View; }  
      
        int nr;  
        public void Execute(object parameter)  
        {  
          Person p = parameter as Person;  
          p.DynamicProperties.Add(new DynamicProperty() { Name = $"Prop {++nr}", Type = typeof(string), Value = nr + 10 });  
        }  
        public event EventHandler CanExecuteChanged;  
        public bool CanExecute(object parameter) => true;  
      }  
      
      public class Person  
      {  
        public int Id { set; get; }  
        public string Name { set; get; }  
        public int Age { set; get; }  
        public ObservableCollection<DynamicProperty> DynamicProperties { get; } = new ObservableCollection<DynamicProperty>();  
      }  
      public class DynamicProperty  
      {  
        public string Name { set; get; }  
        public Type Type { set; get; }  
        public object Value { set; get; }  
      }  
    }  
    

    Result:

    98872-x.gif

    1 person found this answer helpful.
    0 comments No comments

0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.