Switch between views without losing data in WPF
I am working on a project that has one Main Window and two User Controls (Views). The Main Window has a Menu on the left hand side with some buttons that activate one View or the other. On the right hand side of Main Window there's a Content Control that displays the CurrentView. My idea was to have a User Form for adding people on one View and a DataGrid displaying those people on the other. For that I made an ObservableColection that has People and bound that collection to the DataGrid on the corresponding View. Everything is working as intended (with the UserForm I can add people to the collection and then those people get added to the DataGrid) except that when I add a person and then change the View to see if it got added to the DataGrid, nothing is added to the DataGrid but the headers. If I put the same DataGrid with the same Binding in the first View (where the UserForm is) I can see that the people are getting added so I think the problem is that when I change Views I am losing the information about the collection.
`` This is the code I'm using to make the navigation between views available.
MainWindow
<Window x:Class="SwitchViewsDemo2.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:vm="clr-namespace:SwitchViewsDemo2.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<vm:NavigationViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="180"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<StackPanel VerticalAlignment="Center">
<Button Command="{Binding GoToAddPeopleCommand}" Content="Go To Add People" />
<Button Command="{Binding GoToDisplayPeopleCommand}" Content="Go To Display People" />
</StackPanel>
<ContentControl Grid.Column="1" Content="{Binding CurrentView}" />
</Grid>
</Window>
AddPeopleView
<UserControl x:Class="SwitchViewsDemo2.Views.AddPeopleView"
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:SwitchViewsDemo2.ViewModels"
mc:Ignorable="d" FontSize="28"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<vm:AddPeopleViewModel/>
</UserControl.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="Add People" />
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Text="First Name" />
<TextBox Text="{Binding FirstName}"/>
</StackPanel>
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Grid.Column="1">
<TextBlock Text="Last Name" />
<TextBox Text="{Binding LastName}"/>
</StackPanel>
</Grid>
<StackPanel Grid.Row="1">
<Button Content="Add Person" Command="{Binding AddPersonCommand}"
Height="40" Width="200" VerticalAlignment="Top"/>
<DataGrid ItemsSource="{Binding People}"/>
</StackPanel>
</Grid>
</UserControl>
DisplayPeopleView
<UserControl x:Class="SwitchViewsDemo2.Views.DisplayPeopleView"
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:SwitchViewsDemo2.ViewModels"
mc:Ignorable="d" FontSize="28"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<vm:DisplayPeopleViewModel/>
</UserControl.DataContext>
<Grid>
<StackPanel>
<TextBlock Text="Display People"/>
<DataGrid ItemsSource="{Binding People}"/>
</StackPanel>
</Grid>
</UserControl>
NavigationViewModel
using SwitchViewsDemo2.Utilities;
using System.Windows.Input;
namespace SwitchViewsDemo2.ViewModels
{
public class NavigationViewModel : Utilities.ViewModelBase
{
private object _currentView;
public object CurrentView
{
get { return _currentView; }
set { _currentView = value; OnPropertyChanged(); }
}
public ICommand GoToAddPeopleCommand { get; set; }
public ICommand GoToDisplayPeopleCommand { get; set; }
private void AddPeople(object obj) => CurrentView = new AddPeopleViewModel();
private void DisplayPeople(object obj) => CurrentView = new DisplayPeopleViewModel();
public NavigationViewModel()
{
GoToAddPeopleCommand = new RelayCommand(AddPeople);
GoToDisplayPeopleCommand = new RelayCommand(DisplayPeople);
// Startup View
CurrentView = new AddPeopleViewModel();
}
}
}
ViewModelBase
using SwitchViewsDemo2.Models;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace SwitchViewsDemo2.Utilities
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string propName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
private ObservableCollection<PersonModel> _people = new();
public ObservableCollection<PersonModel> People
{
get { return _people; }
set { _people = value; }
}
}
}
RelayCommand
using System;
using System.Windows.Input;
namespace SwitchViewsDemo2.Utilities
{
public class RelayComm开发者_如何学Cand : ICommand
{
private readonly Action<object> _execute;
private readonly Func<object, bool> _canExecute;
public event EventHandler? CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object? parameter) => _canExecute == null || _canExecute(parameter);
public void Execute(object? parameter) => _execute(parameter);
}
}
DataTemplate(Resource Dictionary)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:SwitchViewsDemo2.ViewModels"
xmlns:view="clr-namespace:SwitchViewsDemo2.Views">
<DataTemplate DataType="{x:Type vm:AddPeopleViewModel}">
<view:AddPeopleView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:DisplayPeopleViewModel}">
<view:DisplayPeopleView/>
</DataTemplate>
</ResourceDictionary>
App.xaml
<Application x:Class="SwitchViewsDemo2.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SwitchViewsDemo2"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Utilities/DataTemplate.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Edit: This is how I add the data to the People object
using SwitchViewsDemo2.Models;
using SwitchViewsDemo2.Utilities;
namespace SwitchViewsDemo2.ViewModels
{
public class AddPeopleViewModel : ViewModelBase
{
private string _firstName;
public string FirstName
{
get { return _firstName; }
set { _firstName = value; OnPropertyChanged(); }
}
private string _lastName;
public string LastName
{
get { return _lastName; }
set { _lastName = value; OnPropertyChanged(); }
}
public ButtonCommand AddPersonCommand { get; set; }
public void AddPerson()
{
PersonModel p = new PersonModel()
{
FirstName = FirstName,
LastName = LastName
};
FirstName = string.Empty;
LastName = string.Empty;
People.Add(p);
}
public AddPeopleViewModel()
{
AddPersonCommand = new ButtonCommand(AddPerson);
}
}
}
ButtonCommand:
using System;
using System.Windows.Input;
namespace SwitchViewsDemo2.Utilities
{
public class ButtonCommand : ICommand
{
public event EventHandler? CanExecuteChanged;
private Action _execute;
public ButtonCommand(Action execute)
{
_execute = execute;
}
public bool CanExecute(object? parameter)
{
return true;
}
public void Execute(object? parameter)
{
_execute.Invoke();
}
}
}
精彩评论