MVVM Light - user controls in the form of views

I decided to use the MVVM Light library to develop the user interface. After subtle research and testing and error, I have not yet found the answers I am looking for. I googled and read every StackOverflow question I can find, however my problem seems unique on SO.

I want to create a single window user interface and populate it with various views / UserControls. I do NOT want to use a common navigation bar among UserControls, nor do I want multiple windows to appear. Each View / UserControl must bind to its own ViewModel, while the MainWindow will be bound to the MainViewModel.

Sample Script - MainWindow with 3 UserControls

1. MainWindow populates with first UserControl which has a listbox and 3 buttons, the first button is enabled. 2. User clicks the first button. 3. MainWindow populates with second UserControl. 

Or, in addition,

  2. User selects choice from a listbox, button two and three become available. 3. User clicks second/third button. 4. MainWindow populates with second/third UserControl. 

Etc etc.

Perhaps my approach is unrealistic, but I believe that this should be possible. I do not understand how to do all these parts conceptually. I do not want my desires to be unique. If you think this is a recurring question, redirect it. Greetings.


To simplify understanding, I have included some classes below. Firstly, my App.xaml.

 <Application x:Class="Bobcat_BETA.App" 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:views="clr-namespace:Bobcat_BETA.UserControls" xmlns:vm="clr-namespace:Bobcat_BETA.ViewModels" StartupUri="MainWindow.xaml" mc:Ignorable="d"> <Application.Resources> <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" /> <DataTemplate DataType="{x:Type vm:SavedScenariosViewModel}"> <views:SavedScenariosUserControl /> </DataTemplate> <DataTemplate DataType="{x:Type vm:ScenarioEditorViewModel}"> <views:ScenarioEditorUserControl /> </DataTemplate> <DataTemplate DataType="{x:Type vm:SimulatorViewModel}"> <views:SimulatorUserControl /> </DataTemplate> </Application.Resources> </Application> 

MainWindow.xaml

 <Window x:Class="Bobcat_BETA.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Bobcat - Version:0.00" DataContext="{Binding Main, Source={StaticResource Locator}}"> <Grid> <ContentControl Content="{Binding CurrentView}"/> </Grid> 

ViewModelLocator.cs

 namespace Bobcat_BETA.ViewModels { public class ViewModelLocator { private static MainViewModel _main; public ViewModelLocator() { _main = new MainViewModel(); } [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "This non-static member is needed for data binding purposes.")] public MainViewModel Main { get { return _main; } } } } 

MainViewModel.cs

 namespace Bobcat_BETA.ViewModels { public class MainViewModel : ViewModelBase { private ViewModelBase _currentViewModel; readonly static SavedScenariosViewModel _savedScenarioViewModel = new SavedScenariosViewModel(); readonly static ScenarioEditorViewModel _scenarioEditorViewModel = new ScenarioEditorViewModel(); readonly static SimulatorViewModel _simulatorViewModel = new SimulatorViewModel(); public ViewModelBase CurrentViewModel { get { return _currentViewModel; } set { if (_currentViewModel == value) return; _currentViewModel = value; RaisePropertyChanged("CurrentViewModel"); } } public MainViewModel() { CurrentViewModel = MainViewModel._savedScenarioViewModel; SavedScenarioViewCommand = new RelayCommand(() => ExecuteSavedScenarioViewCommand()); ScenarioEditorViewCommand = new RelayCommand(() => ExecuteScenarioEidtorViewCommand()); SimulatorViewCommand = new RelayCommand(() => ExecuteSimulatorViewCommand()); } public ICommand SavedScenarioViewCommand { get; private set; } public ICommand ScenarioEditorViewCommand { get; private set; } public ICommand SimulatorViewCommand { get; private set; } private void ExecuteSavedScenarioViewCommand() { CurrentViewModel = MainViewModel._savedScenarioViewModel; } private void ExecuteScenarioEidtorViewCommand() { CurrentViewModel = MainViewModel._scenarioEditorViewModel; } private void ExecuteSimulatorViewCommand() { CurrentViewModel = MainViewModel._simulatorViewModel; } } } 

SavedScenariosViewModel.cs

 namespace Bobcat_BETA.ViewModels { public class SavedScenariosViewModel : ViewModelBase { public SavedScenariosViewModel() { } ObservableCollection<ScenarioModel> _scenarioModels = new ObservableCollection<ScenarioModel>() { new ScenarioModel() {Name = "Scenario 0", ID = 000, Desc = "This will describe the Scenario Model."}, new ScenarioModel() {Name = "Scenario 1", ID = 001, Desc = "This will describe the Scenario Model."}, new ScenarioModel() {Name = "Scenario 2", ID = 002, Desc = "This will describe the Scenario Model."}, new ScenarioModel() {Name = "Scenario 3", ID = 003, Desc = "This will describe the Scenario Model."}, new ScenarioModel() {Name = "Scenario 4", ID = 004, Desc = "This will describe the Scenario Model."}, new ScenarioModel() {Name = "Scenario 5", ID = 005, Desc = "This will describe the Scenario Model."}, new ScenarioModel() {Name = "Scenario 6", ID = 006, Desc = "This will describe the Scenario Model."}, new ScenarioModel() {Name = "Scenario 7", ID = 007, Desc = "This will describe the Scenario Model."}, new ScenarioModel() {Name = "Scenario 8", ID = 008, Desc = "This will describe the Scenario Model."}, new ScenarioModel() {Name = "Scenario 9", ID = 009, Desc = "This will describe the Scenario Model."} }; public ObservableCollection<ScenarioModel> ScenarioModels { get { return _scenarioModels; } } } } 

SavedScenariosUserControl.xaml

 <UserControl x:Class="Bobcat_BETA.UserControls.SavedScenariosUserControl" 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:Bobcat_BETA.ViewModels" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <UserControl.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="/Dictionaries/MasterDictionary.xaml" /> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </UserControl.Resources> <UserControl.Style> <DynamicResource ResourceKey="GeneralUserControl"/> </UserControl.Style> <Grid> <Label Content="Saved Scenario Selection" Style="{StaticResource GeneralLabel}" HorizontalAlignment="Left" Margin="26,30,0,0" VerticalAlignment="Top" Height="62" Width="345"/> <Label Content="Chose Flight Model:" Style="{StaticResource GeneralLabel2}" HorizontalAlignment="Left" Margin="27,111,0,0" VerticalAlignment="Top" Height="43" Width="345"/> <ListBox Style="{StaticResource GeneralListBox}" HorizontalAlignment="Left" Height="509" Margin="27,154,0,0" VerticalAlignment="Top" Width="345" ItemsSource="{Binding ScenarioModels}"/> <Button Content="New" Style="{StaticResource TransitionButton}" HorizontalAlignment="Left" Margin="948,601,0,0" VerticalAlignment="Top" MinHeight="62" MinWidth="150" IsEnabled="True" Command="{Binding ScenarioEditorViewCommand}"/> <Button Content="Edit" Style="{StaticResource TransitionButton}" HorizontalAlignment="Left" Margin="401,519,0,0" VerticalAlignment="Top" MinHeight="62" MinWidth="150" Command="{Binding SaveScenariosViewCommand}"/> <Button Content="Load" Style="{StaticResource TransitionButton}" HorizontalAlignment="Left" Margin="401,601,0,0" VerticalAlignment="Top" MinHeight="62" MinWidth="150" Command="{Binding SimulatorViewCommand}"/> </Grid> </UserControl> 

If something is unclear, I can add model classes, but I assume that you can draw conclusions from what is happening. Thanks.

+5
source share
2 answers

So your approach is very believable. To get this functionality, you will need some subtleties. I had to use "MainViewModel", which contained all view models. These presentation models behave so that when the data context switches to another presentation model, the corresponding user control will be changed to the corresponding presentation. A good example that I followed was answered by Sheridan here . The hook in your app.xaml with the appropriate data patterns and data context switches will be treated like magic: D

Where I deviated from Sheridan’s example (because I didn’t want to create a separate class / object of the relay command), I actually used mvvm light (galasoft) to send messages from my view models to a message back to “MainViewModel” before switching your data context. A good example of using MVVM lightweight messaging can be found here. Send a message from the "child" view model and register it in the "MainViewModel".

Good luck

+3
source

Can you use the interface for the IChildLayout example? Each ViewModel inherits this interface ...

 public interface IChildLayout:INotifyPropertyChanged { public MainWindows_ViewModel Parent; } 

In your MainWindows ViewModel, you can have something like this ...

The IChildLayout property, which changes when you click on your buttons ...

 public class MainWindows_ViewModel:INotifyPropertyChanged { public MainWindows_ViewModel() { //Here i set the default ViewModel this.Child=new First_ViewModel(){Parent=this}; } private IChildLayout _child; public IChildLayout Child { get { return _child; } set { _child=value; _child.Parent=this; NotifyPropertyChanged("Child"); } } #region INotifyPropertyChangedMembers... } 

For each layout, you can get the parent ViewModel window (it is important to switch the layout by editing the "Child" property from your own ViewModel ...)

You put the UserControl inside your main windows (in xaml), the content is bound to the Child property, after which it will be updated every time your child property changes.

 <Windows> <Windows.DataContext> <local:MainWindows_ViewModel/> </Windows.DataContext> <UserControl Content={Binding Child}> <UserControl.Resources> <DataTemplate DataType="{x:Type ViewModels:First_ViewModel}"> <Controls:First_View DataContext="{Binding}"/> </DataTemplate> <DataTemplate DataType="{x:Type ViewModels:Second_ViewModel}"> <Controls:Second_View DataContext="{Binding}" /> </DataTemplate> </UserControl.Resources> </UserControl> </Windows> 

In this case, your First_ViewModel might be: (In this example, I use the DelegateCommand prism to bind button actions ...

 public class First_ViewModel:IChildLayout { public MainWindows_ViewModel Parent {get;set;} public ICommand cmdBtn1click{get;set;} private Pass_to_second_ViewModel() { //Here i change the Parent Child Property, it will switch to Second_View.xaml... this.Parent.Child=new Second_ViewModel(); } public First_ViewModel() { // Here i connect the button to the command with Prism... this.cmdBtn1click=new DelegateCommand(()=>Pass_to_second_ViewModel()); } #region INotifyPropertyChangedMembers... 

}

Hope this helps you, I did this to manage a different tab in a WPF application.

+1
source

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


All Articles