我有一个用户需求:选择列表中的一个项目。单击一个按钮。对话模式窗口打开,显示该项目的详细信息(后面的窗口不会消失)。
因此,我试图找到从第一个视图打开第二个视图并传递有关在绑定的 DataGrid 中选择哪个项目的信息的最简单方法。我在 Microsoft 网站上找到了一个有关从 ViewModel 打开视图的示例,该示例非常简单here,但它不包含传递 DataGrid 中所选项目的机制。我可以使用此代码,但我不知道如何获取所选项目。
来自 winforms,等效的解决方案可能像这样简单:
EditForm frm = new EditForm(lvw.SelectedItem[0].Text);
目前,我正在寻找一个具有最少数量的库/框架/服务/抽象的解决方案,以便我能够真正理解正在发生的事情。
因此我的问题是,如何在调用 ViewModel 中以最简单的方式提供有关在 DataGrid 中选择哪个项目的信息,并将其传递给辅助 View/ViewModel 对,以便我可以使用它加载模型并将视图绑定到它。
将 ViewModel 的数据模板整齐地放置在 App.xaml 中怎么样:
<DataTemplate DataType="DataType="{x:Type vm:MainViewModel}"">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Menu IsMainMenu="True" ItemsSource="{Binding MenuItems}">
<Menu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding HeaderText}"/>
<Setter Property="Command" Value="{Binding ExecuteCommand}"/>
</Style>
</Menu.ItemContainerStyle>
</Menu>
<ListView ItemsSource="{Binding People}" Grid.ColumnSpan="2" Grid.Row="1" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling" SelectedItem="{Binding SelectedPerson}">
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type vm:PersonViewModel}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}"/>
<Button Content="Open Details" Command="{Binding OpenMoreDetailsCommand}" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<ProgressBar Value="{Binding Progress}" Grid.Row="2" Maximum="1" LargeChange="0.1" SmallChange="0.01"/>
<TextBlock Text="{Binding Progress, StringFormat=\{0:P\}}" Grid.Row="2"/>
<TextBlock Text="{Binding Progress, StringFormat=\{0:C\}}" Grid.Row="2" HorizontalAlignment="Right"/>
<Button Content="Populate people" Grid.Column="2" Grid.Row="2" Command="{Binding PopulatePeople}"/>
</Grid>
</DataTemplate>
然后要在窗口中显示您的模板,您只需使用以下命令:
<Window x:Class="WPF_Prototyping.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:WPF_Prototyping"
xmlns:vm="clr-namespace:WPF_Prototyping.ViewModels"
xmlns:models="clr-namespace:WPF_Prototyping.Models"
xmlns:views="clr-namespace:WPF_Prototyping.Views"
mc:Ignorable="d"
Title="MainWindow" Height="549" Width="558">
<ContentControl Content="{Binding .}"/>
</Window>
现在假设这个窗口名为 GenericModalWindow,我们可以在服务中使用它:
public class WindowService : IWindowService
{
public bool? ShowDialog(BaseViewModel bvm)
{
var wnd = new GenericModalWindow
{
DataContext = bvm
};
return wnd.ShowDialog();
}
}
地点:
public interface IWindowService
{
bool? ShowDialog(BaseViewModel bvm);
}
现在您看到了,您的 ListView 的 SelectedItem(如数据模板中的 SelectedItem)已绑定其选定项,因此我们可以轻松地在代码中使用它。我们来看看 Person View Model:
public class PersonViewModel : BaseViewModel
{
public PersonViewModel()
{
WindowService = new WindowService();
OpenMoreDetailsCommand = new DelegateCommand(OpenMoreDetails);
}
public PersonViewModel(Person p) : this()
{
Person = p;
}
private void OpenMoreDetails()
{
WindowService.ShowDialog(this);
}
private Person _person;
public Person Person
{
get { return _person; }
set
{
_person = value;
OnPropertyChanged();
}
}
public IWindowService WindowService { get; set; }
public IDelegateCommand OpenMoreDetailsCommand { get; set; }
}
正如您所看到的,除了分离此功能之外,实际上没有发生任何事情,因此更容易让人们开始使用名为 WPF 的框架。这是最好的部分,您的通用模态窗口看起来与主窗口完全相同。没有框架,没有额外的依赖项,纯 xaml 和 MvvM,您还可以轻松测试这些视图模型,并使用您想要的任何内容作为该窗口的参数,只要它是基本视图模型即可。尝试一下,看看你能做什么。一旦您了解了 WPF 的功能,您将永远不会以同样的方式回顾 WinForms。还有一个提示,不要在视图模型中放置任何 UI 元素,例如用于所选项目绑定的 ListViewItem 或 ComboBoxItem,因为它不起作用,并且您无法真正测试此类代码。快乐编码:-)