I am very new to the MVVM concept and try to set up a WPF application which controls laboratory equipment remotely. After setting up the following views and view models:
App.xaml
<Application x:Class="VirtualLab.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VirtualLab"
xmlns:viewModel="clr-namespace:VirtualLab.MVVM.ViewModel"
xmlns:views="clr-namespace:VirtualLab.MVVM.Views"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Theme/MenuButtonTheme.xaml"/>
<ResourceDictionary Source="Theme/FuncGenButtonStyles.xaml"/>
<ResourceDictionary Source="Theme/OsziButtonTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
<DataTemplate DataType="{x:Type viewModel:CircuitViewModel}">
<views:CircuitView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:OszilloskopViewModel}">
<views:OszilloskopView/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:FunctionGeneratorViewModel}">
<views:FunctionGeneratorView/>
</DataTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
MainViewModel.cs
using VirtualLab.Core;
using VirtualLab.MVVM.Model;
using VirtualLab.MVVM.ViewModel;
namespace VirtualLab.MVVM.ViewModel
{
public class MainViewModel : ObservableObject
{
public SafetyCheck safetyCheck = new SafetyCheck();
// Commands for Relays
public RelayCommands CircuitViewCommand { get; set; }
public RelayCommands OszilloskopViewCommand { get; set; }
public RelayCommands FunctionGeneratorCommand { get; set; }
// All View Models
public CircuitViewModel CircuitVM { get; set; }
public OszilloskopViewModel OszilloskopVM { get; set; }
public FunctionGeneratorViewModel FunctionGeneratorVM { get; set; }
// Current View
public object _currentView;
public object CurrentView
{
get { return _currentView; }
set
{
_currentView = value;
OnPropertyChanged();
}
}
CircuitViewModel GetCircuitVM()
{
return this.CircuitVM;
}
// Constructor MainViewModel
public MainViewModel()
{
CircuitVM = new CircuitViewModel();
OszilloskopVM = new OszilloskopViewModel(this);
FunctionGeneratorVM = new FunctionGeneratorViewModel(this);
CurrentView = CircuitVM;
CircuitViewCommand = new RelayCommands(o =>
{
CurrentView = CircuitVM;
});
OszilloskopViewCommand = new RelayCommands(o =>
{
CurrentView = OszilloskopVM;
});
FunctionGeneratorCommand = new RelayCommands(o =>
{
CurrentView = FunctionGeneratorVM;
});
}
}
}
MainWindow.xaml.cs
using System;
using System.Windows;
using System.Windows.Input;
using VirtualLab.MVVM.Views;
namespace VirtualLab
{
/// <summary>
/// Interaktionslogik für MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void minimizeApp(object sender, RoutedEventArgs e)
{
try
{
this.WindowState = WindowState.Minimized;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void closeApp(object sender, RoutedEventArgs e)
{
try
{
Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private void Border_MouseDown(object sender, MouseButtonEventArgs e)
{
if(e.LeftButton == MouseButtonState.Pressed )
{
DragMove();
}
}
}
}
FunctionGeneratorViewModel.cs
namespace VirtualLab.MVVM.ViewModel
{
public class FunctionGeneratorViewModel : ObservableObject
{
public Functiongenerator funcGen; // the actual model that contains the functions for the functionGenerator
public MainViewModel MainVM;
//A lot of RelayCommands to access the functions for funcGen are implemented here......
public FunctionGeneratorViewModel(MainViewModel MainVM)
{
//Objectdefinition
this.funcGen = new Functiongenerator(MainVM);
}
}
FunctionGeneratorView.cs
<UserControl x:Class="VirtualLab.MVVM.Views.FunctionGeneratorView"
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:VirtualLab.MVVM.Views"
mc:Ignorable="d"
d:DesignHeight="790" d:DesignWidth="1400">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<TextBlock Text="Funktionsgenerator"
Foreground="White"
FontSize="25"
HorizontalAlignment="Left"
Margin="20,20,0,20"
FontFamily="/Fonts/#Poppins"/>
<!--a lot of Code for all elements in the view -->
</Grid>
</UserControl>
I encountered the following problem.... every time I navigate between the views, it seems as if the new page is reset, or to put it in other words, the page change causes a reset of the "old" view page.
How can I avoid this behaviour?
Hopefully the given code sufficient information. Otherwise I can provide further code excerpts.
Thanks in advance!:)
In the simplest case, you need a similar converter:
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace Core2022.Converters
{
public class ObjectToUIElementConverter : IValueConverter
{
public object? Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is null)
return DependencyProperty.UnsetValue;
if (!uiElements.TryGetValue(value, out var element))
{
if (value is UIElement elm)
{
element = elm;
}
else
{
element = new ContentControl()
{ DataContext = value, Content = value };
}
uiElements.Add(value, element);
}
return element;
}
private readonly ConditionalWeakTable<object, UIElement> uiElements
= new ConditionalWeakTable<object, UIElement>();
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
public static ObjectToUIElementConverter Default { get; } = new ObjectToUIElementConverter();
}
}
Use it in the CurrentView property binding:
<ContentControl Content="{Binding CurrentView, Converter={x:Static vms:ObjectToUIElementConverter.Default}}"/>
But I immediately warn you, such a converter is not suitable for all scenarios, it may have memory leaks (when VMs are created dynamically and there are a lot of them), in some cases it is possible to reinitialize remembered UIElements and other problems.