簡體   English   中英

WPF MVVM:如何在UI中反映ObservableCollection的更改

[英]WPF MVVM: How to reflect changes of ObservableCollection in the UI

我在WPF中相對較新,並且正在嘗試了解MVVM模式以及ObservableCollection如何進行數據綁定,以便構建我正在使用MVVM處理的應用程序。 我創建了一個具有MainWindow的應用程序示例,根據用戶按下哪個按鈕,將顯示不同的視圖(UserControl)。 總體思路是,用戶將可以訪問數據庫中某些元素的數據(例如:客戶,產品等),並且能夠添加新元素以及編輯或刪除現有元素。

因此,分別有一個CustomerView和它的CustomerViewModel,以及一個ProductView和它的ProductViewModel。 此外,還有兩個表示模型的類(Customer.cs和Product.cs)。 項目的結構顯示在此處

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的“客戶”或“產品”按鈕時,將顯示相應的視圖,並根據LoadCustomers(或LoadProducts)方法加載集合,該方法由ViewModel的構造函數調用。 同樣,當用戶從組合框選擇其他對象時,其屬性也會正確顯示(即ID,Name等)。 問題是當用戶添加新(或刪除現有)元素時。


問題1 :更新元素的已更改可觀察集合並在UI(組合框,屬性等)中反映其更改的正確和最佳方法是什么?

問題2 :在測試該項目期間,我注意到ViewModels的構造函數(因此是LoadCustomers&LoadProducts方法)被調用了兩次。 但是,僅當用戶分別按下“客戶”或“產品”按鈕時才調用它。 是否也通過XAML數據綁定來調用? 這是最佳實現嗎?

您的第一個問題基本上是用戶體驗,沒有正確或“最佳”的方法。 您肯定會最終使用某種ItemsControl ,但是哪種很大程度上取決於您希望用戶如何與之交互。

第二個問題,您的代碼有一些錯誤:

  1. <viewModel:CustomerViewModel x:Key="customerVM"/>實例化一個新的視圖模型,除了主應用程序創建的模型之外

  2. Grid DataContext="{StaticResource ResourceKey=customerVM}"然后使用此“本地”視圖模型,而忽略了從主應用程序繼承的模型

這就是為什么您看到構造函數觸發兩次,而您正在構造兩個實例的原因! 消除本地VM,不要在網格上分配DC。 其他事宜:

  • <views:ProductView DataContext="{Binding}"/> DataContext分配是完全不必要的,因為它位於數據模板中,因此已經設置了數據上下文
  • <ContentControl Grid.Column="0" Content="{Binding}"/>好吧,您應該有一個“ MainViewModel”及其使用的屬性 不要讓它成為整個數據上下文

  • 缺少用於單擊按鈕的命令(與上面的項目符號有關)

MVVM中的列表需要3種變更通知:

  1. 更改列表項的每個屬性上的通知。
  2. 在必須替換整個實例的情況下,在暴露列表的屬性上的“更改通知”(由於3而非常常見)
  3. 如果元素添加到集合中或從集合中刪除,則更改通知。 那是ObservableCollection唯一需要注意的事情。 不幸的是,沒有Addrange選項,因此批量操作將通過通知將GUI打包。 那就是Nr。 2在那里。

作為高級選項,請考慮公開CollectionView而不是原始Collection。 WPF GUI元素不綁定到原始Collection,僅綁定到CollectionViews。 但是,如果您不交給他們一個,他們就會自己創造一個。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM