C
C#2y ago
FaNim

✅ How can i save Ui content when switching between views in MVVM wpf

when i switching between views everytime what i my textboxes are cleared only listview items are saved but i have main list in MainWindow what can i do?
48 Replies
ACiDCA7
ACiDCA72y ago
show code but i guess you either overwrite the data when you switch back to the view or recreate the datacontext
FaNim
FaNimOP2y ago
public class MainViewModel : BaseViewModel
{
public MainWindow MainWindow { get; set; }

public SettingsViewModel Settings { get; } = new SettingsViewModel();
public StartViewModel Start { get; } = new StartViewModel();

private BaseViewModel? _selectedViewModel;
public BaseViewModel? SelectedViewModel
{
get { return _selectedViewModel; }
set
{
_selectedViewModel = value;
OnPropertyChanged(nameof(SelectedViewModel));
}
}

public ICommand UpdateViewCommand { get; set; }

public MainViewModel(MainWindow mainWindow)
{
MainWindow = mainWindow;

UpdateViewCommand = new UpdateViewCommand(this);
UpdateViewCommand.Execute("Start");
}
}
public class MainViewModel : BaseViewModel
{
public MainWindow MainWindow { get; set; }

public SettingsViewModel Settings { get; } = new SettingsViewModel();
public StartViewModel Start { get; } = new StartViewModel();

private BaseViewModel? _selectedViewModel;
public BaseViewModel? SelectedViewModel
{
get { return _selectedViewModel; }
set
{
_selectedViewModel = value;
OnPropertyChanged(nameof(SelectedViewModel));
}
}

public ICommand UpdateViewCommand { get; set; }

public MainViewModel(MainWindow mainWindow)
{
MainWindow = mainWindow;

UpdateViewCommand = new UpdateViewCommand(this);
UpdateViewCommand.Execute("Start");
}
}
Thats my main view model
class UpdateViewCommand : ICommand
{
private readonly MainViewModel viewModel;

public UpdateViewCommand(MainViewModel viewModel)
{
this.viewModel = viewModel;
}

public event EventHandler? CanExecuteChanged;

public bool CanExecute(object? parameter) => true;
public void Execute(object? parameter)
{
//TODO: Zapisywac stan startu i settingsow

string result = parameter?.ToString() ?? "";
viewModel.MainWindow.EnableDisableChoosenHeadButton(result);

if (result.Equals("Start"))
{
//TODO: Tu tez bedzie wazne zeby przy zmianie na start sprawdzac czy lsity sie roznia i zeby robic to w dwie strony
viewModel.SelectedViewModel = viewModel.Start;
}
else if (result.Equals("Settings"))
{
viewModel.SelectedViewModel = viewModel.Settings;
}
}
}
class UpdateViewCommand : ICommand
{
private readonly MainViewModel viewModel;

public UpdateViewCommand(MainViewModel viewModel)
{
this.viewModel = viewModel;
}

public event EventHandler? CanExecuteChanged;

public bool CanExecute(object? parameter) => true;
public void Execute(object? parameter)
{
//TODO: Zapisywac stan startu i settingsow

string result = parameter?.ToString() ?? "";
viewModel.MainWindow.EnableDisableChoosenHeadButton(result);

if (result.Equals("Start"))
{
//TODO: Tu tez bedzie wazne zeby przy zmianie na start sprawdzac czy lsity sie roznia i zeby robic to w dwie strony
viewModel.SelectedViewModel = viewModel.Start;
}
else if (result.Equals("Settings"))
{
viewModel.SelectedViewModel = viewModel.Settings;
}
}
}
Thats my update view command
<Application x:Class="MultiOpener.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MultiOpener"
xmlns:viewModels="clr-namespace:MultiOpener.ViewModels"
xmlns:views="clr-namespace:MultiOpener.Views"
StartupUri="MainWindow.xaml">
<Application.Resources>
<DataTemplate DataType="{x:Type viewModels:SettingsViewModel}">
<views:SettingsView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:StartViewModel}">
<views:StartView />
</DataTemplate>
</Application.Resources>
</Application>
<Application x:Class="MultiOpener.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MultiOpener"
xmlns:viewModels="clr-namespace:MultiOpener.ViewModels"
xmlns:views="clr-namespace:MultiOpener.Views"
StartupUri="MainWindow.xaml">
<Application.Resources>
<DataTemplate DataType="{x:Type viewModels:SettingsViewModel}">
<views:SettingsView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:StartViewModel}">
<views:StartView />
</DataTemplate>
</Application.Resources>
</Application>
And thats where i create Application resources That could be it but i can't see it what else from code you need?
ACiDCA7
ACiDCA72y ago
hmm nothing jumps into my face as wrong here... in the views do you maybe have some onload/unload event implemented that clears the data? in the worst case just debug your code are variables behind the textboxes properties?
FaNim
FaNimOP2y ago
i have nothing like that you have any instruction how to properly do that? i mean with debugging UI
#line 53 "..\..\..\..\Views\SettingsView.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox AppDirectoryPathField;

#line default
#line hidden
#line 53 "..\..\..\..\Views\SettingsView.xaml"
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")]
internal System.Windows.Controls.TextBox AppDirectoryPathField;

#line default
#line hidden
all my fields and elements in ui are normal i think @ACiDCA7 btw do i need to have like connected datacontext in every view to its model? Okay i found that initialize component is not triggering from one of my views i dont understand it because InitializeComponent is not triggered but rest of things that are next to it are triggered and my models for every view are empty iam doing everyting in views
ACiDCA7
ACiDCA72y ago
sorry i had to go yesterday... did you solve your problem?
FaNim
FaNimOP2y ago
no problem still not yet but i think problem could be in my model - view because iam new with xaml i have no idea how to connect it but with what i read to this time it all reseting when switching view because iam not containing things in model? iam not 100% sure iam still trying and testing things
ACiDCA7
ACiDCA72y ago
that could be a cause aswell you are connecting the value of a textbox with the viiewmodel/model via bindings
FaNim
FaNimOP2y ago
so i can make any variable that will store my field text and it needs to be created in viewModel? and then i need to set dataContext for view to that viewModel or i can do it different?
ACiDCA7
ACiDCA72y ago
the dataconext gets autmatically set since you are using datatemplates.. basicly whats its doing is when you want to draw a viewmodel it draws the view instead and sets the datacontext of the view to the viewmodel lets say you have a viewmodel that includes a string property
public string TextboxContent {get;set;}
public string TextboxContent {get;set;}
so that the content of a textbox gets written into it the TextboxContent has to look something like this
<TextBox Text="{Binding TextboxContent}"/>
<TextBox Text="{Binding TextboxContent}"/>
FaNim
FaNimOP2y ago
there is no way that i didn't found that useful informations on internet. It makes sense a lot now
ACiDCA7
ACiDCA72y ago
you would have found it.. its all over the place..xD
FaNim
FaNimOP2y ago
And so if iam changing visibility of my grid for example do i need to make bool variable and store it in viewModel so it will be saved when i will be chaning views? i was finding similar things but when you explained it i understood it immediately
ACiDCA7
ACiDCA72y ago
i dont quite get what you mean
FaNim
FaNimOP2y ago
hmm because iam chaning my grid visibility in one place one of my views and when i will bind grid visibility to my variable grid visibility will be stored it this variable ye? Sorry for my stupid english xD
ACiDCA7
ACiDCA72y ago
yea i guess... but since you cant just say true or false to visibility you have to use a converter with the binding, that converts true to visible and false to hidden/collapsed
FaNim
FaNimOP2y ago
how to use that converter?
ACiDCA7
ACiDCA72y ago
<Grid Visibility="{Binding VisibilityInViewModel, Converter={StaticRessource BoolToVisibilityConverter}}"/>
<Grid Visibility="{Binding VisibilityInViewModel, Converter={StaticRessource BoolToVisibilityConverter}}"/>
i typed that out of my head so might have to doublecheck... that what i have typed assumes you have created the converter yourself but iirc wpf has its own might have to google a bit
FaNim
FaNimOP2y ago
And i have problem with this because in my xaml
Text="{Binding AddName}"
Text="{Binding AddName}"
because on my AddName it says that it not found element DataContext for AddName hmm so i assumes that i need to create that BoolToVisibilityConverter needs to return Visibility.Hidden or false etc etc
ACiDCA7
ACiDCA72y ago
thats coming from the designer right? dont listen on it too much.. its because you have to tell the designer what type the datacontext is like i said, i think wpf has its own so you can save a couple of lines but to be honest it doesnt hurt to learn how to do it ;)
FaNim
FaNimOP2y ago
Okay but i want to say that i need for my settings view set data context from constructor to MainWindow because i need to change my ItemsSource from MainWindow to list view in settings facts Oh and i need to say that it still not saves my TextBox text when iam swithing between views :/ so thats why i said with what designer said to it
ACiDCA7
ACiDCA72y ago
do you have your project on github or so? i have to guess what you now actually did and its hard to point out mistakes ;)
FaNim
FaNimOP2y ago
i have but its private but i can add you for a moment I immediately say that iam trying to make opening program tool for minecraft speedrun community
ACiDCA7
ACiDCA72y ago
why not make it public? i dont judge what you do as long you dont do anything nefarious
FaNim
FaNimOP2y ago
i will make it public but when i will make at least first working demo version but i can make it public for a moment if you want to help me with it its my second xaml project so there is so much prototyping and trying different things
ACiDCA7
ACiDCA72y ago
fine, you got a link for me?
FaNim
FaNimOP2y ago
GitHub
GitHub - FaNim21/MultiOpener
Contribute to FaNim21/MultiOpener development by creating an account on GitHub.
FaNim
FaNimOP2y ago
here's link i made it public at the moment
ACiDCA7
ACiDCA72y ago
jup give me a moment
FaNim
FaNimOP2y ago
ye sure
ACiDCA7
ACiDCA72y ago
meeh i see where the problem is... you are overriding the datacontext in the constructor in the view at least in the settings view when doing mvvm you usually the codebehind in the view are pretty empty, just covering UI stuff
FaNim
FaNimOP2y ago
hmm okay i was thinking later that this can cause problem yeeee okay thats it but now how can i set itemSource for listview from MainWindow list? :/
ACiDCA7
ACiDCA72y ago
you create a list and bind to it in the VM
FaNim
FaNimOP2y ago
so you think it will be better to have that list in VM for settings and reference to this list from settingsVM via MainViewModel etc?
ACiDCA7
ACiDCA72y ago
yes assuming i understood you correctly^^
FaNim
FaNimOP2y ago
Okay so i things thats propably all thank you so much you are insane that you helped with this all ❤️
ACiDCA7
ACiDCA72y ago
np
FaNim
FaNimOP2y ago
@ACiDCA7 sorry for taking up your time again but in ViewModel side can i make functions for normal button clicks? because it cant work for me like i wrote it in view code side do i really need to make it as commands? or i can keep button functions in view side class and just edit field from view not from ViewModel?
ACiDCA7
ACiDCA72y ago
you can and should do it the viewmodel... as soon as you got the hang of it how it works its easy
FaNim
FaNimOP2y ago
but i have debug error when joining settings view
FaNim
FaNimOP2y ago
FaNim
FaNimOP2y ago
Click="{Binding SaveCurrentOpenButtonClick}"
Click="{Binding SaveCurrentOpenButtonClick}"
this is the 112 line so it needs to be as ICommand or not?
public event RoutedEventHandler? Click;

public ButtonNormal()
{
InitializeComponent();
}

private void ButtonClick(object sender, RoutedEventArgs e)
{
Click?.Invoke(sender, e);
}
public event RoutedEventHandler? Click;

public ButtonNormal()
{
InitializeComponent();
}

private void ButtonClick(object sender, RoutedEventArgs e)
{
Click?.Invoke(sender, e);
}
Thats my user control button code side
<UserControl x:Class="MultiOpener.Components.Buttons.ButtonNormal"
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"
mc:Ignorable="d"
d:DesignHeight="25" d:DesignWidth="100"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Button Name="button"
Click="ButtonClick"
FontWeight="Medium"
Foreground="White"
Content="{Binding ContentText}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="#17191A"/>
<Setter Property="Cursor" Value="Hand"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#27292B"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>

<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</UserControl>
<UserControl x:Class="MultiOpener.Components.Buttons.ButtonNormal"
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"
mc:Ignorable="d"
d:DesignHeight="25" d:DesignWidth="100"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Button Name="button"
Click="ButtonClick"
FontWeight="Medium"
Foreground="White"
Content="{Binding ContentText}">
<Button.Style>
<Style TargetType="Button">
<Setter Property="Background" Value="#17191A"/>
<Setter Property="Cursor" Value="Hand"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#27292B"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>

<Button.Template>
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}">
<ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Border>
</ControlTemplate>
</Button.Template>
</Button>
</UserControl>
and thats the xaml version of my custom button i want to use normal click trigger from and with RoutedEventHandler
ACiDCA7
ACiDCA72y ago
yes it needs to be something that implement icommand in xaml you then use Command instead of Click
<Button Command="{Binding CommandInViewModel}" ...
<Button Command="{Binding CommandInViewModel}" ...
FaNim
FaNimOP2y ago
but what if i need to use RoutedEventHandler like here
private void TextBlockMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && sender is FrameworkElement frameworkElement)
{
DragDrop.DoDragDrop(frameworkElement, new DataObject(DataFormats.Serializable, frameworkElement.DataContext), DragDropEffects.Move);
}
}
private void TextBlockMouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed && sender is FrameworkElement frameworkElement)
{
DragDrop.DoDragDrop(frameworkElement, new DataObject(DataFormats.Serializable, frameworkElement.DataContext), DragDropEffects.Move);
}
}
so i need to put that back to view cs?
ACiDCA7
ACiDCA72y ago
thats now subjective and depends on how you want to do it.. but to me its a plain UI-operation (drag'n'drop) and therefore can be done in the codebehind(in the view) but when you moved a item from one place to another the removing of first place and adding to secondplace should be done if necessary in the viewmodel thats just my opinion tho
FaNim
FaNimOP2y ago
hmm but what can i done if i use sender and args and list from viewmodel at the same moment
ACiDCA7
ACiDCA72y ago
what? anyway sadly im off now
FaNim
FaNimOP2y ago
Okay then thank you any way
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server