MVVM:将带有 Observable 集合的命令绑定到 Listbox 并从文本框中获取值

Posted

技术标签:

【中文标题】MVVM:将带有 Observable 集合的命令绑定到 Listbox 并从文本框中获取值【英文标题】:MVVM : Binding Command with Observable collection to Listbox and taking values from textbox 【发布时间】:2015-02-25 02:32:16 【问题描述】:

我是 MVVM 的新手,在这个小应用程序中,我有一个列表框、三个文本框和两个按钮,一个是更新,另一个是添加。在 XAML 中,我已经完成了所有列表框列与文本框的绑定,根据命令,当我更改任一文本框中的值时,我的更新按钮功能正常,但我不知道如何从文本框中获取值并使用命令在集合中添加值。

这是 Xaml 代码。

<Grid Height="314">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <ListView Name="ListViewEmployeeDetails" Grid.Row="1" Margin="4,109,12,23"  ItemsSource="Binding Products"  >
        <ListView.View>
            <GridView x:Name="grdTest">
                <GridViewColumn Header="ID" DisplayMemberBinding="Binding ID"  Width="100"/>
                <GridViewColumn Header="Name" DisplayMemberBinding="Binding Name"  Width="100" />
                <GridViewColumn Header="Price" DisplayMemberBinding="Binding Price" Width="100" />
            </GridView>
        </ListView.View>
    </ListView>
    <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.ID" />
    <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.Name" />
    <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,61,0,0" Name="txtPrice" VerticalAlignment="Top" Width="178" Text="Binding ElementName=ListViewEmployeeDetails,Path=SelectedItem.Price" />
    <Label Content="ID" Grid.Row="1" HorizontalAlignment="Left" Margin="12,12,0,274" Name="label1" />
    <Label Content="Price" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,59,0,0" Name="label2" VerticalAlignment="Top" />
    <Label Content="Name" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,35,0,0" Name="label3" VerticalAlignment="Top" />
    <Button Content="Update" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,59,0,0" Name="btnUpdate"
            VerticalAlignment="Top" Width="141"
            Command="Binding Path=UpdateCommad"
            />
    <Button Content="Add" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,17,0,0" Name="btnAdd"
        VerticalAlignment="Top" Width="141"
        Command="Binding UpdateCommad"
            />
</Grid>

这是产品类

public class Product : INotifyPropertyChanged

    private int m_ID;
    private string m_Name;
    private double m_Price;
    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propertyName)
    
        if (PropertyChanged != null)
        
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        
    
    #endregion

    public int ID
    
        get
        
            return m_ID;
        
        set
        
            m_ID = value;
            OnPropertyChanged("ID");
        
    
    public string Name
    
        get
        
            return m_Name;
        
        set
        
            m_Name = value;
            OnPropertyChanged("Name");
        
    
    public double Price
    
        get
        
            return m_Price;
        
        set
        
            m_Price = value;
            OnPropertyChanged("Price");
        
    

这里是 ViewModel 类,现在我将产品静态添加到 m_Products 中。

class ProductViewModel

    private ObservableCollection<Product> m_Products;
    public ProductViewModel()
    
        m_Products = new ObservableCollection<Product>
    
        new Product ID=1, Name ="Pro1", Price=10,
        new ProductID=2, Name="BAse2", Price=12
    ;
    
    public ObservableCollection<Product> Products
    
        get
        
            return m_Products;
        
        set
        
            m_Products = value;
        
    
    private ICommand mUpdater;
    public ICommand UpdateCommand
    
        get
        
            if (mUpdater == null)
                mUpdater = new Updater();
            return mUpdater;
        
        set
        
            mUpdater = value;
        
    
    private ICommand addUpdater;
    public ICommand AddCommand
    
        get 
        
            if (addUpdater == null)
                addUpdater = new Updater();
            return addUpdater; 
        
        set
        
            addUpdater = value;
        
    



    private class Updater : ICommand
    
        #region ICommand Members
        public bool CanExecute(object parameter)
        
            return true;
        
        public event EventHandler CanExecuteChanged;
        public void Execute(object parameter)
        
        
        #endregion
    

现在我不知道如何通过单击添加按钮上的命令将值(产品)添加到集合中。

【问题讨论】:

在 ListBox 中,当我添加新条目时,它将使用新条目更新所有可用条目。我正在使用 Observable Collection,这是一个问题吗? 【参考方案1】:

您可以使用relay command。它允许您通过传递给其构造函数的委托来注入命令的逻辑:

/// <summary>
/// Class representing a command sent by a button in the UI, defines what to launch when the command is called
/// </summary>
public class RelayCommand : ICommand

    #region Fields

    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    #endregion // Fields

    #region Constructors

    public RelayCommand(Action<object> execute)
        : this(execute, null)
    
    

    public RelayCommand(Action<object> execute, Predicate<object> canExecute)
    
        if (execute == null)
            throw new ArgumentNullException("execute");

        _execute = execute;
        _canExecute = canExecute;
    
    #endregion // Constructors

    #region ICommand Members

    //[DebuggerStepThrough]
    /// <summary>
    /// Defines if the current command can be executed or not
    /// </summary>
    /// <param name="parameter"></param>
    /// <returns></returns>
    public bool CanExecute(object parameter)
    
        return _canExecute == null || _canExecute(parameter);
    

    public event EventHandler CanExecuteChanged
    
        add  CommandManager.RequerySuggested += value; 
        remove  CommandManager.RequerySuggested -= value; 
    

    public void Execute(object parameter)
    
        _execute(parameter);
    

    #endregion // ICommand Members

使用这种类型的命令很容易做你想做的事,例如在你的视图模型中你现在可以这样做:

public class ProductViewModel : INotifyPropertyChanged

    private ObservableCollection<Product> m_Products;
    public ProductViewModel()
    
        m_Products = new ObservableCollection<Product>
        
            new Product ID = 1, Name = "Pro1", Price = 10,
            new Product ID = 2, Name = "BAse2", Price = 12
        ;
    


    private Product _selectedProduct;
    public Product SelectedProduct
    
        get
        
            return _selectedProduct;
        
        set
        
            _selectedProduct = value;
            OnPropertyChanged("SelectedProduct");
        
    
    public ObservableCollection<Product> Products
    
        get
        
            return m_Products;
        
        set
        
            m_Products = value;
        
    

    ICommand _addCommand;
    public ICommand AddCommand
    
        get
        
            if (_addCommand == null)
            
                _addCommand = new RelayCommand(param => AddItem());
            
            return _addCommand;
        
    


    ICommand _deleteCommand;
    public ICommand DeleteCommand
    
        get
        
            if (_deleteCommand == null)
            
                _deleteCommand = new RelayCommand(param => DeleteItem((Product)param));
            
            return _deleteCommand;
        
    

    private void DeleteItem(Product product)
    
        if (m_Products.Contains(product))
        
            m_Products.Remove(product);
        
    

    private void AddItem()
    
        m_Products.Add(new Product());

    


    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    

如您所见,有两个命令,一个用于添加产品,另一个用于删除所选产品。您不必担心更新,您使用的是 ObservableCollection。另外,我添加了属性 selectedProduct 到您的 ViewModel 以了解在您的视图中选择了哪个元素:

    <Grid Height="314">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <ListView Name="ListViewEmployeeDetails" Grid.Row="1" Margin="4,109,12,23"  ItemsSource="Binding Products" SelectedValue="Binding SelectedProduct"  >
        <ListView.View>
            <GridView x:Name="grdTest">
                <GridViewColumn Header="ID" DisplayMemberBinding="Binding ID"  Width="100" />
                <GridViewColumn Header="Name" DisplayMemberBinding="Binding Name"  Width="100" />
                <GridViewColumn Header="Price" DisplayMemberBinding="Binding Price" Width="100" />
            </GridView>
        </ListView.View>
    </ListView>
    <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="Binding SelectedProduct.ID" />
    <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="Binding SelectedProduct.Name" />
    <TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,61,0,0" Name="txtPrice" VerticalAlignment="Top" Width="178" Text="Binding SelectedProduct.Price" />
    <Label Content="ID" Grid.Row="1" HorizontalAlignment="Left" Margin="12,12,0,274" Name="label1" />
    <Label Content="Price" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,59,0,0" Name="label2" VerticalAlignment="Top" />
    <Label Content="Name" Grid.Row="1" Height="28" HorizontalAlignment="Left" Margin="12,35,0,0" Name="label3" VerticalAlignment="Top" />
    <Button Content="Remove" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,59,0,0" Name="btnUpdate"
        VerticalAlignment="Top" Width="141"
        Command="Binding DeleteCommand" CommandParameter="Binding SelectedProduct"
        />
    <Button Content="Add" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="311,17,0,0" Name="btnAdd"
    VerticalAlignment="Top" Width="141"
    Command="Binding AddCommand"
        />
</Grid>

在删除按钮中,我声明了 CommandParamameter 并将其绑定到 SelectedProduct 属性。这是接收 RelayCommand 以删除产品的参数。没有必要,您已经在视图模型中选择了 SelectedProduct,但我还是这样做了,以展示如何将参数传递给命令。

[编辑 1]

要实现您想要的行为,您需要在 viewModel 中添加三个新属性(Id、Name 和 Price)。现在这些属性应该与 TextBoxes 绑定。要在 ListView 中编辑选定产品,在 SelectedProduct 属性集中,您还需要设置 ID、Name 和 Price 属性的值。当文本框也更改其值时,您必须设置所选产品的属性。

ViewModel 的变化:

  private int _id=1;
    public int Id
    
        get
        
            return _id;
        
        set
        
            _id = value;
            if (SelectedProduct!=null)
            
                SelectedProduct.ID = _id;
            
            OnPropertyChanged("Id");
        
    

    private string _name;
    public string Name
    
        get
        
            return _name;
        
        set
        
            _name = value;
            if (SelectedProduct != null)
            
                SelectedProduct.Name = _name;
            
            OnPropertyChanged("Name");
        
    


    private double _price = 0;
    public double Price
    
        get
        
            return _price;
        
        set
        
            _price = value;
            if (SelectedProduct != null)
            
                SelectedProduct.Price = _price;
            
            OnPropertyChanged("Price");
        
    

    private Product _selectedProduct;
    public Product SelectedProduct
    
        get
        
            return _selectedProduct;
        
        set
        
            _selectedProduct = value;

            Id = _selectedProduct != null ? _selectedProduct.ID : 0;
            Name = _selectedProduct != null ? _selectedProduct.Name : "";
            Price = _selectedProduct != null ? _selectedProduct.Price : 0;

            OnPropertyChanged("SelectedProduct");
        
    

你的观点的变化:

<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="Binding Id" />
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="Binding Name" />
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,61,0,0" Name="txtPrice" VerticalAlignment="Top" Width="178" Text="Binding Price" />

【讨论】:

我可以使用这个 RelayCommand 进行添加、更新和删除吗?如果可以,我该如何更新和删除? 更新的逻辑是什么。当您在列表视图中选择一种产品时,您想在文本框中查看其字段以进行编辑吗? 是的……我也想要一个删除按钮来删除当前项目。 嗨@PraveenDeewan,我编辑我的答案。现在您可以执行您想要的三个功能(添加、更新、删除) 文本框不受 AddItem 的限制,这就是为什么当我添加新项目然后 0 时,空白,0 被添加到列表框中并且删除也不起作用

以上是关于MVVM:将带有 Observable 集合的命令绑定到 Listbox 并从文本框中获取值的主要内容,如果未能解决你的问题,请参考以下文章

在 MVVM 中将 Observable 集合绑定到 wrappanel

MVVM Binding Observable Collection 来查看?

当 MVVM 中的属性更改时通知可观察集合

带有 Repository/Firestore 的 MVVM - 存储来自单个集合的不同查询数组的最佳位置在哪里?

RxSwift 和 MVVM:observable 在没有绑定的情况下无法执行

删除项目 RxSwift MVVM 模式