背水一戰 Windows 10 之 MVVM(Model-View-ViewModel): 通過 Binding 或 x:Bind 結合 Command 實現,通過非 ButtonBase 觸發命令 ...
背水一戰 Windows 10 (24) - MVVM: 通過 Binding 或 x:Bind 結合 Command 實現,通過非 ButtonBase 觸發命令
作者:webabcd
介紹
背水一戰 Windows 10 之 MVVM(Model-View-ViewModel)
- 通過 Binding 或 x:Bind 結合 Command 實現,通過非 ButtonBase 觸發命令
示例
1、Model
MVVM/Model/Product.cs
/* * Model 層的實體類,如果需要通知則需要實現 INotifyPropertyChanged 介面 */ using System.ComponentModel; namespace Windows10.MVVM.Model { public class Product : INotifyPropertyChanged { public Product() { ProductId = 0; Name = ""; Category = ""; } private int _productId; public int ProductId { get { return _productId; } set { _productId = value; RaisePropertyChanged(nameof(ProductId)); } } private string _name; public string Name { get { return _name; } set { _name = value; RaisePropertyChanged(nameof(Name)); } } private string _category; public string Category { get { return _category; } set { _category = value; RaisePropertyChanged(nameof(Category)); } } public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } } }
MVVM/Model/ProductDatabase.cs
/* * Model 層的數據持久化操作(本地或遠程) * * 本例只是一個演示 */ using System; using System.Collections.Generic; using System.Linq; namespace Windows10.MVVM.Model { public class ProductDatabase { private List<Product> _products = null; public List<Product> GetProducts() { if (_products == null) { Random random = new Random(); _products = new List<Product>(); for (int i = 0; i < 100; i++) { _products.Add ( new Product { ProductId = i, Name = "Name" + i.ToString().PadLeft(4, '0'), Category = "Category" + (char)random.Next(65, 91) } ); } } return _products; } public List<Product> GetProducts(string name, string category) { return GetProducts().Where(p => p.Name.Contains(name) && p.Category.Contains(category)).ToList(); } public void Update(Product product) { var oldProduct = _products.Single(p => p.ProductId == product.ProductId); oldProduct = product; } public Product Add(string name, string category) { Product product = new Product(); product.ProductId = _products.Max(p => p.ProductId) + 1; product.Name = name; product.Category = category; _products.Insert(0, product); return product; } public void Delete(Product product) { _products.Remove(product); } } }
2、ViewModel
MVVM/ViewModel1/MyCommand.cs
/* * 為了方便使用,把 ICommand 再封裝一層 */ using System; using System.Windows.Input; namespace Windows10.MVVM.ViewModel1 { public class MyCommand : ICommand { // 由 public void Execute(object parameter) 調用的委托 public Action<object> MyExecute { get; set; } // 由 public bool CanExecute(object parameter) 調用的委托 public Func<object, bool> MyCanExecute { get; set; } public MyCommand(Action<object> execute, Func<object, bool> canExecute) { this.MyExecute = execute; this.MyCanExecute = canExecute; } // 需要發佈此事件的話,則調用 RaiseCanExecuteChanged 方法即可 public event EventHandler CanExecuteChanged; public void RaiseCanExecuteChanged() { if (CanExecuteChanged != null) { CanExecuteChanged(this, EventArgs.Empty); } } // 用於決定當前綁定的 Command 能否被執行 // parameter 是由 ButtonBase 的 CommandParameter 傳遞過來的 // 如果返回 false 則對應的 ButtonBase 將變為不可用 public bool CanExecute(object parameter) { return this.MyCanExecute == null ? true : this.MyCanExecute(parameter); } // 用於執行對應的命令,只有在 CanExecute() 返回 true 時才可以被執行 // parameter 是由 ButtonBase 的 CommandParameter 傳遞過來的對象 public void Execute(object parameter) { this.MyExecute(parameter); } } }
MVVM/ViewModel1/ProductViewModel.cs
/* * ViewModel 層 * * 註:為了方便使用,此例對 ICommand 做了一層封裝。如果需要瞭解比較原始的 MVVM 實現請參見 http://www.cnblogs.com/webabcd/archive/2013/08/29/3288304.html */ using System; using System.Collections.ObjectModel; using System.ComponentModel; using Windows10.MVVM.Model; namespace Windows10.MVVM.ViewModel1 { public class ProductViewModel : INotifyPropertyChanged { // 用於提供 Products 數據 private ObservableCollection<Product> _products; public ObservableCollection<Product> Products { get { return _products; } set { _products = value; RaisePropertyChanged(nameof(Products)); } } // 用於“添加”和“查詢”的 Product 對象 private Product _product; public Product Product { get { return _product; } set { _product = value; RaisePropertyChanged(nameof(Product)); } } // 資料庫對象 private ProductDatabase _context = null; public ProductViewModel() { _context = new ProductDatabase(); Product = new Product(); Products = new ObservableCollection<Product>(_context.GetProducts()); } private MyCommand _getProductsCommand; public MyCommand GetProductsCommand { get { return _getProductsCommand ?? (_getProductsCommand = new MyCommand ((object obj) => { // 從 Model 層獲取數據 Products = new ObservableCollection<Product>(_context.GetProducts(Product.Name, Product.Category)); }, null)); } } private MyCommand _addProductCommand; public MyCommand AddProductCommand { get { return _addProductCommand ?? (_addProductCommand = new MyCommand ((object obj) => { // 在 Model 層添加一條數據 Product newProduct = _context.Add(Product.Name, Product.Category); // 更新 ViewModel 層數據 Products.Insert(0, newProduct); }, null)); } } private MyCommand _updateProductCommand; public MyCommand UpdateProductCommand { get { return _updateProductCommand ?? (_updateProductCommand = new MyCommand ((object obj) => { // 通過 CommandParameter 傳遞過來的數據 Product product = obj as Product; // 更新 ViewModel 層數據 product.Name = product.Name + "U"; product.Category = product.Category + "U"; // 更新 Model 層數據 _context.Update(product); }, // 對應 ICommand 的 CanExecute(),如果返回 false 則對應的 ButtonBase 將變為不可用 (object obj) => obj != null)); } } private MyCommand _deleteProductCommand; public MyCommand DeleteProductCommand { get { return _deleteProductCommand ?? (_deleteProductCommand = new MyCommand ((object obj) => { // 通過 CommandParameter 傳遞過來的數據 Product product = obj as Product; // 更新 Model 層數據 _context.Delete(product); // 更新 ViewModel 層數據 Products.Remove(product); }, // 對應 ICommand 的 CanExecute(),如果返回 false 則對應的 ButtonBase 將變為不可用 (object obj) => obj != null)); } } public event PropertyChangedEventHandler PropertyChanged; protected void RaisePropertyChanged(string name) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(name)); } } } }
3、View
MVVM/View/Demo1_2.xaml
<Page x:Class="Windows10.MVVM.View.Demo1_2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Windows10.MVVM.View" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" xmlns:Interactivity="using:Microsoft.Xaml.Interactivity" xmlns:Core="using:Microsoft.Xaml.Interactions.Core" xmlns:vm="using:Windows10.MVVM.ViewModel1"> <Grid Background="Transparent"> <StackPanel Margin="10 0 10 10"> <!-- View 層 --> <!-- 本例通過 Binding 結合 Command 實現 MVVM(用 x:Bind 結合 Command 實現 MVVM 也是一樣的),通過非 ButtonBase 觸發命令 --> <StackPanel.DataContext> <vm:ProductViewModel /> </StackPanel.DataContext> <ListView Name="listView" ItemsSource="{Binding Products}" Width="300" Height="300" HorizontalAlignment="Left" VerticalAlignment="Top"> <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding Name}" HorizontalAlignment="Left" /> <TextBlock Text="{Binding Category}" HorizontalAlignment="Left" Margin="10 0 0 0" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView> <StackPanel Orientation="Horizontal" Margin="0 10 0 0" DataContext="{Binding Product}"> <TextBlock Text="Name:" VerticalAlignment="Center" /> <TextBox Name="txtName" Text="{Binding Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100" /> <TextBlock Text="Category:" VerticalAlignment="Center" Margin="20 0 0 0" /> <TextBox Name="txtCategory" Text="{Binding Category, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Width="100" /> </StackPanel> <!-- 需要引用 Microsoft.Xaml.Interactions.dll 和 Microsoft.Xaml.Interactivity.dll Microsoft.Xaml.Interactions.Core:EventTriggerBehavior EventName - 關聯的事件名稱 Microsoft.Xaml.Interactions.Core:InvokeCommandAction Command - 指定關聯的 ICommand CommandParameter - 傳遞給 ICommand 的參數 --> <StackPanel Orientation="Horizontal" Margin="0 10 0 0"> <TextBlock Text="查詢"> <Interactivity:Interaction.Behaviors> <Core:EventTriggerBehavior EventName="Tapped"> <Core:InvokeCommandAction Command="{Binding GetProductsCommand}" /> </Core:EventTriggerBehavior> </Interactivity:Interaction.Behaviors> </TextBlock> <TextBlock Text="添加" Margin="10 0 0 0"> <Interactivity:Interaction.Behaviors> <Core:EventTriggerBehavior EventName="Tapped"> <Core:InvokeCommandAction Command="{Binding AddProductCommand}" /> </Core:EventTriggerBehavior> </Interactivity:Interaction.Behaviors> </TextBlock> <TextBlock Text="更新" Margin="10 0 0 0"> <Interactivity:Interaction.Behaviors> <Core:EventTriggerBehavior EventName="Tapped"> <Core:InvokeCommandAction Command="{Binding UpdateProductCommand}" CommandParameter="{Binding SelectedItem, ElementName=listView}"/> </Core:EventTriggerBehavior> </Interactivity:Interaction.Behaviors> </TextBlock> <TextBlock Text="刪除" Margin="10 0 0 0"> <Interactivity:Interaction.Behaviors> <Core:EventTriggerBehavior EventName="Tapped"> <Core:InvokeCommandAction Command="{Binding DeleteProductCommand}" CommandParameter="{Binding SelectedItem, ElementName=listView}"/> </Core:EventTriggerBehavior> </Interactivity:Interaction.Behaviors> </TextBlock> </StackPanel> </StackPanel> </Grid> </Page>
OK
[源碼下載]