在尝试基于 ListView 中的项目创建新的 window 时,这是否正确使用了 MVVM?
Is this proper use of MVVM when trying to create a new window based on a item in a ListView?
所以我目前正在研究 WPF 和 MVVM,我一直在尝试找到一种方法来 select 列表中的项目并将其显示在新的 window 中。我想出了一个我个人喜欢的解决方案,但我不确定它是否遵循有效的 MVVM 架构。
所以我有我的 MainWindow.xaml
<Window x:Class="Views.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:Views"
xmlns:viewmodel="clr-namespace:Views.MVVM.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
Background="#252525">
<Window.DataContext>
<viewmodel:MainViewModel/>
</Window.DataContext>
<StackPanel Orientation="Horizontal">
<ListView ItemsSource="{Binding NetworkObjects}"
Style="{StaticResource ListStyle}"
Name="MainList"/>
</StackPanel>
</Window>
它使用此样式绑定集合中的每个项目,还应用 MouseBinding
允许我 LeftDoubleClick
项目调用命令。
我将整个对象作为命令参数传递,因为那是 DataContext
。
这是新 Window.
所需要的
<Style TargetType="ListView" x:Key="ListStyle">
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<DockPanel Margin="2">
<DockPanel.InputBindings>
<MouseBinding Gesture="LeftDoubleClick"
Command="{Binding DisplayItemCommand}"
CommandParameter="{Binding Path=.}"/>
</DockPanel.InputBindings>
<DockPanel.Style>
<Style TargetType="DockPanel">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#303030"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" Value="Transparent"/>
</Trigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}},
Path=IsSelected}" Value="True">
<Setter Property="Background" Value="MediumSpringGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<TextBlock Text="{Binding NetworkModel.Address}" Foreground="Black"/>
</DockPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
MainViewModel
除了在构造函数中生成一些虚拟数据外什么都不做。这是我开始怀疑这是否是有效的 MVVM 的地方,因为我生成的对象不是基于 Model
而是基于整个 ViewModel
,这在理论上是有道理的,但我是不确定。
public ObservableCollection<NetworkObjectViewModel> NetworkObjects { get; set; }
public MainViewModel()
{
NetworkObjects = new ObservableCollection<NetworkObjectViewModel>();
for (int i = 0; i < 10; i++)
{
NetworkObjects.Add(new NetworkObjectViewModel() { NetworkModel = new NetworkModel { Address = $"Address {i}", Port = i } });
}
}
并且 NetworkObjectViewModel
包含一个 RelayCommand
和 NetworkModel
。
public class NetworkObjectViewModel
{
public NetworkModel NetworkModel { get; set; }
public RelayCommand DisplayItemCommand { get; set; }
public NetworkObjectViewModel()
{
DisplayItemCommand = new RelayCommand(o =>
{
WindowService.ShowWindow(o);
});
}
}
WindowService
很简单,就是新建一个GenericWindow
,然后设置DataContext
,这样它就可以根据UserControl
做出判断并显示正确的UserControl
DataContext
internal class WindowService
{
public static void ShowWindow(object DataCtx)
{
var t = new GenericWindow();
t.DataContext = DataCtx;
t.Show();
}
}
这是 GenericWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type vms:NetworkObjectViewModel}">
<v:TestView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:NetworkObjectViewModel2}">
<v:TestView2/>
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding .}" />
而 TestView.xaml
只是一个简单的 UserControl
,看起来像这样
<Grid>
<TextBox Text="{Binding NetworkModel.Address, UpdateSourceTrigger=PropertyChanged}"
Width="100"
Height="25"
IsEnabled="True"/>
</Grid>
This is where I start doubting whether or not this is valid MVVM, because I'm generating objects that are not based on a Model but rather an entire ViewModel which in theory makes sense, but I'm not sure.
这很有道理。
MVVM 中的模型通常指的是您不想直接绑定到的类型,例如数据传输对象 (DTO)、包含业务逻辑甚至服务或存储库的域对象。
创建并公开“子”视图模型的集合以从“父”视图模型绑定到 class 是一种非常好的通用方法。
所以我目前正在研究 WPF 和 MVVM,我一直在尝试找到一种方法来 select 列表中的项目并将其显示在新的 window 中。我想出了一个我个人喜欢的解决方案,但我不确定它是否遵循有效的 MVVM 架构。
所以我有我的 MainWindow.xaml
<Window x:Class="Views.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:Views"
xmlns:viewmodel="clr-namespace:Views.MVVM.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800"
Background="#252525">
<Window.DataContext>
<viewmodel:MainViewModel/>
</Window.DataContext>
<StackPanel Orientation="Horizontal">
<ListView ItemsSource="{Binding NetworkObjects}"
Style="{StaticResource ListStyle}"
Name="MainList"/>
</StackPanel>
</Window>
它使用此样式绑定集合中的每个项目,还应用 MouseBinding
允许我 LeftDoubleClick
项目调用命令。
我将整个对象作为命令参数传递,因为那是 DataContext
。
这是新 Window.
<Style TargetType="ListView" x:Key="ListStyle">
<Setter Property="Foreground" Value="White"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="ListViewItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<DockPanel Margin="2">
<DockPanel.InputBindings>
<MouseBinding Gesture="LeftDoubleClick"
Command="{Binding DisplayItemCommand}"
CommandParameter="{Binding Path=.}"/>
</DockPanel.InputBindings>
<DockPanel.Style>
<Style TargetType="DockPanel">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#303030"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="False">
<Setter Property="Background" Value="Transparent"/>
</Trigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ListBoxItem}},
Path=IsSelected}" Value="True">
<Setter Property="Background" Value="MediumSpringGreen"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<TextBlock Text="{Binding NetworkModel.Address}" Foreground="Black"/>
</DockPanel>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
MainViewModel
除了在构造函数中生成一些虚拟数据外什么都不做。这是我开始怀疑这是否是有效的 MVVM 的地方,因为我生成的对象不是基于 Model
而是基于整个 ViewModel
,这在理论上是有道理的,但我是不确定。
public ObservableCollection<NetworkObjectViewModel> NetworkObjects { get; set; }
public MainViewModel()
{
NetworkObjects = new ObservableCollection<NetworkObjectViewModel>();
for (int i = 0; i < 10; i++)
{
NetworkObjects.Add(new NetworkObjectViewModel() { NetworkModel = new NetworkModel { Address = $"Address {i}", Port = i } });
}
}
并且 NetworkObjectViewModel
包含一个 RelayCommand
和 NetworkModel
。
public class NetworkObjectViewModel
{
public NetworkModel NetworkModel { get; set; }
public RelayCommand DisplayItemCommand { get; set; }
public NetworkObjectViewModel()
{
DisplayItemCommand = new RelayCommand(o =>
{
WindowService.ShowWindow(o);
});
}
}
WindowService
很简单,就是新建一个GenericWindow
,然后设置DataContext
,这样它就可以根据UserControl
做出判断并显示正确的UserControl
DataContext
internal class WindowService
{
public static void ShowWindow(object DataCtx)
{
var t = new GenericWindow();
t.DataContext = DataCtx;
t.Show();
}
}
这是 GenericWindow.xaml
<Window.Resources>
<DataTemplate DataType="{x:Type vms:NetworkObjectViewModel}">
<v:TestView/>
</DataTemplate>
<DataTemplate DataType="{x:Type vms:NetworkObjectViewModel2}">
<v:TestView2/>
</DataTemplate>
</Window.Resources>
<ContentPresenter Content="{Binding .}" />
而 TestView.xaml
只是一个简单的 UserControl
,看起来像这样
<Grid>
<TextBox Text="{Binding NetworkModel.Address, UpdateSourceTrigger=PropertyChanged}"
Width="100"
Height="25"
IsEnabled="True"/>
</Grid>
This is where I start doubting whether or not this is valid MVVM, because I'm generating objects that are not based on a Model but rather an entire ViewModel which in theory makes sense, but I'm not sure.
这很有道理。
MVVM 中的模型通常指的是您不想直接绑定到的类型,例如数据传输对象 (DTO)、包含业务逻辑甚至服务或存储库的域对象。
创建并公开“子”视图模型的集合以从“父”视图模型绑定到 class 是一种非常好的通用方法。