WPF隨筆(十二)--使用MVVM模式

規模稍大的WPF項目一般會採用MVVM模式,常見的框架有Prism、MvvmLight、Caliburn等。今天就從頭開始創建一個使用MVVM模式的WPF項目,對MVVM也能有一個更好的瞭解。


1.實現INotifyPropertyChanged接口

實現INotifyPropertyChanged接口是爲了利用WPF的數據綁定特性,當數據源發生變化時,能及時通知UI層進行刷新,避免手動刷新UI的問題。

    public class ViewModelBase : INotifyPropertyChanged
    {

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        protected internal virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion

    }

2.實現ICommand接口

WPF默認的交互方式是事件驅動的,即UI層的動作觸發路由事件,數據層在路由事件的函數中進行相應處理。實現ICommand接口是爲了將UI層和數據層解耦,避免綁定路由事件時的強關聯。

public class DelegateCommands<T> : ICommand
    {
        private readonly Action<T> _executeMethod = null;
        private readonly Func<T, bool> _canExecuteMethod = null;

        public DelegateCommands(Action<T> executeMethod)
            : this(executeMethod, null)
        { }

        public DelegateCommands(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
        {
            if (executeMethod == null)
                throw new ArgumentNullException("executeMetnod");
            _executeMethod = executeMethod;
            _canExecuteMethod = canExecuteMethod;
        }

        #region ICommand 成員
        /// <summary>
        ///  Method to determine if the command can be executed
        /// </summary>
        public bool CanExecute(T parameter)
        {
            if (_canExecuteMethod != null)
            {
                return _canExecuteMethod(parameter);
            }
            return true;

        }

        /// <summary>
        ///  Execution of the command
        /// </summary>
        public void Execute(T parameter)
        {
            if (_executeMethod != null)
            {
                _executeMethod(parameter);
            }
        }

        #endregion


        event EventHandler ICommand.CanExecuteChanged
        {
            add { CommandManager.RequerySuggested += value; }
            remove { CommandManager.RequerySuggested -= value; }
        }

        #region ICommand 成員

        public bool CanExecute(object parameter)
        {
            if (parameter == null && typeof(T).IsValueType)
            {
                return (_canExecuteMethod == null);

            }

            return CanExecute((T)parameter);
        }

        public void Execute(object parameter)
        {
            Execute((T)parameter);
        }

        #endregion
    }

3.創建ViewModel

新建MainWindowViewModel類,繼承自上面的ViewModelBase類,用於命令的綁定觸發和數據的處理通知。

    public class MainWindowViewModel:ViewModelBase
    {
        public MainWindowViewModel()
        {
        	//命令綁定
            AddCommand = new DelegateCommands<string>(Add);
            EditCommand = new DelegateCommands<DetailGoodsDto>(Edit);
            DeleteCommand = new DelegateCommands<DetailGoodsDto>(Delete);
			//數據初始化
            InitData();
        }

        #region Properties-屬性
        private List<DetailGoodsDto> _goodsList;

        public List<DetailGoodsDto> GoodsList
        {
            get { return _goodsList; }
            set
            {
                if (_goodsList != value)
                {
                    _goodsList = value;
                    OnPropertyChanged("GoodsList");
                }
            }
        }
        #endregion

        #region Commands-命令
        public DelegateCommands<string> AddCommand { get; set; }
        public DelegateCommands<DetailGoodsDto> EditCommand { get; set; }
        public DelegateCommands<DetailGoodsDto> DeleteCommand { get; set; }
        #endregion

        #region Methods-方法
        private void InitData()
        {
            GoodsList = new List<DetailGoodsDto>()
            {
            	new DetailGoodsDto
            	{
            		GoodsName="礦泉水",
            		GoodsType="酒水飲料",
            		Location="1號庫",
            		GoodsNum=100
				}
			};
            
        }

        private void Add(string obj)
        {
        	//to do
        }

        private void Edit(DetailGoodsDto obj)
        {
            //to do
        }

        private void Delete(DetailGoodsDto obj)
        {
            //to do
        }
        #endregion

    }

4.修改View並綁定數據上下文

修改MainWindow.xaml,與MainWindowViewModel中的屬性和命令一一對應。

<Window x:Class="AbpDemo.Client.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:AbpDemo.Client"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid Margin="5">
        <Grid.RowDefinitions>
            <RowDefinition Height="80"></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <WrapPanel Grid.Row="0" VerticalAlignment="Center" HorizontalAlignment="Right">
            <Button Command="{Binding AddCommand}" CommandParameter="Add">添加</Button>
            <Button Command="{Binding EditCommand}" CommandParameter="{Binding ElementName=GoodsData,Path=SelectedItem}">編輯</Button>
            <Button Command="{Binding DeleteCommand}" CommandParameter="{Binding ElementName=GoodsData,Path=SelectedItem}">刪除</Button>
        </WrapPanel>
        <DataGrid x:Name="GoodsData" Grid.Row="1" AutoGenerateColumns="False" SelectionUnit="FullRow" ItemsSource="{Binding GoodsList}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="貨品名稱" Width="*" Binding="{Binding GoodsName}"></DataGridTextColumn>
                <DataGridTextColumn Header="貨品類型" Width="*" Binding="{Binding GoodsType}"></DataGridTextColumn>
                <DataGridTextColumn Header="存放位置" Width="*" Binding="{Binding Location}"></DataGridTextColumn>
                <DataGridTextColumn Header="貨品數量" Width="*" Binding="{Binding GoodsNum}"></DataGridTextColumn>
            </DataGrid.Columns>
        </DataGrid>
    </Grid>
</Window>

同時修改MainWindow.xaml.cs以建立View視圖層與ViewModel視圖模型層的關聯關係。

    public partial class MainWindow : Window
    {
        private MainWindowViewModel _viewModel;

        public MainWindow()
        {
            _viewModel = new MainWindowViewModel();
            this.DataContext = _viewModel;//設置數據上下文
            InitializeComponent();
        }
    }

以上步驟就完成了最簡單的MVVM模式。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章