WPF / MVVM Load UserControl at Run Time

I know that there are many articles about my problem, but I can not find a solution. I am new to WPF - MVVM and I am trying to understand MVVM-Logic. So I did a little project to figure it out. For my later applications, I want to dynamically load UserControls into my window.

In my StartView, I have a binding to StartViewModel. (The binding is in APP.xaml)

StartView app = new StartView(); StartViewModel context = new StartViewModel(); 

Startview

 <Window x:Class="test.Views.StartView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views="clr-namespace:test.ViewModel" Title="Window1" Height="300" Width="516"> <Grid> <Menu IsMainMenu="True" Margin="0,0,404,239"> <MenuItem Header="_Einstellungen"> <MenuItem Header="Server" /> </MenuItem> </Menu> <ContentControl Content="{Binding LoadedControl}" Margin="0,28,0,128" /> </Grid> </Window> 

StartViewModel

 namespace test.ViewModel { public class StartViewModel : ViewModelBase { #region Fields private UCStastistikViewModel _loadedControl; #endregion public StartViewModel() { LoadedControl = new UCStastistikViewModel(); } #region Properties / Commands public UCStastistikViewModel LoadedControl { get { return _loadedControl; } set { if (value == _loadedControl) return; _loadedControl = value; OnPropertyChanged("LoadedControl"); } } #endregion #region Methods #endregion } } 

UCStatistikView

 <UserControl x:Class="test.Views.UCStatistik" 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:vm="clr-namespace:test.ViewModel" mc:Ignorable="d" d:DesignHeight="188" d:DesignWidth="508"> <UserControl.DataContext> <vm:UCStastistikViewModel /> </UserControl.DataContext> <Grid Background="red"> </Grid> </UserControl> 

UCStatistikViewModel

 namespace test.ViewModel { public class UCStastistikViewModel : ViewModelBase { #region Fields #endregion public UCStastistikViewModel() { } #region Properties / Commands #endregion #region Methods #endregion } } 

Now I want to load my UCStatistikView into the ContentControl of my StartView. But in Startview, only the Path test is shown .UCStatistikViewModel instead of all UC Can someone give me some ideas in which my problem / where I am wrong?

Bye j

+4
source share
4 answers

Your ViewModels should not care about UserControls. Instead, have ViewModels and let WPF decide how to draw the ViewModel with a DataTemplate.

For instance,

 <Window x:Class="test.Views.StartView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views="clr-namespace:test.Views" xmlns:viewmodels="clr-namespace:test.ViewModel" Title="Window1" Height="300" Width="516"> <Window.Resources> <DataTemplate DataType="{x:Type viewmodels:UCStastistikViewModel}"> <views:UCStastistikView /> </DataTemplate> </Window.Resources> <Grid> <Menu IsMainMenu="True" Margin="0,0,404,239"> <MenuItem Header="_Einstellungen"> <MenuItem Header="Server" /> </MenuItem> </Menu> <ContentControl Content="{Binding LoadedControl}" Margin="0,28,0,128" /> </Grid> </Window> 

Also get rid of <UserControl.DataContext> in UserControl. DataContext must be passed by any control that is not defined in UserControl :)

Edit

Based on your comment on an earlier answer about disabling StartPage content by switching ViewModel, you might be interested to see this post of mine . It is called Navigation with MVVM , but the same concept is used to switch views or UserControls.

Basically, you will create a LoadedControl property of type ViewModelBase instead of hard coding it, and then set it for any object that you want to display in ContentControl. WPF DataTemplates will take care of connecting the correct view for the ViewModel.

+16
source

WPF does not support automatic presentation resolution for this presentation model. The naive solution to your problem was to directly add a UCStatistikView to your StartView and associate a VM with it.

 <Window x:Class="test.Views.StartView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views="clr-namespace:test.ViewModel" Title="Window1" Height="300" Width="516"> <Grid> <Menu IsMainMenu="True" Margin="0,0,404,239"> <MenuItem Header="_Einstellungen"> <MenuItem Header="Server" /> </MenuItem> </Menu> <UCStatistikView DataContext="{Binding LoadedControl}" Margin="0,28,0,128" /> </Grid> 

A more complex approach would be to implement ViewLocator (the first approach to the view model) or ViewModelLocator (view the first approach). The locator automatically finds and binds the view to the view model. There are some MVVM frameworks / tools that implement such a locator.

  • Caliburn.Micro : Offers flexible ViewLocator and ViewModelLocator based on naming conventions. Here is an article about them
  • MVVM Light : offers ViewModelLocator. Here is an introduction

With Caliburn.Micro, you’ll start to see how it looks.

 <Window x:Class="test.Views.StartView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views="clr-namespace:test.ViewModel" Title="Window1" Height="300" Width="516"> <Grid> <Menu IsMainMenu="True" Margin="0,0,404,239"> <MenuItem Header="_Einstellungen"> <MenuItem Header="Server" /> </MenuItem> </Menu> <ContentControl cm:View.Model="{Binding LoadedControl}" Margin="0,28,0,128" /> </Grid> 

The cm:View.Model="{Binding LoadedControl}" attached property tells caliburn to find the view for the view model that is bound and set the Content ContentControl property for it.

+2
source

Here is what you should do:

StartView:

 <Window x:Class="test.Views.StartView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:views="clr-namespace:test.ViewModel" Title="Window1" Height="300" Width="516"> <Grid> <Menu IsMainMenu="True" Margin="0,0,404,239"> <MenuItem Header="_Einstellungen"> <MenuItem Header="Server" /> </MenuItem> </Menu> <UCStatistikView x:Name="myUCStatistikView" Margin="0,28,0,128" /> </Grid> </Window> 

StartViewModel:

 namespace test.ViewModel { public class StartViewModel : ViewModelBase { private UCStastistikViewModel _myControlViewModel; public StartViewModel() { _myControlViewModel = new UCStastistikViewModel(); } public UCStastistikViewModel MyControlViewModel { get { return _myControlViewModel; } set { if (value == _myControlViewModel) return; _myControlViewModel = value; OnPropertyChanged("MyControlViewModel"); } } } } 

UCStatistikView:

 <UserControl x:Class="test.Views.UCStatistik" 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:vm="clr-namespace:test.ViewModel" mc:Ignorable="d" d:DesignHeight="188" d:DesignWidth="508"> <Grid Background="red"> </Grid> </UserControl> 

Code behind your StartView:

 this.myUCStatistikView.DataContext = ((StartViewModel)this.DataContext).MyControlViewModel; 

After testing the various approaches, my conclusion is that the best way to bind the datacontext is to code the parent view in case you have userControls.

EDIT: ViewModel locators are great for simple examples, but if your ViewModel needs to be dynamically created (mainly when its constructor requires parameters), you cannot use it. Because of this, I personally stopped using locators.

0
source

Overview: - http://patelrocky1608.wordpress.com/2013/12/26/how-to-add-custom-control-dynamically-in-wpf/

Which contain the complete code for dynamic control of UserControl ..

-1
source

Source: https://habr.com/ru/post/1388571/


All Articles