zoukankan      html  css  js  c++  java
  • WPF使用MVVM(一)属性绑定

    WPF使用MVVM(一)-属性绑定

    简单介绍MVVM

    MVVMModel(数据类型),View(界面),ViewModel(数据与界面之间的桥梁)的缩写,是一种编程模式,优点一劳永逸,初步增加一些逻辑和工作量,但是为后期维护增加了极大的便利性,减少编程的关注点。

    如:界面显示某一数据,在数据有变动的情况下,传统方式是更新此数据,同时需要手动更新界面中的数据显示。在MVVM的模式下只需关心数据变更即可,数据可通过绑定的模式进行刷新或不刷新。

    关于窗口中的行为(点击、鼠标移入、移出。。。),也可以通过MVVM的方式进行绑定,后期就算窗体重新设计,依然可以在对应的地方绑定对应的行为,维护方式也非常灵活。

    项目搭建

    有了大概的层级描述之后, 我们可以尝试建立一个新的WPF程序,此程序用来显示某个员工的信息(姓名年龄职业),这里先创建我们的Model(数据模型),字段不多就三个,就叫EmployeeModel

        public class EmployeeModel
        {
            /// <summary>
            /// 姓名
            /// </summary>
            public string Name { get; set; }
    
            /// <summary>
            /// 年龄
            /// </summary>
            public int Age { get; set; }
    
            /// <summary>
            /// 职业
            /// </summary>
            public string Profession { get; set; }
        }
    

    然后来布局一下我们的View(界面),这里叫做MainWindow

    image-20220105164759019

    界面代码如下(老样子,注意命名空间与你的项目保持一致):

    <Window
        x:Class="Test.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:local="clr-namespace:Test"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="800"
        Height="450"
        mc:Ignorable="d">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
    
            <TextBlock
                Grid.Row="0"
                Grid.Column="0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Text="姓名:" />
    
            <TextBlock
                Grid.Row="0"
                Grid.Column="1"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Foreground="#0078d4"
                Text="王虎" />
    
            <TextBlock
                Grid.Row="1"
                Grid.Column="0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Text="年龄:" />
    
            <TextBlock
                Grid.Row="1"
                Grid.Column="1"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Foreground="#0078d4"
                Text="35" />
            <TextBlock
                Grid.Row="2"
                Grid.Column="0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Text="工种:" />
    
            <TextBlock
                Grid.Row="2"
                Grid.Column="1"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Foreground="#0078d4"
                Text="程序员" />
    
            <Button
                Grid.Row="3"
                Grid.ColumnSpan="2"
                Margin="20"
                Content="更新一下信息"
                FontSize="30"
                FontWeight="Bold" />
        </Grid>
    </Window>
    
    

    可以看到,上面的数据都是写死的,需要我们将显示的地方,绑定到我们的数据中,在开始绑定之前呢,这里需要建立一下我们的桥梁,也就是ViewModel,为了和界面能对应上,名字叫做MainWindowVM,在这个ViewModel中,我们使用刚才创建的数据模型EmployeeModel,并在构造函数中,初始化一个员工的信息:

        public class MainWindowVM
        {
            private EmployeeModel _employee;
            /// <summary>
            /// 员工数据
            /// </summary>
            public EmployeeModel EmployeeM
            {
                get { return _employee; }
                set { _employee = value; }
            }
    
            public MainWindowVM()
            {
                EmployeeM = new EmployeeModel();
                EmployeeM.Name = "小明(绑定)";
                EmployeeM.Age = 44;
                EmployeeM.Profession = "程序员";
            }
        }
    

    现在我们只需要界面绑定一下MainWindowVM中数据EmployeeM的对应属性就行了,修改界面代码如下:

    <Window
        x:Class="Test.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:local="clr-namespace:Test"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="800"
        Height="450"
        mc:Ignorable="d">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
                <RowDefinition />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition />
                <ColumnDefinition />
            </Grid.ColumnDefinitions>
    
            <TextBlock
                Grid.Row="0"
                Grid.Column="0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Text="姓名:" />
    
            <TextBlock
                Grid.Row="0"
                Grid.Column="1"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Foreground="#0078d4"
                Text="{Binding EmployeeM.Name}" />
    
            <TextBlock
                Grid.Row="1"
                Grid.Column="0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Text="年龄:" />
    
            <TextBlock
                Grid.Row="1"
                Grid.Column="1"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Foreground="#0078d4"
                Text="{Binding EmployeeM.Age}" />
            <TextBlock
                Grid.Row="2"
                Grid.Column="0"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Text="工种:" />
    
            <TextBlock
                Grid.Row="2"
                Grid.Column="1"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Foreground="#0078d4"
                Text="{Binding EmployeeM.Profession}" />
    
            <Button
                Grid.Row="3"
                Grid.ColumnSpan="2"
                Margin="20"
                Content="更新一下信息"
                FontSize="30"
                FontWeight="Bold" />
        </Grid>
    </Window>
    
    

    这时候当我们运行项目的时候,发现界面空空如也,什么数据也没有:

    image-20220105164337683

    这是因为,虽然我们已经有了MainWindowVM(ViewModel),但是并没有将它与MainWindow(View)进行一个关联,所以需要在MainWindow的后台指定一下当前的ViewModel是谁,设置MainWindowDataContext即可:

        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
    
                this.DataContext = new MainWindowVM();
            }
        }
    

    image-20220105164659556

    这下就正常了,下面我们要来演示下MVVM的魔力!

    属性变更

    之前已经说了,MVVM是数据驱动的,我们已经绑定了后台的数据,现在改动下后台数据来看看界面的更新效果。

    为了演示,我们给界面中的Button按钮,添加一个点击事件:

            <Button
                Grid.Row="3"
                Grid.ColumnSpan="2"
                Margin="20"
                Click="Button_Click"
                Content="更新一下信息"
                FontSize="30"
                FontWeight="Bold" />
    

    在点击事件中,我们修改当前绑定的MainWindowVM(ViewModel)中的EmployeeM属性,观察界面的变化,为了在Click事件中获取绑定的实例,需要为MainWindowVM创建一个外部的字段,代码如下:

        public partial class MainWindow : Window
        {
            MainWindowVM mainWindowVM;
            public MainWindow()
            {
                InitializeComponent();
    
                mainWindowVM = new MainWindowVM();
                this.DataContext = mainWindowVM;
            }
    
            private void Button_Click(object sender, RoutedEventArgs e)
            {
                EmployeeModel model = new EmployeeModel();
                model.Name = "王明(修改)";
                model.Age = 38;
                model.Profession = "外卖员";
    
                mainWindowVM.EmployeeM = model;
            }
        }
    

    此时,我们在按钮的点击事件中重新给EmployeeM赋值一个新值,运行程序,点击按钮,发现什么都没变,断点跟进去,发现属性的值已经被修改,但是界面的数据并未发生更改:

    image-20220105171927963

    这跟之前说的MVVM模式介绍有出入,按理说应该是数据更新界面也更新。

    原来绑定属性才是第一步,仅仅绑定还不行,还需要让属性具备通知界面更新的能力。

    那我们就来给属性添加一下这个能力。

    WPF中让属性具备通知界面更新的能力,需要让我们的ViewModel也就是MainWindowVM,继承类型INotifyPropertyChanged,并实现它的PropertyChanged属性:

        public class MainWindowVM: INotifyPropertyChanged
        {
            public event PropertyChangedEventHandler PropertyChanged;
            private void RaisePropertyChanged(string propertyName)
            {
                PropertyChangedEventHandler handler = PropertyChanged;
                if (handler != null)
                    handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    

    完成这一步呢,还没完, 还差一个小地方,那就是我们EmployeeM属性的Set中,需要再补上一句,这样当我们的EmployeeM被设置值的时候,就会调用更新界面的方法RaisePropertyChanged

            private EmployeeModel _employee;
            /// <summary>
            /// 员工数据
            /// </summary>
            public EmployeeModel EmployeeM
            {
                get { return _employee; }
                set
                {
                    _employee = value;
                    //当前属性的名称
                    RaisePropertyChanged("EmployeeM");
                }
            }
    

    大功告成,现在运行项目,点击按钮, 就能够看到界面进行更新了。

    这时候有些老哥就有疑问了,你这给EmployeeM赋值怎么不一样呢,为啥要新建一个数据模型,再赋值呢,我实际的情况是想在原有的数据上改个名字而已:

            private void Button_Click(object sender, RoutedEventArgs e)
            {
                mainWindowVM.EmployeeM.Name = "王明(原属性修改)";
            }
    

    这种修改方式,界面还是没变呢!

    那我们就再来看看类型属性修改怎么通知界面:

    类型属性修改

    上面我们知道数据变动通知界面更新,是我们在EmployeeMSet中调用RaisePropertyChanged方法实现的,所以要想实现类型属性在原有数据的基础上进行界面通知,我们就需要为该类型(EmployeeModel)中属性(Name)的Set中调用RaisePropertyChanged方法,也就是:

            /// <summary>
            /// 姓名
            /// </summary>
            public string Name
            {
                set
                {
                    //value;
                    RaisePropertyChanged("Name")
                }
            }
    

    这样需要什么条件呢,需要我们让EmployeeModel也要继承INotifyPropertyChanged的接口,并实现对应的方法。很先然违背了我们设计的初衷,毕竟我们希望所有的处理都放在桥梁MainWindowVM(ViewModel)中去弄,所以看来只能另辟蹊径了!

    不过这难不倒我们,现在我这边有两种方式可以这么弄,一种中规中矩的,一种比较暴力,先看中规中矩的吧!

    既然我们的在属性的Set中调用通知界面更新的方法,那么我们完全可以在MainWindowVM(ViewModel)新建几个属性,这些属性返回我们模型数据的属性,如下:

            /// <summary>
            /// 名称
            /// </summary>
            public string EmployeeName
            {
                get { return EmployeeM.Name; }
                set
                {
                    EmployeeM.Name = value;
                    RaisePropertyChanged("EmployeeName");
                }
            }
    

    此时我们的界面绑定的值也不是EmployeeM.Name,而是直接改为EmployeeName:

            <TextBlock
                Grid.Row="0"
                Grid.Column="1"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                FontSize="50"
                Foreground="#0078d4"
                Text="{Binding EmployeeName}" />
    

    现在我们直接修改Name属性就可以了

            private void Button_Click(object sender, RoutedEventArgs e)
            {
                //mainWindowVM.EmployeeM.Name = "王明(原属性修改)";
                mainWindowVM.Name = "王明(原属性修改)";
            }
    

    另外一种方式更加直接,更加暴力的方式,上面都不用修改,直接重新赋值一遍:

            private void Button_Click(object sender, RoutedEventArgs e)
            {
                mainWindowVM.EmployeeM.Name = "王明(原属性修改)";
                mainWindowVM.EmployeeM = mainWindowVM.EmployeeM;
            }
    

    既然它没有走Set,我们手动帮助他走一遍!

    总的来说,两种方式都不是特别理想,这边也想不出比较不错的方式,如果有好的意见,还请分享下!非常感谢!

    下一节来说一下命令,让我们的界面后台看起来更加的干净!也就是将Button_Click移动到MainWindowVM(ViewModel)中。

    本文来自博客园,作者:丑萌气质狗,转载请注明原文链接:https://www.cnblogs.com/choumengqizhigou/p/15769574.html

    转载请注明出处QQ群:560611514

  • 相关阅读:
    xamarin ios html5 video.js 无法播放
    限制WPF textbox 字符最大值
    .net 客户端 WCF Binding 多次迭代
    10款无需编程的App DIY开发工具
    国外一些好用的UX/UI设计工具和资源分享
    成功网页设计师的七大必备技能
    提升编程能力的11个技巧
    2015程序员推荐书单
    前端工作流程自动化——Grunt/Gulp 自动化
    HTML5初学者福利!11个在线学习网站推荐
  • 原文地址:https://www.cnblogs.com/choumengqizhigou/p/15769574.html
Copyright © 2011-2022 走看看