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.xaml

<Page
    x:Class="Windows10.MVVM.View.Demo1"
    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: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}" Width="100" />
                <TextBlock Text="Category:" VerticalAlignment="Center" Margin="20 0 0 0" />
                <TextBox Name="txtCategory" Text="{Binding Category, Mode=TwoWay}" Width="100" />
            </StackPanel>

            <!--
                ButtonBase
                    Command - 指定关联的 ICommand
                    CommandParameter - 传递给 ICommand 的参数
            -->
            <StackPanel Orientation="Horizontal" Margin="0 10 0 0">
                <Button Name="btnSearch" Content="查询" Command="{Binding GetProductsCommand}" Margin="10 0 0 0" />
                <Button Name="btnAdd" Content="添加" Command="{Binding AddProductCommand}" Margin="10 0 0 0" />
                <Button Name="btnUpdate" Content="更新" Command="{Binding UpdateProductCommand}" CommandParameter="{Binding SelectedItem, ElementName=listView}" Margin="10 0 0 0" />
                <Button Name="btnDelete" Content="删除" Command="{Binding DeleteProductCommand}" CommandParameter="{Binding SelectedItem, ElementName=listView}" Margin="10 0 0 0" />
            </StackPanel>

        </StackPanel>
    </Grid>
</Page>
时间: 2024-10-31 07:00:12

MVVM: 通过 Binding 或 x:Bind 结合 Command 实现,通过 ButtonBase 触发命令的相关文章

背水一战 Windows 10 (24) - MVVM: 通过 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.ModelMVVM/Model/Product.cs /* * Model 层的实

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

介绍背水一战 Windows 10 之 MVVM(Model-View-ViewModel) 通过 Binding 或 x:Bind 结合 Command 实现,通过非 ButtonBase 触发命令 示例1.ModelMVVM/Model/Product.cs /* * Model 层的实体类,如果需要通知则需要实现 INotifyPropertyChanged 接口 */ using System.ComponentModel; namespace Windows10.MVVM.Model

背水一战 Windows 10 (23) - MVVM: 通过 Binding 或 x:Bind 结合 Command 实现,通过 ButtonBase 触发命令

[源码下载] 作者:webabcd 介绍背水一战 Windows 10 之 MVVM(Model-View-ViewModel) 通过 Binding 或 x:Bind 结合 Command 实现,通过 ButtonBase 触发命令 示例1.ModelMVVM/Model/Product.cs /* * Model 层的实体类,如果需要通知则需要实现 INotifyPropertyChanged 接口 */ using System.ComponentModel; namespace Wind

(WPF, MVVM) Slider Binding.

对于Button的Command的绑定可以通过实现ICommand接口来进行,但是Slider并没有Command属性. 另外如果要实现MVVM模式的话,需要将一些Method和Slider的Event进行绑定,如何进行呢? (对于UIElement的一些Event进行绑定一定有一些通用的方法,目前还没有深入研究.) 首先,Slider Value的绑定是很简单的, 绑定Slider的Value属性即可. (1)ViewModel public class SliderViewModel : V

(WPF) MVVM: DataGrid Binding

Binding到DataGrid的时候,需要用到ObservableCollection. public ObservableCollection<Customer> Customers { get { return this.customers; } set { this.customers = value; base.OnPropertyChanged("Customers"); } } (WPF) MVVM: DataGrid Binding,布布扣,bubuko.c

(WPF) MVVM: ComboBox Binding

基本思路还是在View的Xmal里面绑定ViewModel的属性,虽然在View的后台代码中也可以实现binding,但是还是在Xmal里面相对的代码量要少一些. 此例子要实现的效果就是将一个List<Customer> 绑定到一个ComboBox,并将选择后的Customer的Age显示在一个TextBlock中. 1. Model public class Customer { public string Name { get; set; } public int Age { get; s

command not found,系统很多命令都用不了 ,修改环境变量

bash: ***: command not found,系统很多命令都用不了,均提示没有此命令. 突然之间linux很多命令都用不了,均提示没有此命令. 这应该是系统环境变量出现了问题导致的. 解决办法: 先用:echo $PATH  查看path是否含有:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 如果没有 先用临时环境变量(重启后消失)#export PATH=$PATH:/usr/local/sbin:/usr

MVVM模式解析和在WPF中的实现(三) 命令绑定

MVVM模式解析和在WPF中的实现(三) 命令绑定 0x00 命令绑定要达到的效果 命令绑定要关注的核心就是两个方面的问题,命令能否执行和命令怎么执行.也就是说当View中的一个Button绑定了ViewModel中一个命令后,什么时候这个Button是可用的,按下Button后执行什么操作.解决了这两个问题基本就实现了命令绑定.另外一个问题就是执行过程中需要的数据(参数)要如何传递.本次主要探讨这几个问题. 0x01 命令绑定的实现 自定义一个能够被绑定的命令需要实现ICommand接口.该接

(MVVM) ListBox Binding

当需要用Lisbbox 来log 一些记录的时候,ObservableCollection 并不可以是记录实时的反应在WPF 的UI上面. 这个时候就需要用一个异步collection 来完成. /// <summary> /// Represents the asynchronous observable collection. /// </summary> /// <typeparam name="T"></typeparam> pu