MVVM 框架

  1. MVVM

MVVM的目标和思想与MVP类似,利用数据绑定(Data Binding)、依赖属性(Dependency Property)、命令(Command)、路由事件(Routed Event)等新特性,打造了一个更加灵活高效的架构。

  1. 数据驱动

在常规的开发模式中,数据变化需要更新UI的时候,需要先获取UI控件的引用,然后再更新UI。获取用户的输入和操作也需要通过UI控件的引用。在MVVM中,这些都是通过数据驱动来自动完成的,数据变化后会自动更新UI,UI的改变也能自动反馈到数据层,数据成为主导因素。这样MVVM层在业务逻辑处理中只要关心数据,不需要直接和UI打交道,在业务处理过程中简单方便很多。

  1. 低耦合度

MVVM模式中,数据是独立于UI的。

数据和业务逻辑处于一个独立的ViewModel中,ViewModel只需要关注数据和业务逻辑,不需要和UI或者控件打交道。UI想怎么处理数据都由UI自己决定,ViewModel不涉及任何和UI相关的事,也不持有UI控件的引用。即便是控件改变了(比如:TextView换成EditText),ViewModel也几乎不需要更改任何代码。它非常完美的解耦了View层和ViewModel,解决了上面我们所说的MVP的痛点。

  1. 更新UI

在MVVM中,数据发生变化后,我们在工作线程直接修改(在数据是线程安全的情况下)ViewModel的数据即可,不用再考虑要切到主线程更新UI了,这些事情相关框架都帮我们做了。

  1. View

View层做的就是和UI相关的工作,我们只在XML、Activity和Fragment写View层的代码,View层不做和业务相关的事,也就是我们在Activity不写业务逻辑和业务数据相关的代码,更新UI通过数据绑定实现,尽量在ViewModel里面做(更新绑定的数据源即可),Activity要做的事就是初始化一些控件(如控件的颜色,添加RecyclerView的分割线),View层可以提供更新UI的接口(但是我们更倾向所有的UI元素都是通过数据来驱动更改UI),View层可以处理事件(但是我们更希望UI事件通过Command来绑定)。简单地说:View层不做任何业务逻辑、不涉及操作数据、不处理数据,UI和数据严格的分开。

  1. ViewModel

ViewModel层做的事情刚好和View层相反,ViewModel只做和业务逻辑和业务数据相关的事,不做任何和UI相关的事情,ViewModel 层不会持有任何控件的引用,更不会在ViewModel中通过UI控件的引用去做更新UI的事情。ViewModel就是专注于业务的逻辑处理,做的事情也都只是对数据的操作(这些数据绑定在相应的控件上会自动去更改UI)。同时DataBinding框架已经支持双向绑定,让我们可以通过双向绑定获取View层反馈给ViewModel层的数据,并对这些数据上进行操作。关于对UI控件事件的处理,我们也希望能把这些事件处理绑定到控件上,并把这些事件的处理统一化,为此我们通过BindingAdapter对一些常用的事件做了封装,把一个个事件封装成一个个Command,对于每个事件我们用一个ReplyCommand去处理就行了,ReplyCommand会把你可能需要的数据带给你,这使得我们在ViewModel层处理事件的时候只需要关心处理数据就行了,具体见MVVM Light Toolkit 使用指南的Command部分。再强调一遍:ViewModel 不做和UI相关的事。

  1. Model

Model层最大的特点是被赋予了数据获取的职责,与我们平常Model层只定义实体对象的行为截然不同。实例中,数据的获取、存储、数据状态变化都是Model层的任务。Model包括实体模型(Bean)、Retrofit的Service ,获取网络数据接口,本地存储(增删改查)接口,数据变化监听等。Model提供数据获取接口供ViewModel调用,经数据转换和操作并最终映射绑定到View层某个UI元素的属性上。

  1. 下面是一个简单的增、删、查、改的例子:

创建好WPF项目后,首先要在引用中添加MvvmLigth框架(在管理NuGet程序包中搜索MVVM)如下图:

然后它会自动生成一个ViewModel文件夹,里面有两个类,把其它多余的类删除,留下MainViewModel类即可,如下图:

还有它会在App.xaml文件里自动生成一些内容,要把内容删除,如下图:

下面是主页的界面代码:

<Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="60"/>

            <RowDefinition/>

        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal">

            <TextBlock Text="搜索条件"  VerticalAlignment="Center" Margin="10,0,0,0"/>

            <TextBox Width="200" Height="25" Text="{Binding Search}" Margin="10,0,0,0" />

            <Button Content="查找" Command="{Binding QueryCommand}" Width="70" Height="25" Margin="10,0,0,0"/>

            <Button Content="重置" Command="{Binding ResetCommand}" Width="70" Height="25" Margin="10,0,0,0"/>

            <Button Content="新增" Command="{Binding AddCommand}" Width="70" Height="25" Margin="10,0,0,0"/>

        </StackPanel>

        <DataGrid Grid.Row="1" ColumnWidth="*" AutoGenerateColumns="False" ItemsSource="{Binding GridModelList}" CanUserAddRows="False">

            <DataGrid.Columns>

                <DataGridTextColumn Header="序号" Binding="{Binding Id}"/>

                <DataGridTextColumn Header="姓名" Binding="{Binding Name}"/>

                <DataGridTemplateColumn Header="操作">

                    <DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <StackPanel Orientation="Horizontal">

                                <Button Content="修改" Height="25" Width="60" Foreground="Black" Background="White"

                                        CommandParameter="{Binding Id}" Command="{Binding DataContext.EditCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DataGrid}}"/>

                                <Button Content="删除" Height="25" Width="60" Foreground="White" Background="Red"

                                        CommandParameter="{Binding Id}" Command="{Binding DataContext.DelCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=DataGrid}}"/>

                            </StackPanel>

                        </DataTemplate>

                    </DataGridTemplateColumn.CellTemplate>

                </DataGridTemplateColumn>

            </DataGrid.Columns>

        </DataGrid>

    </Grid>

下面是主页面后台的代码:

/// <summary>

    /// MainWindow.xaml 的交互逻辑

    /// </summary>

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

            MainViewModel viewModel = new MainViewModel();

            viewModel.Query();

            this.DataContext = viewModel;

        }

    }

然后创建一个类MainViewModel,也就是上面实例化的MainViewModel viewModel = new MainViewModel();,在MainViewModel继承ViewModelBase,如下图:

下面是详细代码:

public class MainViewModel : ViewModelBase

    {

        /// <summary>

        /// 构造函数

        /// </summary>

        public MainViewModel()

        {

            localDb = new localDb();

 

            //初始化(查询)命令

            QueryCommand = new RelayCommand(Query);

            //初始化(重置)命令

            ResetCommand = new RelayCommand(()=> {Search = string.Empty;this.Query();});

            //初始化(修改)命令

            EditCommand = new RelayCommand<int>(t => Edit(t));

            //初始化(删除)命令

            DelCommand = new RelayCommand<int>(t => Del(t));

            //初始化(新增)命令

            AddCommand = new RelayCommand(Add);

        }

 

        localDb localDb;

 

        #region 声明按钮命令

        //声明查询按钮命令

        public RelayCommand QueryCommand { set; get; }

        //声明重置按钮命令

        public RelayCommand ResetCommand { set; get; }

        //声明修改按钮命令

        public RelayCommand<int> EditCommand { set; get; }

        //声明删除按钮命令

        public RelayCommand<int> DelCommand { set; get; }

        //声明新增按钮命令

        public RelayCommand AddCommand { set; get; }

        #endregion

        //私有储存数据的变量

        private string search = string.Empty;

        //提供被访问的属性

        public string Search

        {

            get { return search; }//读取数据

            set { search = value;RaisePropertyChanged();}//写入或修改数据

        }

 

        /// <summary>

        /// 数据集

        /// </summary>

        private ObservableCollection<Student> gridModelList;

        public ObservableCollection<Student> GridModelList

        {

            get { return gridModelList; }

            set { gridModelList = value;RaisePropertyChanged(); }

        }

        /// <summary>

        /// 数据查询

        /// </summary>

        public void Query()

        {

            var model = localDb.GetStudentsByName(Search);

            GridModelList = new ObservableCollection<Student>();

            if (model !=null)

            {

                model.ForEach(arg =>

                {

                    GridModelList.Add(arg);

                });

            }

        }

        /// <summary>

        /// 数据修改

        /// </summary>

        /// <param name="id"></param>

        public void Edit(int id)

        {

            var model = localDb.GetStudentById(id);

            if (model !=null)

            {

                UserView userView = new UserView(model);

                var r = userView.ShowDialog();

                if (r.Value)

                {

                    var newModel = GridModelList.FirstOrDefault(t => t.Id == model.Id);

                    if (newModel !=null)

                    {

                        newModel.Name = model.Name;

                    }

                }

            }

        }

        /// <summary>

        /// 数据删除

        /// </summary>

        /// <param name="id"></param>

        public void Del(int id)

        {

            var model = localDb.GetStudentById(id);

            if (model != null)

            {

                var r = MessageBox.Show($"确认删除当前用户:{model.Name}?","操作提示",MessageBoxButton.YesNo,MessageBoxImage.Question);

                if (r==MessageBoxResult.Yes)

                {

                    localDb.DelStudent(model.Id);

                    this.Query();

                }

            }

        }

        /// <summary>

        /// 数据新增

        /// </summary>

        public void Add()

        {

            Student student = new Student();

            UserView userView = new UserView(student);

            var r = userView.ShowDialog();

            if (r.Value)

            {

                student.Id = GridModelList.Max(t => t.Id) + 1;

                localDb.AddStudent(student);

                this.Query();

            }

        }

    }

在创建一个Model文件夹,里面创建一个Student类,同时继承ViewModelBase代码如下:

public class Student:ViewModelBase

    {

        private string name;

        public string Name

        {

            get { return name; }

            set { name = value; RaisePropertyChanged();}

        }

        private int id;

        public int Id

        {

            get { return id; }

            set { id = value; RaisePropertyChanged(); }

        }

    }

再创建一个DB文件夹,里面创建一个localDb类,用来模拟数据生成,还有增删查改的方法,代码如下:

public class localDb

    {

        public localDb()

        {

            Init();

        }

        private List<Student> Student;

        private void Init()

        {

            Student = new List<Student>();

            for (int i = 0; i < 30; i++)

            {

                Student.Add(new Student()

                {

                    Id = i,

                    Name = $"Sample{i}"

                });

            }

        }

        public List<Student> GetStudents()

        {

            return Student;

        }

        public void AddStudent(Student student)

        {

            Student.Add(student);

        }

        public void DelStudent(int Id)

        {

            var model = Student.FirstOrDefault(t => t.Id == Id);

            if (model !=null)

            {

                Student.Remove(model);

            }

        }

        public List<Student> GetStudentsByName(string name)

        {

            return Student.Where(q => q.Name.Contains(name)).ToList();

        }

        public Student GetStudentById(int id)

        {

            var model = Student.FirstOrDefault(t => t.Id == id);

            if (model !=null)

            {

                return new Student()

                {

                    Id = model.Id,

                    Name = model.Name

                };

            }

            return null;

        }

    }

还有再创建一个Views文件夹,里面创建一个UserView窗体,用来实现用用编辑用户信息新增页面,代码如下:

<Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="60"/>

            <RowDefinition />

            <RowDefinition Height="60"/>

        </Grid.RowDefinitions>

        <TextBlock Text="编辑用户信息" FontSize="30" FontWeight="Bold" VerticalAlignment="Center" Margin="10,0,0,0"/>

        <StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">

            <TextBlock Text="姓名" Margin="0,0,10,0" FontSize="16" />

            <TextBox Width="200" Height="25" Text="{Binding Model.Name}"/>

        </StackPanel>

        <StackPanel Orientation="Horizontal" Grid.Row="2" HorizontalAlignment="Right">

            <Button Content="确定" Width="70" Height="25" Click="Button_Click"/>

            <Button Content="取消" Width="70" Height="25" Margin="10,0,10,0" Click="Button_Click_1"/>

        </StackPanel>

    </Grid>

后台:

/// <summary>

    /// UserView.xaml 的交互逻辑

    /// </summary>

    public partial class UserView : Window

    {

        public UserView(Student stu)

        {

            InitializeComponent();

            this.DataContext = new

            {

                Model = stu

            };

        }

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            this.DialogResult = true;

        }

        private void Button_Click_1(object sender, RoutedEventArgs e)

        {

            this.DialogResult = false;

        }

    }

运行效果如何下图:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值