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 来查看?
带有 Repository/Firestore 的 MVVM - 存储来自单个集合的不同查询数组的最佳位置在哪里?