generic type in xaml

essamce 621 Reputation points
2021-03-27T15:30:20.897+00:00

Hi

<DataTemplate DataType="{x:Type vm:MyViewModel1}">
                <StackPanel DataContext="{Binding}">
                    <!--template-->
                </StackPanel>
            </DataTemplate>

the above code works fine except the case of generic class
examples:
MyViewModel2<T>
MyViewModel3<TAcad, TCivil>

i'm using .Net Framework 4.7 Wpf ClassLibrary project
any help will be appreciated, thanks in advance.

Windows Presentation Foundation
Windows Presentation Foundation
A part of the .NET Framework that provides a unified programming model for building line-of-business desktop applications on Windows.
2,706 questions
XAML
XAML
A language based on Extensible Markup Language (XML) that enables developers to specify a hierarchy of objects with a set of properties and logic.
786 questions
0 comments No comments
{count} votes

Accepted answer
  1. Peter Fleischer (former MVP) 19,306 Reputation points
    2021-03-28T05:34:15.353+00:00

    Hi,
    the easiest way is to encapsulate the generic class. Try following demo:

    XAML MainWindow:

    Window x:Class="WpfApp1.Window034"
            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:WpfApp034"
            xmlns:view="clr-namespace:WpfControlLibrary1;assembly=WpfControlLibrary1"
            mc:Ignorable="d"
            Title="MainWindow" Height="450" Width="800">
      <Window.DataContext>
        <local:ViewModel/>
      </Window.DataContext>
      <StackPanel>
        <Button Content="Switch" Command="{Binding}" Width="200" Margin="5"/>
        <view:Window034UC1 DataContext="{Binding CurrentVM}"/>
      </StackPanel>
    </Window>
    

    ViewModel:

    using System;
    using System.ComponentModel;
    using System.Runtime.CompilerServices;
    using System.Windows;
    using System.Windows.Input;
    
    namespace WpfApp034
    {
      public class ViewModel : ICommand, INotifyPropertyChanged
      {
        public object CurrentVM { get; set; } = new WpfControlLibrary034.MyViewModel1();
    
        public void Execute(object parameter)
        {
          if (CurrentVM is WpfControlLibrary034.MyViewModel2)
          {
            WpfControlLibrary034.MyViewModel1 vm = new WpfControlLibrary034.MyViewModel1();
            vm.Data.Info1 = DateTime.Now.ToString("mm:ss.fff");
            CurrentVM = vm;
          }
          else
          {
            WpfControlLibrary034.MyViewModel2 vm = new WpfControlLibrary034.MyViewModel2();
            vm.Data.Info2 = DateTime.Now.ToString("mm:ss.fff");
            CurrentVM = vm;
          }
          OnPropertyChanged(nameof(CurrentVM));
        }
    
        public event EventHandler CanExecuteChanged;
        public bool CanExecute(object parameter) => true;
    
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged([CallerMemberName] string propName = "") =>
          PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
      }
    }
    

    UserControl with DataTemplates

    <UserControl x:Class="WpfControlLibrary1.Window034UC1"
                 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 xmlns:local="clr-namespace:WpfControlLibrary1"
                 xmlns:view="clr-namespace:WpfControlLibrary034"
                 xmlns:vm="clr-namespace:WpfControlLibrary034"
                 mc:Ignorable="d" 
                 d:DesignHeight="450" d:DesignWidth="800">
      <UserControl.Resources>
        <DataTemplate DataType="{x:Type vm:MyViewModel1}">
          <view:MyViewModel1View DataContext="{Binding}"/>
        </DataTemplate>
        <DataTemplate DataType="{x:Type vm:MyViewModel2}">
          <view:MyViewModel2View DataContext="{Binding}"/>
        </DataTemplate>
      </UserControl.Resources>
      <ContentControl Content="{Binding}"/>
    </UserControl>
    

    And classes:

    using System;
    using System.Windows.Controls;
    using System.Windows.Data;
    
    namespace WpfControlLibrary034
    {
      public class MyViewModel1 : MyViewModel<ModelData1> { }
      public class MyViewModel2 : MyViewModel<ModelData2> { }
    
      public class MyViewModel<T>
      {
        private T d;
        public T Data
        {
          get
          {
            if (d == null) d = (T)Activator.CreateInstance(typeof(T));
            return d;
          }
        }
      }
      public class ModelData1 { public string Info1 { get; set; } }
      public class ModelData2 { public string Info2 { get; set; } }
      public class MyViewModel1View : UserControl
      {
        public MyViewModel1View()
        {
          StackPanel stp = new StackPanel();
          this.Content = stp;
          stp.Children.Add(new Label() { Content = "1. View" });
          Label lbl = new Label();
          lbl.SetBinding(Label.ContentProperty, new Binding("Data.Info1"));
          stp.Children.Add(lbl);
        }
      }
      public class MyViewModel2View : UserControl
      {
        public MyViewModel2View()
        {
          StackPanel stp = new StackPanel();
          this.Content = stp;
          stp.Children.Add(new Label() { Content = "2. View" });
          Label lbl = new Label();
          lbl.SetBinding(Label.ContentProperty, new Binding("Data.Info2"));
          stp.Children.Add(lbl);
        }
      }
    }
    
    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful