【发布时间】:2018-12-03 15:26:54
【问题描述】:
我在 WPF 方面相对较新,我正在尝试了解 MVVM 模式以及数据绑定如何与 ObservableCollection 一起工作,以便构建我正在使用 MVVM 处理的应用程序。我创建了一个具有 MainWindow 的应用程序示例,其中根据用户按下的按钮显示不同的视图 (UserControl)。总体思路是,用户可以访问数据库中某些元素的数据(例如:客户、产品等),并且能够添加新元素、编辑或删除现有元素。
因此,有一个 CustomerView 及其 CustomerViewModel 和一个 ProductView 及其 ProductViewModel。此外,还有两个代表模型的类(Customer.cs 和 Product.cs)。项目结构显示here。
MainWindow.xaml 如下:
<Window.Resources>
<DataTemplate DataType="{x:Type viewModels:CustomerViewModel}">
<views:CustomerView DataContext="{Binding}"/>
</DataTemplate>
<DataTemplate DataType="{x:Type viewModels:ProductViewModel}">
<views:ProductView DataContext="{Binding}"/>
</DataTemplate>
</Window.Resources>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20*"/>
<ColumnDefinition Width="80*"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0" HorizontalAlignment="Center" VerticalAlignment="Center">
<Button x:Name="btnCustomers" Click="btnCustomers_Click" Content="Customers" Width="80" Height="50" Margin="10"/>
<Button x:Name="btnProducts" Click="btnProducts_Click" Content="Products" Width="80" Height="50" Margin="10"/>
</StackPanel>
<Grid Grid.Column="1">
<ContentControl Grid.Column="0" Content="{Binding}"/>
</Grid>
</Grid>
以及 MainWindow.xaml.cs 背后的代码:
public partial class MainWindow : Window
{
public CustomerViewModel customerVM;
public ProductViewModel productVM;
public MainWindow()
{
InitializeComponent();
}
private void btnCustomers_Click(object sender, RoutedEventArgs e)
{
if (customerVM == null)
{
customerVM = new CustomerViewModel();
}
this.DataContext = customerVM;
}
private void btnProducts_Click(object sender, RoutedEventArgs e)
{
if (productVM == null)
{
productVM = new ProductViewModel();
}
this.DataContext = productVM;
}
}
最后CustomerView.xaml如下:
<UserControl.Resources>
<viewModel:CustomerViewModel x:Key="customerVM"/>
<!-- Styling code here...-->
</UserControl.Resources>
<Grid DataContext="{StaticResource ResourceKey=customerVM}">
<Grid.RowDefinitions>
<RowDefinition Height="2*"/>
<RowDefinition Height="7*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Text="Customers" FontSize="18"/>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<ComboBox x:Name="cmbCustomers" Grid.Column="0" VerticalAlignment="Top"
IsEditable="True"
Text="Select customer"
ItemsSource="{Binding}"
DisplayMemberPath="FullName" IsSynchronizedWithCurrentItem="True">
</ComboBox>
<StackPanel Grid.Column="1" Margin="5">
<StackPanel Orientation="Horizontal">
<TextBlock Grid.Column="0" Text="Id:" />
<TextBlock Grid.Column="1" x:Name="txtId" Text="{Binding Path=Id}" FontSize="16"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Grid.Column="0" Text="Name:" />
<TextBlock Grid.Column="1" x:Name="txtFirstName" Text="{Binding Path=FirstName}" FontSize="16"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Grid.Column="0" Text="Surname:" />
<TextBlock Grid.Column="1" x:Name="txtLastName" Text="{Binding Path=LastName}" FontSize="16"/>
</StackPanel>
</StackPanel>
</Grid>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center">
<Button x:Name="btnAddNew" Content="Add New" Click="btnAddNew_Click"/>
<Button x:Name="btnDelete" Content="Delete Customer" Click="btnDelete_Click"/>
</StackPanel>
</Grid>
和 CustomerViewModel.cs:
public class CustomerViewModel : ObservableCollection<Customer>
{
public CustomerViewModel()
{
LoadCustomers();
}
private void LoadCustomers()
{
for (int i = 1; i <= 5; i++)
{
var customer = new Customer()
{
Id = i,
FirstName = "Customer_" + i.ToString(),
LastName = "Surname_" + i.ToString()
};
this.Add(customer);
}
}
public void AddNewCustomer(int id)
{
var customer = new Customer()
{
Id = id,
FirstName = "Customer_" + id.ToString(),
LastName = "Surname_" + id.ToString()
};
Add(customer);
}
}
请注意,ProductView.xaml 和 ProductViewModel.cs 是相似的。 目前,当用户按下“客户”或“产品”按钮时 MainWindow,然后显示相应的 View 并加载集合 根据 LoadCustomers(或 LoadProducts)方法,该方法由 ViewModel 的构造函数。此外,当用户从 ComboBox,则其属性显示正确(即 Id、Name 等)。这 问题是当用户添加新的(或删除现有的)元素时。
问题 1:哪种方法是更新已更改的 Observable 的正确和最佳方式 一个元素的集合并在 UI 中反映它的变化(组合框、属性等)?
问题 2:在测试这个项目的过程中,我注意到 ViewModels(因此是 LoadCustomers 和 LoadProducts 方法)被调用两次。但是,它仅在用户按下客户或 产品按钮分别。它是否也通过 XAML 数据绑定调用?是 这是最佳的实现方式?
【问题讨论】:
-
您不能将 UserControl 的 XAML 中顶级 Grid 的 DataContext 分配给“私有”视图模型实例(
customerVM资源)。如果这样做,则在外部分配给 UserControl 的 DataContext 将被忽略。除此之外,<views:CustomerView DataContext="{Binding}"/>等价于<views:CustomerView />。 -
好吧,老实说,我还没有 100% 了解数据绑定机制和 MVVM 模式。关于顶级 Grid 中的 DataContext,这是我在 WPF 书籍示例中看到的。更具体地说,它提到:“通过设置顶层网格的DataContext,我们将使子控件继承DataContext进行数据绑定”。你说的外部添加的DataContext是指在MainWindow类后面的代码中设置“this.DataContext = customerVM”的时候?
-
是的,就是这样。
标签: c# wpf xaml mvvm observablecollection