首页 > 代码库 > MVVM: 通过 Binding 或 x:Bind 结合 Command 实现,通过非 ButtonBase 触发命令

MVVM: 通过 Binding 或 x:Bind 结合 Command 实现,通过非 ButtonBase 触发命令

介绍
背水一战 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>

MVVM: 通过 Binding 或 x:Bind 结合 Command 实现,通过非 ButtonBase 触发命令