WPF-MVVM pattern learning 2 notes - MVVM simple example

 A. MVVM understand  

  1. create a simple WPF sample, and gradually it will be the reconstruction of the MVVM pattern.

   This requirement is Demo: place the text box on the screen to display the class name defined Student placed Button to change the name of Student.

   Just create a good sample project document as shown below:

   

  Then add a Student class,

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVVMDemo
{
    public class Student : INotifyPropertyChanged
    {
        string firstName;
        public string FirstName
        {
            get
            {
                return firstName;
            }
            set
            {
                firstName = value;
                OnPropertyChanged("FirstName");
            }
        }

        string lastName;
        public string LastName
        {
            get
            {
                return lastName;
            }
            set
            {
                lastName = value;
                OnPropertyChanged("LastName");
            }   
        }

        public Student(string firstName, string lastName)
        {
            this.firstName = firstName;
            this.lastName = lastName;
        }


        void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
    }
}

   At this time, the following configuration diagram of FIG Engineering

  

   Then modify MainWindow.xaml, as follows

<Window x:Class="MVVMDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid x:Name="gridLayout">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="5*" />
            <ColumnDefinition Width="5*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="5*" />
            <RowDefinition Height="5*" />
            <RowDefinition Height="5*" />
            <RowDefinition Height="5*" />
        </Grid.RowDefinitions>
        
        <TextBlock Text="FirstName:" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBlock Text="{Binding Path=FirstName,Mode=TwoWay}" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left"/>
        <TextBlock Text="LastName:" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox Text="{Binding Path=LastName,Mode=TwoWay}" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left"/>
        <Button x:Name="BtnView" Content="I am View" Grid.Row="2" Grid.Column="0" Width="150" Height="50" VerticalAlignment="Center" HorizontalAlignment="Right"/>
    </Grid>
</Window>

The figure below shows MainWindow view


   Then, add the following in MainWindow.cs

        public MainWindow()
        {
            InitializeComponent();
            Student student = new Student("Wang", "WenSong");
            gridLayout.DataContext = student;
            BtnView.Click += new RoutedEventHandler(delegate(object sender, RoutedEventArgs e)
                {
                    student.FirstName = "BBK工作室";
                    student.LastName = "www.bigbearking.com";
                });
        }


   In this case the program runs, the following diagram


Click the button BtnView, this time following interface


    The above code works, click here to download

   2. The problem here

      If we need to let the value of the page and the Student instance values ​​remain consistent, you must make the type inherits from the INotifyPropertyChanged interface, and so coded as follows:

public class Student : INotifyPropertyChanged
    {
        string firstName;
        public string FirstName
        {
            get
            {
                return firstName;
            }
            set
            {
                firstName = value;
                OnPropertyChanged("FirstName");
            }
        }

        string lastName;
        public string LastName
        {
            get
            {
                return lastName;
            }
            set
            {
                lastName = value;
                OnPropertyChanged("LastName");
            }   
        }

        public Student(string firstName, string lastName)
        {
            this.firstName = firstName;
            this.lastName = lastName;
        }


        void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
    }

  If there is a plurality of such type applications, such as there Teacher class, each class must realize their OnPropertyChanged method, which is obviously unreasonable. So, we need a super class to wrap this demand, of course, the super class inherits from INotifyPropertyChanged.

   3. Next, add the superclass NotificationObject in engineering, the structure of FIG.


The superclass code

    public abstract class NotificationObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler handler = this.PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected void RaisePropertyChanged(params string[] propertyNames)
        {
            if (propertyNames == null) throw new ArgumentNullException("propertyNames");

            foreach (var name in propertyNames)
            {
                this.RaisePropertyChanged(name);
            }
        }

        protected void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
        {
            var propertyName = ExtractPropertyName(propertyExpression);
            this.RaisePropertyChanged(propertyName);
        }

        public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
        {
            if (propertyExpression == null)
            {
                throw new ArgumentNullException("propertyExpression");
            }

            var memberExpression = propertyExpression.Body as MemberExpression;
            if (memberExpression == null)
            {
                throw new ArgumentException("PropertySupport_NotMemberAccessExpression_Exception", "propertyExpression");
            }

            var property = memberExpression.Member as PropertyInfo;
            if (property == null)
            {
                throw new ArgumentException("PropertySupport_ExpressionNotProperty_Exception", "propertyExpression");
            }

            var getMethod = property.GetGetMethod(true);
            if (getMethod.IsStatic)
            {
                throw new ArgumentException("PropertySupport_StaticExpression_Exception", "propertyExpression");
            }

            return memberExpression.Member.Name;
        }

    }

  Accordingly, the Student type is changed to:

  

    public class Student : NotificationObject
    {
        string firstName;
        public string FirstName
        {
            get
            {
                return firstName;
            }
            set
            {
                firstName = value;
                //OnPropertyChanged("FirstName");
                this.RaisePropertyChanged("FirstName");
            }
        }

        string lastName;
        public string LastName
        {
            get
            {
                return lastName;
            }
            set
            {
                lastName = value;
                //OnPropertyChanged("LastName");
                this.RaisePropertyChanged("LastName");
            }   
        }

        public Student(string firstName, string lastName)
        {
            this.firstName = firstName;
            this.lastName = lastName;
        }


        void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        #endregion
    }

     This part of the code, click here to download

    4. The problem occurs again, what type of modified through Student is?

      Is the entity Model, field Model, or something else? In fact, since not using any mode architecture, the current Student type nothing, rub a lot of miscellaneous functions. It is responsible for providing property, are also responsible for control.

      In MVVM architecture mode, title and MVC in different places, that is, VM (ViewModel) section. VM is responsible for: View to accept the request and decide which model component call to process the request, but it also is responsible for the data back to the View for display. That is, VM role can be understood as the completion of the MVC Control. (Also note that, there is a concept called MVC model performance, the performance of the model is called a flat field projection model, and MVVM not confused in the VIEW MODEL).

    So, we now want to clear these concepts. First, the Student types of functional segmentation, VM section, we correspond with the name of the page it should be called MainViewModel. The actual project, the page will function accordingly named StudentView.xaml, then the corresponding VM name will be called StudentViewModel.cs. We continue to reconstruct the code above.

II. Establish various parts of MVVM

    Structural changes now refactor the code, the project is relatively large, I will put this part of the code is also passed up.

    First, create three folders Model, View, ViewModel in the original project, as shown below

    

   1. Field of the model portion DomainModel

   Then Student.cs moved Model folder, and modify the code in Student.cs, Student.cs content modified as follows (note change namespace)

  

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVVMDemo.Model
{
    public class Student
    {
        string firstName;
        public string FirstName
        {
            get
            {
                return firstName;
            }
            set
            {
                firstName = value;
            }
        }

        string lastName;
        public string LastName
        {
            get
            {
                return lastName;
            }
            set
            {
                lastName = value;
            }   
        }

        public Student()
        {
            //模拟获取数据
            //这里为什么会有模拟数据一说呢?我是这样认为的,有时候类的属性会存在数据库或者本地文件系统等上面,
            //我们需要读取操作将这些数据加载到咱们定义的类里。
            Mock();
        }

        public void Mock()
        {
            FirstName = "firstName:" + DateTime.Now.ToString();
            LastName = "lastName:" + DateTime.Now.ToString();
        }


    }
}
   At this point the project file structure becomes the figure below

   

   2.ViewModel part

   Next, right-click to add a class StudentViewModel ViewModel folder, as follows

   

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MVVMDemo.Model;

namespace MVVMDemo.ViewModel
{
    public class StudentViewModel:NotificationObject
    {
        private Student student;
        public Student Student
        {
            get
            {
                return this.student;
            }
            set
            {
                this.student = value;
                //下面这一句话的用法以后再拿出一章具体介绍
                this.RaisePropertyChanged(() => this.student);
            }
        }

        public StudentViewModel()
        {
            student = new Student();
        }

    }
}

   At this point the project file structure for the next drawing

   

  3.View part

  Then again add a user control in the View folder named StudentView, its XAML code for the next

  

<UserControl x:Class="MVVMDemo.View.StudentView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:vm="clr-namespace:MVVMDemo.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="400">
    <Grid x:Name="gridLayout">
        <Grid.DataContext>
            <vm:StudentViewModel />
        </Grid.DataContext>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="5*" />
            <ColumnDefinition Width="5*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="5*" />
            <RowDefinition Height="5*" />
            <RowDefinition Height="5*" />
            <RowDefinition Height="5*" />
        </Grid.RowDefinitions>

        <TextBlock Text="FirstName:" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBlock Text="{Binding Path=Student.FirstName,Mode=Default}" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left"/>
        <TextBlock Text="LastName:" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox Text="{Binding Path=Student.LastName,Mode=TwoWay}" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left"/>
        <Button x:Name="BtnView" Content="I am View" Grid.Row="2" Grid.Column="0" Width="150" Height="50" VerticalAlignment="Center" HorizontalAlignment="Right"/>
    </Grid>
</UserControl>
  Notice that the XAML code to bind some changes, is bound Student.FirstName and Student.LastName, instead of FirstName and LastName.

  At this point the project file picture shows the structure of the figure below

  

   Then in MainWindow need to refer to this control, modify MainWindow.xaml code reads as follows

   

<Window x:Class="MVVMDemo.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:view="clr-namespace:MVVMDemo.View"
        Title="MainWindow" Height="350" Width="525">
    <Grid >
        <view:StudentView />
    </Grid>
</Window>

   Then in the code before adding MainWindow.cs deleted, modified content follows

   

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MVVMDemo
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

        }
    }
}

   Compile the project, run the results in Figure

   

  The portion of the code, click here to download

   4. Several explanations

   In the project Demo, the domain model Student is responsible for obtaining data, where the data comes from is not the focus of our concern (perhaps a database, it may be the configuration file, and so on), so we get a direct analog in the Student process data, i.e. Mock method. This is equivalent to the completion of the process of a OneWay, that data is pushed to the foreground to the background display, which can only be regarded as part of the complete functionality with UI interaction. UI interaction will also need to include data from the persistent UI (e.g., saved to the database). UI and interaction with the background, you need to go through a mechanism to achieve a binding order.

   5. Bind command

     In the next project, we demonstrate two types of commands, one command is binding attribute class, a class is a class event command binding.

     First of all, we know that contact is responsible for UI and the domain model VM, therefore, binding methods must be supported in the VM, so we define a property CanSubmit, and a method in StudentViewModel in Submit

     

        public bool CanSubmit
        {
            get
            {
                return true;
            }
        }

        public void Submit()
        {
            student.Mock();
        }

    At this time, the contents of the following StudentViewModel

    

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MVVMDemo.Model;

namespace MVVMDemo.ViewModel
{
    public class StudentViewModel:NotificationObject
    {
        private Student student;
        public Student Student
        {
            get
            {
                return this.student;
            }
            set
            {
                this.student = value;
                //下面这一句话的用法以后再拿出一章具体介绍
                this.RaisePropertyChanged(() => this.student);
            }
        }

        public StudentViewModel()
        {
            student = new Student();
        }

        public bool CanSubmit
        {
            get
            {
                return true;
            }
        }

        public void Submit()
        {
            student.Mock();
        }

    }
}

    Note that the above method Submit For simplicity, the simulation method. Since the method of Mock may still be related to changes in the UI (e.g., change over certain specific variation value database), so the art may also need to inherit model Student NotificationObject, in the present embodiment, the following changes Student

  

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MVVMDemo.Model
{
    public class Student : NotificationObject
    {
        string firstName;
        public string FirstName
        {
            get
            {
                return firstName;
            }
            set
            {
                firstName = value;
                this.RaisePropertyChanged("FirstName");
            }
        }

        string lastName;
        public string LastName
        {
            get
            {
                return lastName;
            }
            set
            {
                lastName = value;
                this.RaisePropertyChanged("LastName");
            }   
        }

        public Student()
        {
            //模拟获取数据
            //这里为什么会有模拟数据一说呢?我是这样认为的,有时候类的属性会存在数据库或者本地文件系统等上面,
            //我们需要读取操作将这些数据加载到咱们定义的类里。
            Mock();
        }

        public void Mock()
        {
            FirstName = "firstName:" + DateTime.Now.ToString();
            LastName = "lastName:" + DateTime.Now.ToString();
        }
    }
}

    Second, you need to change StudentView, due to the use of the VIEW command and binding properties, we need to add two references

   

   After you add these two references, the content modification StudentView.xaml follows:

   

<UserControl x:Class="MVVMDemo.View.StudentView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
             xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 
             xmlns:vm="clr-namespace:MVVMDemo.ViewModel"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="400">
    <Grid x:Name="gridLayout">
        <Grid.DataContext>
            <vm:StudentViewModel />
        </Grid.DataContext>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="5*" />
            <ColumnDefinition Width="5*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="5*" />
            <RowDefinition Height="5*" />
            <RowDefinition Height="5*" />
            <RowDefinition Height="5*" />
        </Grid.RowDefinitions>

        <TextBlock Text="FirstName:" Grid.Row="0" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBlock Text="{Binding Path=Student.FirstName,Mode=Default}" Grid.Row="0" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left"/>
        <TextBlock Text="LastName:" Grid.Row="1" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
        <TextBox Text="{Binding Path=Student.LastName,Mode=TwoWay}" Grid.Row="1" Grid.Column="1" VerticalAlignment="Center" HorizontalAlignment="Left"/>
        <Button x:Name="BtnView" Content="I am View"  IsEnabled="{Binding CanSubmit}"  Grid.Row="2" Grid.Column="0" Width="150" Height="50" VerticalAlignment="Center" HorizontalAlignment="Right">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="Click">
                    <ei:CallMethodAction TargetObject="{Binding}" MethodName="Submit"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </Button>
    </Grid>
</UserControl>

   Compile and run, click the button BtnView, you can see the real content updates.

  The above-mentioned project code, click here to download

   6. After the statement

   After this time the reconstruction, basically meet the needs of a simple MVVM model, I also have knowledge about MVVM, but the learning process is also designed to some of the issues that I need to continue to explore, such as in the Lambda class NotificationObject expression, as well as command binding. This study notes series is not over, step by step, come on.

   The film comes from the vast majority of this article: http://www.cnblogs.com/luminji/archive/2011/05/27/2060127.html , the author expressed his thanks !!! original reason for wearing a logo, because of the knock-out line by line their own words, and then doped some of their own ideas, but also be encouraged to own it.

Published 143 original articles · won praise 161 · Views 1.21 million +

Guess you like

Origin blog.csdn.net/mybelief321/article/details/44423845