I have the following xaml: What is needed that the following xaml must be filled dynamic at runtime, but how? The MainWorkspaceViewModel has a property named "View". This property is of type object, so I can set every view in it.
<UserControl x:Class="DesignerWorkspace.Views.MainWorkspaceView"
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:DesignerWorkspace.Views"
xmlns:vm="clr-namespace:DesignerWorkspace.ViewModels"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<ContentControl Content="{Binding View}"/>
</Grid>
</UserControl>
The minimum would be to add a context that has the view and updates on property changed.
From somewhere else in the code you can then manage the displayed view by setting the new view.
Here is a simplistic implementation with missing checks you may want to add.
class MainWorkspaceViewModel : INotifyPropertyChanged
{
private object _view;
public object View {
get { return _view; }
set { _view = value; OnPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
internal class MyViewManager
{
internal static MainWorkspaceView MakeMainView()
{
var view = new MainWorkspaceView();
view.DataContext = new MainWorkspaceViewModel();
return view;
}
internal static void UpdateView(MainWorkspaceViewModel viewmodel, object _next)
{
viewmodel.View = _next;
}
internal static void UpdateView(MainWorkspaceView view, object _next)
{
(view.DataContext as MainWorkspaceViewModel).View = _next;
}
}
There are countless ways to do this.
First you have to identify if you are using any toolkit that does this for you.
After identifying just check how this toolkit was set up and how it is used.
There is a very simple way to do this without any toolkit and the one I am going to show you so that you understand how it works.
note that my example is how to create from a new one, it may not be your case, but it's just to understand (feel free to create a new one and see how it works)
first create a ViewModelBase that implements INotifyPropertyChanged, preferably make this abstract class
ViewModelBase class
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public virtual ICommand NavigateCommand => new RelayCommand(Navigate);
protected virtual void Navigate(object param)
{
}
}
then create your MainViewModel, have it inherit the ViewModelBase, and create a property for it to be assigned its ViewModels
public class MainViewModel: ViewModelBase
{
private ViewModelBase _currentViewModel;
public ViewModelBase CurrentViewModel
{
get => _currentViewModel;
set
{
if (_currentViewModel == value)
return;
_currentViewModel = value;
OnPropertyChanged(nameof(CurrentViewModel));
}
}
public MainViewModel()
{
}
protected override void Navigate(object args)
{
var namespaceName = "YourNameSpace.";
var className = args.ToString();
var fullClassName = string.Concat(namespaceName, (string)className);
if (string.IsNullOrEmpty(fullClassName))
return;
var tipo = Type.GetType(fullClassName);
if (tipo == null)
return;
var myObj = Activator.CreateInstance(tipo) as ViewModelBase;
if (myObj != null)
CurrentViewModel = (ViewModelBase)myObj;
}
}
you should note that the MainViewModel
has an overridden method of the ViewModelBase
(Navigate), it is this method that will be used to open other ViewModels from a simple parameter, a string with the name of the ViewModel that wants to open
now you add to your MainWindow
your content, in my example, a menu and the content that will be populated with the ViewModels
<Window x:Class="YourNameSpace.MainWindow"
.......
Title="MainWindow"
DataContext="{StaticResource MainViewModel}" >
<Grid>
.....
<!--In the Menu, how to open a View -->
<MenuItem Header="Open ViewA" Command="{Binding NavigateCommand}" CommandParameter="ViewModelA" />
.......
<!-- Your Content Control -->
<ContentControl
HorizontalAlignment="Center"
VerticalAlignment="Top"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Top"
ClipToBounds="True"
Content="{Binding CurrentViewModel}" >
</ContentControl>
.....
</Window>
Create your views.
Example:
<UserControl x:Class="YourNameSpace.ucViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
>
<Grid Background="White">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="this is the ViewA" Margin="5" />
<Button Grid.Column="2" Content="Open View B"
Command="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=OneWay}"
CommandParameter="ViewModelB" Margin="5" />
</Grid>
<DataGrid Grid.Row="2" ColumnWidth="*" Margin="5"
IsReadOnly="True"
AutoGenerateColumns="False"
SelectionMode="Single"
HorizontalContentAlignment="Center"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
ScrollViewer.HorizontalScrollBarVisibility="Auto">
<DataGrid.Columns>
<DataGridTextColumn Header="Column 1" MinWidth="50" Width="Auto"/>
<DataGridTextColumn Header="Column 2" MinWidth="140" Width="Auto"/>
<DataGridTextColumn Header="Column 3" MinWidth="240" Width="*"/>
<DataGridTextColumn Header="Column 4" MinWidth="70" Width="Auto"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>
Now in the app.xaml file let's say which ViewModel belongs to which UserControl
<Application x:Class="YourNameSpace.App"
...
xmlns:local="clr-namespace:YourNameSpace"
StartupUri="MainWindow.xaml">
<Application.Resources>
<local:MainViewModel x:Key="MainViewModel" />
<DataTemplate DataType="{x:Type local:ViewModelA}">
<local:ucViewA />
</DataTemplate>
<DataTemplate DataType="{x:Type local:ViewModelB}">
<local:ucViewB />
</DataTemplate>
</Application.Resources>
</Application>
to open a View from another view use the following:
<Button Content="Open View A"
Command="{Binding DataContext.NavigateCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=OneWay}"
CommandParameter="ViewModelA" Margin="5" />
Edit: I forgot to say, your ViewModel must have at least one constructor (empty) so no problem occurs