0
votes

I am trying to make an app using using MVVMLight that has a sidebar with buttons, each button shows a new usercontrol hosted in a ContentControl. When the user clicks the buttons in the sidebar (Show View1 / Show View2) it shows the views correctly.

My problems arise when the user has already shown a View (it could be View1 or View2 ), say View1 and click (Show View1) ** again ** View1 controls remain with the same values and I would like to show the same view with all controls as if it had restarted. (I know that I could reset all controls within the user control (View1) but my application requires having a new instance of the view every time the button is clicked).

Somehow, in the same scenario when the user is in View 1, switch to view 2 and return to view 1, a new instance is created and shown as I would like. I would like to get the same result without the need to switch to view 2 and return.

Here is a minimal reproducible example of the issue:

ViewModelLocator.cs

using CommonServiceLocator;
using GalaSoft.MvvmLight.Ioc;

namespace SidebarApp.ViewModel
{
    public class ViewModelLocator
    {
        /// <summary>
        /// Initializes a new instance of the ViewModelLocator class.
        /// </summary>
        public ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            // Register viewmodels
            SimpleIoc.Default.Register<MainViewModel>();
            SimpleIoc.Default.Register<View1ViewModel>();
            SimpleIoc.Default.Register<View2ViewModel>();
        }

        public MainViewModel Main { get { return ServiceLocator.Current.GetInstance<MainViewModel>(); } }

        public View1ViewModel View1Vm { get { return ServiceLocator.Current.GetInstance<View1ViewModel>(); } }

        public View2ViewModel View2Vm { get { return ServiceLocator.Current.GetInstance<View2ViewModel>(); } }

        public static void Cleanup()
        {
            // TODO Clear the ViewModels
        }
    }
}

MainViewModel.cs

using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using System.Windows.Input;

namespace SidebarApp.ViewModel
{
    public class MainViewModel : ViewModelBase
    {

        // Commands
        public ICommand ShowView1Command { get; private set; }
        public ICommand ShowView2Command { get; private set; }

        /// <summary>
        /// Property that will cointain the current viewmodel to show
        /// ViewModelBase inplements ObservableObject class which imlements INotifyPropertyChanged
        /// </summary>
        public ViewModelBase CurrentViewModel { get; set; }

        /// <summary>
        /// Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            this.ShowView1Command = new RelayCommand(ShowView1);
            this.ShowView2Command = new RelayCommand(ShowView2);
        }

        private void ShowView1()
        {
            // I tried this but it doesn't work
            //CurrentViewModel = null or View2ViewModel;

            CurrentViewModel = new View1ViewModel();

        }
        private void ShowView2()
        {
            CurrentViewModel = new View2ViewModel();
        }

    }
}

MainWindow.xaml

<Window x:Class="SidebarApp.MainWindow"
        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:local="clr-namespace:SidebarApp"
        mc:Ignorable="d"
        DataContext="{Binding Main, Source={StaticResource Locator}}"
        Title="MainWindow" Height="348.965" Width="560.683">

    <Window.Resources>
        <DataTemplate DataType="{x:Type local:View1ViewModel}">
            <local:View1View />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:View2ViewModel}">
            <local:View2View />
        </DataTemplate>

    </Window.Resources>

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="0.3*"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        <StackPanel>
            <Button Command="{Binding ShowView1Command}">Show View1</Button>
            <Button Command="{Binding ShowView2Command}">Show View2</Button>            
        </StackPanel>
        <ContentControl Grid.Column="1" Content="{Binding Path=CurrentViewModel}"></ContentControl>
    </Grid>
</Window>

View1View.xaml

<UserControl x:Class="SidebarApp.View1View"
             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:SidebarApp"
             mc:Ignorable="d" 
             DataContext="{Binding View1Vm, Source={StaticResource Locator}}"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Background="LightPink">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock HorizontalAlignment="Center" Text="Welcome to View 1" FontSize="20"/>
            <TextBox HorizontalAlignment="Stretch" Text="Original text value"/>
            <CheckBox Content="Some boolean value"/>
        </StackPanel>
    </Grid>
</UserControl>

View1ViewModel.cs

using GalaSoft.MvvmLight;

namespace SidebarApp
{
    public class View1ViewModel : ViewModelBase
    {
        public View1ViewModel()
        {

        }
    }
}

View2View.xaml

<UserControl x:Class="SidebarApp.View2View"
             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:SidebarApp"
             mc:Ignorable="d" 
             DataContext="{Binding View2Vm, Source={StaticResource Locator}}"
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Background="LightCyan">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock HorizontalAlignment="Center" Text="Welcome to View 2" FontSize="20"/>
            <TextBox HorizontalAlignment="Stretch" Text="Some text in view2"/>
            <Button Content="Button"/>
        </StackPanel>
    </Grid>
</UserControl>

View2ViewModel.cs

using GalaSoft.MvvmLight;

namespace SidebarApp
{
    public class View2ViewModel : ViewModelBase
    {
        public View2ViewModel()
        {

        }
    }
}

App.xaml

<Application x:Class="SidebarApp.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:SidebarApp" StartupUri="MainWindow.xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d1p1:Ignorable="d" xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
  <Application.Resources>
    <ResourceDictionary>
      <vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:SidebarApp.ViewModel" />
    </ResourceDictionary>
  </Application.Resources>
</Application>

For example I enter to the view1 and change the value of the textbox ("Original text value") to another value and I click again in Show View1 the text and everything in the usercontrol stays the same but when I switch to view2 and go back to view1 a new usercontrol is shown with his original values.

1
Have you tried returning a new ViewModel instance in the getter of your ViewModelLocator (instead of current instance)? - RTF
I've just tried not working. I just realized in debug mode that the first time I show the view1 it calls the contructor of View1View.xaml.cs but the second time is no longer called. I have no idea why. And a new instance of the viewmodel is created (wich is ok) I think the problem is in the view, not sure. - 4lex
Any chance you have the project on github? - RTF
Sure I uploaded the project: github.com/alex-delacruz/SidebarApp - 4lex
Ok. Will take a look and post if I discover something interesting. - RTF

1 Answers

0
votes

This might feel like something of a workaround, but it works. Hopefully it will prove useful. (Obviously make same changes to View2View view/view model).


ViewModelLocator.cs

 public View1ViewModel View1Vm { get { return new View1ViewModel(); } }


MainViewViewModel.cs

 private void ShowView1()
 {            
    CurrentViewModel = new ViewModelLocator().View1Vm;           
 }


View1View.xaml

<UserControl x:Class="SidebarApp.View1View"
             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:SidebarApp"
             mc:Ignorable="d"                  
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid Background="LightPink">
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
            <TextBlock HorizontalAlignment="Center" Text="Welcome to View 1" FontSize="20"/>
            <TextBox HorizontalAlignment="Stretch" Text="{Binding Path=View1Text, Mode=TwoWay}"/>
            <CheckBox Content="Some boolean value" IsChecked="{Binding Path=CheckBoxChecked, Mode=TwoWay}"/>
        </StackPanel>
    </Grid>
</UserControl>

View1ViewModel.cs

public class View1ViewModel : ViewModelBase
{
        private string _view1Text;
        private bool _checkBoxedChecked;

        public string View1Text
        {
            get
            {
                return _view1Text;
            }
            set
            {
                _view1Text = value;
                RaisePropertyChanged("View1Text");                
            }
        }

        public bool CheckBoxChecked
        {
            get
            {
                return _checkBoxedChecked;
            }
            set
            {
                _checkBoxedChecked = value;
                RaisePropertyChanged("CheckBoxChecked");
            }
        }

        public View1ViewModel()
        {
            View1Text = "Original text value";
        }
}