如何使用 MVVM 架构处理 WPF 中的 Master-Detail 屏幕通信?
How to handle Master-Detail screen communication in WPF with MVVM architecture?
我正在尝试使用 WPF 构建我的第一个应用程序,为了完全理解 MVVM 我没有使用任何框架,我使用的唯一帮助程序是 Microsoft.Toolkit.Mvvm
我有 2 个页面的应用程序,一个是主页面,另一个是细节页面。
我确实按照 WPF MVVM navigate views 中的说明设置了导航
现在我不明白我应该如何告诉详细信息屏幕它应该显示哪些数据,因为我不允许将参数传递给我在数据上下文中实例化的视图模型。
我的MainWindow.xaml
<Window x:Class="AlgsManagerDesktop.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:AlgsManagerDesktop"
xmlns:views="clr-namespace:AlgsManagerDesktop.Views"
xmlns:viewModel="clr-namespace:AlgsManagerDesktop.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:MasterViewModel}">
<views:MasterView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:DetailsViewModel}">
<views:DetailsView />
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<viewModel:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ContentControl Content="{Binding ViewModel}" />
</Grid>
</Window>
MainWindowViewModel.cs
public class MainWindowViewModel : ObservableObject
{
private BaseViewModel viewModel;
public BaseViewModel ViewModel
{
get => viewModel;
set => SetProperty(ref viewModel, value);
}
public RelayCommand SwitchToDetailsCommand { get; }
public MainWindowViewModel()
{
ViewModel = new MasterViewModel();
SwitchToDetailsCommand = new RelayCommand(SwitchToDetails);
}
private void SwitchToDetails()
{
ViewModel = new DetailsViewModel();
}
}
MasterViewModel.cs
public class MasterViewModel : BaseViewModel
{
private ItemModel selectedItem;
public ItemModel SelectedItem
{
get => selectedItem;
set
{
SetProperty(ref selectedItem, value);
DeleteCommand.NotifyCanExecuteChanged();
}
}
public ObservableCollection<ItemModel> items { get; set; }
public RelayCommand DeleteCommand { get; }
public MasterViewModel()
{
DeleteCommand = new RelayCommand(RemoveItem, ItemIsSelected);
}
private void RemoveItems()
{
AlgSets.Remove(SelectedItem);
}
private bool ItemIsSelected()
{
return SelectedItem != null;
}
}
MasterView.xaml
<UserControl x:Class="AlgsManagerDesktop.Views.MasterView"
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:AlgsManagerDesktop.Views"
xmlns:viewModel="clr-namespace:AlgsManagerDesktop.ViewModel"
xmlns:root="clr-namespace:AlgsManagerDesktop"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<viewModel:MasterViewModel/>
</UserControl.DataContext>
<!-- ListBox here that updates a SelectedItem property -->
<!-- this button handles navigation to details screen, I'd like to pass SelectedItem to the next screen -->
<Button Command="{Binding DataContext.SwitchToDetailsCommand,
RelativeSource={RelativeSource AncestorType={x:Type root:MainWindow}},
Mode=OneWay}">
Open Selected
</Button>
</UserControl>
DetailsView.xaml
<UserControl x:Class="AlgsManagerDesktop.Views.DetailsView"
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:AlgsManagerDesktop.Views"
xmlns:viewModel="clr-namespace:AlgsManagerDesktop.ViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<viewModel:DetailsViewModel/>
</UserControl.DataContext>
<-- Item details here, I'd like to take them from an Item property in the DetailsViewModel -->
</UserControl>
您在 MasterViewModel
中创建了一个 SelectedItem
属性,大概是为了绑定到您可能丢失的 ListBox
的 SelectedItem
属性来自您的 XAML,但这是一个死胡同的视图模型。事实上,我认为您不应该将视图模型一分为三(实际视图模型、主视图模型和细节视图模型),因为它们都 linked 在一起——它们是一个视图拆分为一个视图和 2 个子视图,因此从逻辑上讲,您应该有一个视图模型。
很明显您的方法不会奏效,因为当您在代码中创建 master/details 视图模型时,您根本不会 link 它们,您只是创建一次性产品。
如果你出于任何原因想要将 3 个视图模型分开,另一种方法是将 属性 link 保留到主视图模型中,并将 SelectedItem
属性 到主视图模型,然后在两个子视图中绑定到它。
DetailsView
应该从 MainWindowViewModel
的 ViewModel
属性 继承 DataContext
如果你删除下面的 XAML 标记,即你不应该在某处明确设置 UserControl
的 DataContext
:
<UserControl.DataContext>
<viewModel:DetailsViewModel/>
</UserControl.DataContext>
然后由 MainWindowViewModel
初始化并设置 DetailsViewModel
的状态。
我正在尝试使用 WPF 构建我的第一个应用程序,为了完全理解 MVVM 我没有使用任何框架,我使用的唯一帮助程序是 Microsoft.Toolkit.Mvvm 我有 2 个页面的应用程序,一个是主页面,另一个是细节页面。 我确实按照 WPF MVVM navigate views 中的说明设置了导航 现在我不明白我应该如何告诉详细信息屏幕它应该显示哪些数据,因为我不允许将参数传递给我在数据上下文中实例化的视图模型。
我的MainWindow.xaml
<Window x:Class="AlgsManagerDesktop.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:AlgsManagerDesktop"
xmlns:views="clr-namespace:AlgsManagerDesktop.Views"
xmlns:viewModel="clr-namespace:AlgsManagerDesktop.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate DataType="{x:Type viewModel:MasterViewModel}">
<views:MasterView />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:DetailsViewModel}">
<views:DetailsView />
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<viewModel:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ContentControl Content="{Binding ViewModel}" />
</Grid>
</Window>
MainWindowViewModel.cs
public class MainWindowViewModel : ObservableObject
{
private BaseViewModel viewModel;
public BaseViewModel ViewModel
{
get => viewModel;
set => SetProperty(ref viewModel, value);
}
public RelayCommand SwitchToDetailsCommand { get; }
public MainWindowViewModel()
{
ViewModel = new MasterViewModel();
SwitchToDetailsCommand = new RelayCommand(SwitchToDetails);
}
private void SwitchToDetails()
{
ViewModel = new DetailsViewModel();
}
}
MasterViewModel.cs
public class MasterViewModel : BaseViewModel
{
private ItemModel selectedItem;
public ItemModel SelectedItem
{
get => selectedItem;
set
{
SetProperty(ref selectedItem, value);
DeleteCommand.NotifyCanExecuteChanged();
}
}
public ObservableCollection<ItemModel> items { get; set; }
public RelayCommand DeleteCommand { get; }
public MasterViewModel()
{
DeleteCommand = new RelayCommand(RemoveItem, ItemIsSelected);
}
private void RemoveItems()
{
AlgSets.Remove(SelectedItem);
}
private bool ItemIsSelected()
{
return SelectedItem != null;
}
}
MasterView.xaml
<UserControl x:Class="AlgsManagerDesktop.Views.MasterView"
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:AlgsManagerDesktop.Views"
xmlns:viewModel="clr-namespace:AlgsManagerDesktop.ViewModel"
xmlns:root="clr-namespace:AlgsManagerDesktop"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<viewModel:MasterViewModel/>
</UserControl.DataContext>
<!-- ListBox here that updates a SelectedItem property -->
<!-- this button handles navigation to details screen, I'd like to pass SelectedItem to the next screen -->
<Button Command="{Binding DataContext.SwitchToDetailsCommand,
RelativeSource={RelativeSource AncestorType={x:Type root:MainWindow}},
Mode=OneWay}">
Open Selected
</Button>
</UserControl>
DetailsView.xaml
<UserControl x:Class="AlgsManagerDesktop.Views.DetailsView"
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:AlgsManagerDesktop.Views"
xmlns:viewModel="clr-namespace:AlgsManagerDesktop.ViewModel"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.DataContext>
<viewModel:DetailsViewModel/>
</UserControl.DataContext>
<-- Item details here, I'd like to take them from an Item property in the DetailsViewModel -->
</UserControl>
您在 MasterViewModel
中创建了一个 SelectedItem
属性,大概是为了绑定到您可能丢失的 ListBox
的 SelectedItem
属性来自您的 XAML,但这是一个死胡同的视图模型。事实上,我认为您不应该将视图模型一分为三(实际视图模型、主视图模型和细节视图模型),因为它们都 linked 在一起——它们是一个视图拆分为一个视图和 2 个子视图,因此从逻辑上讲,您应该有一个视图模型。
很明显您的方法不会奏效,因为当您在代码中创建 master/details 视图模型时,您根本不会 link 它们,您只是创建一次性产品。
如果你出于任何原因想要将 3 个视图模型分开,另一种方法是将 属性 link 保留到主视图模型中,并将 SelectedItem
属性 到主视图模型,然后在两个子视图中绑定到它。
DetailsView
应该从 MainWindowViewModel
的 ViewModel
属性 继承 DataContext
如果你删除下面的 XAML 标记,即你不应该在某处明确设置 UserControl
的 DataContext
:
<UserControl.DataContext>
<viewModel:DetailsViewModel/>
</UserControl.DataContext>
然后由 MainWindowViewModel
初始化并设置 DetailsViewModel
的状态。