开发者

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();
        }
    }
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜