[英]How does this button click work in WPF MVVM?
我开始研究WFM MVVM模式。
但我无法理解为什么这个Button
点击工作而不绑定任何事件或动作。
视图
<Window x:Class="WPF2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:WPF2.ViewModel"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<vm:MainWindowViewModel x:Key="MainViewModel" />
</Window.Resources>
<Grid x:Name="LayoutRoot"
DataContext="{Binding Source={StaticResource MainViewModel}}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewProducts,Path=SelectedItem.ProductId}" />
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,35,0,0" Name="txtName" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewProducts,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=ListViewProducts,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 Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="141" Height="23" Margin="310,40,0,0" Content="Update" />
<ListView Name="ListViewProducts" Grid.Row="1" Margin="4,109,12,23" ItemsSource="{Binding Path=Products}" >
<ListView.View>
<GridView x:Name="grdTest">
<GridViewColumn Header="Product ID" DisplayMemberBinding="{Binding ProductId}" Width="100"/>
<GridViewColumn Header="Name" DisplayMemberBinding="{Binding Name}" Width="250" />
<GridViewColumn Header="Price" DisplayMemberBinding="{Binding Price}" Width="127" />
</GridView>
</ListView.View>
</ListView>
</Grid>
</Window>
视图模型
class MainWindowViewModel : INotifyPropertyChanged
{
public const string ProductsPropertyName = "Products";
private ObservableCollection<Product> _products;
public ObservableCollection<Product> Products
{
get
{
return _products;
}
set
{
if (_products == value)
{
return;
}
_products = value;
RaisePropertyChanged(ProductsPropertyName);
}
}
public MainWindowViewModel()
{
_products = new ObservableCollection<Product>
{
new Product {ProductId=1, Name="Pro1", Price=11},
new Product {ProductId=2, Name="Pro2", Price=12},
new Product {ProductId=3, Name="Pro3", Price=13},
new Product {ProductId=4, Name="Pro4", Price=14},
new Product {ProductId=5, Name="Pro5", Price=15}
};
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
我从我读过的教程中找到了这段代码。 当我从列表视图中单击一行时,将设置文本框值。 当我编辑这些值并单击按钮时,listview上的值会更新。
按钮上没有事件也没有绑定命令。 那么按钮点击如何更新listview中的值?
我知道这已经过时了,但我正在研究这个场景,并找到了答案。 首先,在MVVM理论中,“代码隐藏”不应该包含所有代码应该在ViewModel类中的代码。 因此,为了实现按钮单击,您可以在ViewModel中使用此代码(除了INotifyPropertyChanged实现):
public ICommand ButtonCommand { get; set; }
public MainWindowViewModel()
{
ButtonCommand = new RelayCommand(o => MainButtonClick("MainButton"));
}
private void MainButtonClick(object sender)
{
MessageBox.Show(sender.ToString());
}
使用课程:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace <your namespace>.ViewModels
{
public class RelayCommand : ICommand
{
private readonly Action<object> _execute;
private readonly Predicate<object> _canExecute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
if (execute == null) throw new ArgumentNullException("execute");
_execute = execute;
_canExecute = canExecute;
}
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 ?? "<N/A>");
}
}
}
在XAML中:
<Window.DataContext> <viewModels:MainWindowViewModel /> </Window.DataContext> <Button Content="Button" Command="{Binding ButtonCommand}" />
您需要将UpdateSourceTrigger
设置为Explicit
并在Button
的Click
事件上设置更新绑定源。
Button
s Click
事件首先在Command
之前执行。 因此,可以在Click
事件中更新源属性。 我修改了你的代码来证明这一点。
注意:
RelayCommand
从MVVMLight
库。 第一次XAML变更
<Button Grid.Row="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="141" Height="23" Margin="310,40,0,0" Content="Update" Click="Button_Click" Command="{Binding UpdateCommand}"/>
第二次XAML变更
<ListView Name="ListViewProducts" Grid.Row="1" Margin="4,109,12,23" ItemsSource="{Binding Path=Products}" SelectedItem="{Binding SelectedProduct}" >
代码背后
using GalaSoft.MvvmLight.Command;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace WPF2
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
txtID.GetBindingExpression(TextBox.TextProperty).UpdateSource();
txtName.GetBindingExpression(TextBox.TextProperty).UpdateSource();
txtPrice.GetBindingExpression(TextBox.TextProperty).UpdateSource();
}
}
public class MainWindowViewModel : INotifyPropertyChanged
{
public const string ProductsPropertyName = "Products";
private ObservableCollection<Product> _products;
public ObservableCollection<Product> Products
{
get
{
return _products;
}
set
{
if (_products == value)
{
return;
}
_products = value;
RaisePropertyChanged(ProductsPropertyName);
}
}
private Product _SelectedProduct;
public Product SelectedProduct
{
get { return _SelectedProduct; }
set
{
_SelectedProduct = value;
RaisePropertyChanged("SelectedProduct");
}
}
private ICommand _UpdateCommand;
public ICommand UpdateCommand
{
get
{
if (_UpdateCommand == null)
{
_UpdateCommand = new RelayCommand(() =>
{
MessageBox.Show( String.Format("From ViewModel:\n\n Updated Product : ID={0}, Name={1}, Price={2}", SelectedProduct.ProductId, SelectedProduct.Name, SelectedProduct.Price));
});
}
return _UpdateCommand;
}
}
public MainWindowViewModel()
{
_products = new ObservableCollection<Product>
{
new Product {ProductId=1, Name="Pro1", Price=11},
new Product {ProductId=2, Name="Pro2", Price=12},
new Product {ProductId=3, Name="Pro3", Price=13},
new Product {ProductId=4, Name="Pro4", Price=14},
new Product {ProductId=5, Name="Pro5", Price=15}
};
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
在上面的示例代码段中,ListView被绑定到Product的ObservableCollection
,它在内部实现了INotfiyPropertyChanged接口。 此接口负责引发PropertyChanged
事件,并在更改时更新绑定的UI元素值。
您可以在此处看到,所有TextBox的Text属性绑定为Text="{Binding ElementName=ListViewProducts,Path=SelectedItem.ProductId}
”
Binding ElementName
- 这是一个标记扩展,它将告诉XAML编译器将ListView绑定到Textbox控件
Path
- 这将指向Binded UI Element的特定属性。 在这种情况下,它将指向ListView.SelectedItem对象属性。 即。 Product.ProductID
默认情况下,WPF UI元素的绑定模式为TwoWay
。 因此,只要值发生变化,它就会更新Source和Target。 您可以通过将模式更改为OneWay
来尝试此操作
<TextBox Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="80,7,0,0" Name="txtID" VerticalAlignment="Top" Width="178" Text="{Binding ElementName=ListViewProducts,Path=SelectedItem.ProductId,Mode=OneWay}" />
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.