• WPF之MVVM模式


    1.MVVM模式

    MVVM即模型-视图-视图模型 ,是用于解耦 UI 代码和非 UI 代码的 设计模式。 借助 MVVM,可以在 XAML 中以声明方式定义 UI,将 UI使用数据绑定标到包含数据和命令的其他层。 数据绑定提供数据和结构的松散耦合,使 UI 和链接的数据保持同步,同时可以将用户输入路由到相应的命令。

     MVVM模式由M(Model),V(View),VM(ViewModel)三部分组成,其设计模式类似为MVC开发模式。目的是解耦UI和代码,编译编辑和修改。

    通常一个View对应一个ViewModel,一个ViewModel可能包含n个model。

    实际应用过程中可以对View中的不同控件绑定不同的ViewModel,变成一对多的模式。

    2.View

     View即UI界面,wpf使用xaml编,xaml是在xml的基础上开发的,整体风格与xml有诸多类似之处。

    2.1绑定数据

    View界面中需要通过数据绑定将具体的数据绑定到ViewModel的对应属性上。

    1. <DataGrid x:Name="_dgSubItemList" Grid.Row="0" Margin="10,0,10,0" ItemsSource="{Binding SubitemList}" SelectedItem="{Binding SelectedSubitem}" Style="{StaticResource MLMB.DataGridStyle}">
    2. <DataGrid.Columns>
    3. <DataGridTextColumn Header="{x:Static MUI:Strings.ID}" Binding="{Binding ID, StringFormat=\{0:D\}}" MinWidth="40"/>
    4. <DataGridTextColumn Header="{x:Static MUI:Strings.SubitemName}" Binding="{Binding Name}" MinWidth="100" Width="1*"/>
    5. <DataGridTextColumn Header="{x:Static MUI:Strings.RS}" Binding="{Binding RS}" MinWidth="80"/>
    6. <DataGridTextColumn Header="{x:Static MUI:Strings.RelatedGene}" Binding="{Binding RelatedGene}" MinWidth="100"/>
    7. DataGrid.Columns>
    8. DataGrid>

    如上XAML中将DataGrid的ItemsSource属性绑定到列表,同时将对应列的数据绑定到列表的不同属性数据上。

    2.2绑定事件

    <ctrls:UdButton Content="{x:Static MUI:Strings.Add}" Command="{Binding AddCommand}" CommandParameter="{Binding ElementName=_dgSubItemList}" x:Name="_btnAddSubItem"/>

    按钮不设置Click事件,该有使用Command属性绑定具体的方法,进而调用,同时使用CommandParameter属性传递UI界面参数给ViewModel,以便后台可以访问View的控件进行相关操作。

    如果可以一般ViewModel不建议访问View中的控件,以便完全剥离UI和后台代码,便于移植。

    2.3绑定ViewModel

    1. <Window.DataContext>
    2. <vm:AddSubItemViewModel/>
    3. Window.DataContext>

    通过设置窗体的DataContext属性绑定具体的ViewModel,此方法不需要后台实现ViewModel之后再绑定,可以直接系统实现ViewModel。

    DataContext="{DynamicResource vm:AddSubItemViewModel}"

    Xaml设置DataContext属性时需要单列绑定属性标签,如果在Window标签中绑定DataContext属性需要绑定静态的ViewModel,否则无法正常获取ViewModel数据。

    1. ///
    2. /// 构造函数
    3. ///
    4. public AddSubItemWindow()
    5. {
    6. InitializeComponent();
    7. this.DataContext = new AddSubItemViewModel();
    8. }

    也可以在后台用用代码进行设置,再构造函数里用构造函数绑定ViewModel。

    一般一个界面设定窗体的DataContext属性,不过实际使用可以对不同的孔家设置不同的DataContext属性。

    3. ViewModel

    ViewModel即View和Model的中间层,用于中转界面和具体实现代码。

    3.1 实现INotifyPropertyChanged接口

    ViewModel用于被绑定的数据需要使用INotifyPropertyChanged接口,才能做到UI界面和后台数据的实时变更通知,ViewModel数据更新后实时刷新界面View的数据。

    1. public class BaseNotifyPropertyChanged : INotifyPropertyChanged
    2. {
    3. ///
    4. /// 实现接口
    5. ///
    6. public event PropertyChangedEventHandler PropertyChanged;
    7. ///
    8. /// 属性变更事件
    9. ///
    10. /// 事件源
    11. public void OnPropertyChanged([CallerMemberName] string propertyName = "")
    12. {
    13. this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    14. }
    15. }

    配置[CallerMemberName]属性可以自动获取属性名称作为委托参数的名称,省去代码调用时需要一个个输入委托参数名称。

    3.2 基本属性参数

    1. ///
    2. /// private button enabled state.
    3. ///
    4. private bool _isBtnEnabled = true;
    5. ///
    6. /// Button enabled state.
    7. ///
    8. public bool IsBtnEnabled
    9. {
    10. get
    11. {
    12. return _isBtnEnabled;
    13. }
    14. set
    15. {
    16. _isBtnEnabled = value;
    17. OnPropertyChanged();
    18. }
    19. }

    先定义一个内部私有属性,然后定义一个public属性,通过get,set获取私有类。设置属性值时调用“OnPropertyChanged()”方法通知界面刷新数据。

    3.3 绑定列表

    1. ///
    2. /// private subitem list.
    3. ///
    4. private ObservableCollection _subitemList = new ObservableCollection();
    5. ///
    6. /// Subitem list.
    7. ///
    8. public ObservableCollection SubitemList
    9. {
    10. get
    11. {
    12. return _subitemList;
    13. }
    14. set
    15. {
    16. _subitemList = value;
    17. OnPropertyChanged();
    18. }
    19. }

    绑定列表时一般使用ObservableCollection类实现,以便数据更新和刷新。

    当然,如果不需要数据实时刷新时,可以直接绑定DataTable或者List等数据源。

    3.4 绑定事件

    3.4.1 有command属性时绑定方法

    绑定事件需要继承ICommand类,通过ICommand实现事件。

    1. ///
    2. /// Base command class.
    3. ///
    4. /// 通过事件委托,以便跨线程调用
    5. public class BaseCommand : ICommand
    6. {
    7. ///
    8. /// Event handler
    9. /// 事件出发时先调用CanExecute(),然后调用
    10. ///
    11. public event EventHandler CanExecuteChanged;
    12. ///
    13. /// Execute action.
    14. ///
    15. public Action<object> DoExecute { get; set; }
    16. ///
    17. /// Canexecute method.
    18. ///
    19. public Func<object, bool> DoCanExecute { get; set; } = new Func<object, bool>(obj => true);
    20. ///
    21. /// Can execute.
    22. ///
    23. ///
    24. ///
    25. public bool CanExecute(object parameter)
    26. {
    27. //初始化时会调用一次
    28. return DoCanExecute?.Invoke(parameter) == true;
    29. }
    30. ///
    31. /// Excute.
    32. ///
    33. ///
    34. public void Execute(object parameter)
    35. {
    36. //实例化委托。
    37. DoExecute?.Invoke(parameter);
    38. }
    39. ///
    40. /// 执行委托,调用CanExecute。
    41. ///
    42. public void DoCanExecuteChanged()
    43. {
    44. CanExecuteChanged?.Invoke(this, EventArgs.Empty);
    45. }
    46. }

    ICommand接头实例化时会先调用一次CanExecuteChanged,然后出发方法后先调用一次CanExecuteChanged,然后调用Execute类。

    该BaseCommand类实现两个方法,然后将事件通过委托外传,ViewMode可以为具体事件绑定不同的方法。

    1. ///
    2. /// 构造函数
    3. ///
    4. public AddSubItemViewModel()
    5. {
    6. //Relate to export data method.
    7. ExportDataCommand = new BaseCommand()
    8. {
    9. DoExecute = new Action<object>(ExportData)
    10. };
    11. }
    12. ///
    13. /// Export data.
    14. ///
    15. ///
    16. public void ExportData(object obj)
    17. {
    18. IsBtnEnabled = false;
    19. FileIO.ExportData();
    20. }

    先实现指令类,然后再构造函数中管理具体的实现方法,再在函数中调用具体的处理函数。

    3.4.2 将事件绑定到Command上

    需要先添加NuGet包 behaviors

     然后再Xaml文件中添加引用:

     xmlns:behaviors="http://schemas.microsoft.com/xaml/behaviors"

    再对应的控件中添加具体方法,绑定Command:

    1. <behaviors:Interaction.Triggers>
    2. <behaviors:EventTrigger EventName="SelectionChanged">
    3. <behaviors:InvokeCommandAction Command="{Binding SelectedChangedCommand}" CommandParameter="{Binding ElementName=lb_Items}"/>
    4. behaviors:EventTrigger>
    5. behaviors:Interaction.Triggers>

    4. Model

    Model类是一个个具体处理的类,包括各种处理函数等实体类,通过ViewModel进行调用。

  • 相关阅读:
    JDBC调用存储过程
    springboot+vue美容院美妆化妆品商城管理系统nodejs
    adele心理学
    珍藏的javafx教程——简单了解
    浅谈泰山众筹合约系统开发逻辑技术方案详解(原理解析)
    卫生纸标准及鉴别
    【计算机网络】网络层:外部网关协议BGP
    UE4 UltrDynamicSky与场景物体进行交互
    面试突击36:线程安全问题是怎么产生的?
    sealos 部署halo
  • 原文地址:https://blog.csdn.net/u010839204/article/details/127965938