In WPF MVVM

In WPF MVVM

Model and view model

Defining the model often leads to heated debate, the boundaries between the model and view model may be blurred. Some people do not like to "pollute" their models with INotifyPropertyChanged interface, but in view of the model, it does implement this interface model attributes copied. Like many things in software development, there is no right or wrong answers.

Open view

MVVM goal is to separate these three different areas - model, view and view model. Although the view may accept the view model (VM) and (indirect) model, but the most important rule is MVVM VM should not have access to view or control. VM should be open to view everything you need through public property. VM not disclosed directly or UI controls manipulated, such as TextBox, Button like.

In some cases, this strict separation can be difficult to deal with, especially if you need to start and run some complex UI functions. Here, in view of the "code-behind" file using events and event handlers are perfectly acceptable. If it is purely a UI function, then by all means take advantage of the view of the event. These event handlers can call public methods on the VM instance - just do not pass it to the UI controls or something like that.

RelayCommand

Unfortunately, the class is not part of RelayCommand WPF framework used in this example are (should be!), But almost every WPF developer's toolkit will find it. Quick search online will show a lot of code fragments can be lifted to create your own snippets.

RelayCommand RelayCommand is a useful alternative ActionCommand, it provides as part of the Microsoft.Expression.Interactivity.Core, which provides similar functionality.

The basic example of using MVVM WPF and C #

This is a C # WPF and MVVM model example of using basic Windows desktop application use. The sample code implements a simple "user information" dialog box.

View

XAML

<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>

<TextBlock Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2" Margin="4" Text="{Binding FullName}" HorizontalAlignment="Center" FontWeight="Bold"/>

<Label Grid.Column="0" Grid.Row="1" Margin="4" Content="First Name:" HorizontalAlignment="Right"/>
<!-- UpdateSourceTrigger=PropertyChanged makes sure that changes in the TextBoxes are immediately applied to the model. -->
<TextBox Grid.Column="1" Grid.Row="1" Margin="4" Text="{Binding FirstName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Width="200"/>

<Label Grid.Column="0" Grid.Row="2" Margin="4" Content="Last Name:" HorizontalAlignment="Right"/>
<TextBox Grid.Column="1" Grid.Row="2" Margin="4" Text="{Binding LastName, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Width="200"/>

<Label Grid.Column="0" Grid.Row="3" Margin="4" Content="Age:" HorizontalAlignment="Right"/>
<TextBlock Grid.Column="1" Grid.Row="3" Margin="4" Text="{Binding Age}" HorizontalAlignment="Left"/>

</Grid>

And code behind

 public  partial  class the MainWindow: the Window 
    { 
        Private  Readonly MyViewModel _viewModel;
         public the MainWindow () 
        { 
            the InitializeComponent (); 
            _viewModel = new new MyViewModel ();
             // the DataContext binding path as a starting point 
            the DataContext = _viewModel; 
        } 
    } 
    // the INotifyPropertyChanged View property change notifications to update the bindings. 
    Sealed  class MyViewModel: the INotifyPropertyChanged 
    { 
        Private the User User; 

        public  String  FirstName
        {
            GET { return user.firstName;}
             the SET 
            { 
                IF (! user.firstName = value) 
                { 
                    user.firstName = value; 
                    onpropertychange ( " FirstName " );
                     // If the name has changed, the FullName property need to be updated. 
                    Onpropertychange ( " the FullName " ); 
                } 
            } 
        } 

        public  String the LastName 
        { 
            GET { returnUser.LastName;}
             the SET 
            { 
                IF (! User.LastName = value) 
                { 
                    User.LastName = value; 
                    onpropertychange ( " LastName " );
                     // If the name has changed, the FullName property need to be updated. 
                    Onpropertychange ( " the FullName " ); 
                } 
            } 
        } 
        // This property is how different the view model attributes presented by way of example.
        // In this case, we will convert the user's date of birth age, which is read-only. 
        public  int Age
        { 
            GET 
        {
            { 
                The DateTime Today = DateTime.Today;
                 int Age = today.Year - user.BirthDate.Year;
                 IF (user.BirthDate> today.AddYears (-AGE)) age-- ;
                 return Age; 
            } 
        } 

        // This attribute only to the display object, a component of the existing data. 
        public  String the FullName 
        { 
            GET { return FirstName + "  " + the LastName;} 
        } 

        public MyViewModel () 
            User = new new the User
            {
                FirstName = "John",
                LastName = "Doe",
                BirthDate = DateTime.Now.AddYears(-30)
            };
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChange(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }

model

   sealed class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime BirthDate { get; set; }
    }

MVVM commands

Command is used at the same time comply with WPF MVVM pattern processing in Events.

A common EventHandler looks like this (in Code-Behind):

public MainWindow()
{
       _dataGrid.CollectionChanged += DataGrid_CollectionChanged;
}    
private void DataGrid_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
      //做任何事
}

MVVM not to do the same thing we use Commands:

<Button Command="{Binding Path=CmdStartExecution}" Content="Start" />

I recommend using some sort of prefix (Cmd) for the command property, mainly because you will use them in the xaml - so they are easier to identify.

Because it is MVVM, so you want to process the command (For Button "eq" Button_Click) in ViewModel.

To this end, we basically need two things:

System.Windows.Input.ICommand
RelayCommand ( e.g. obtained from here ).
A simple example might be:

private RelayCommand _commandStart;
public ICommand CmdStartExecution
{
get
{
if(_commandStart == null)
{
    _commandStart = new RelayCommand(param => Start(), 
    param => CanStart());
}
return _commandStart;
}
}

public void Start()
{
    //...
}

public bool CanStart()
{
      return (DateTime.Now.DayOfWeek == DayOfWeek.Monday); //Can only click that button on mondays.
}

Well, this detail is what to do:

Content Control ICommand is bound in xaml. RelayCommand your route commands to Action (ie, call Method). Null-Check Command will only ensure that each initialization time (due to performance issues). If you've read RelayCommand above link, you may have noticed RelayCommand its constructor has two overloaded. (Action <object> execute) and (Action <object> execute, Predicate <object> canExecute).

This means that you can (additionally) add a second Method returns a bool to tell Control "event" are firing.

For example, if Method returns false, then the Button s will be Enabled = "false"

CommandParameters

<DataGrid x:Name="TicketsDataGrid">
<DataGrid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" 
Command="{Binding CmdTicketClick}" 
CommandParameter="{Binding ElementName=TicketsDataGrid, 
Path=SelectedItem}" />
</DataGrid.InputBindings>
<DataGrid />

In this example, I want to pass DataGrid.SelectedItem to my ViewModel in Click_Command.

The method you should look like, and to achieve ICommand itself remains as described above.

private RelayCommand _commandTicketClick;

public ICommand CmdTicketClick
{
get
{
    if(_commandTicketClick == null)
   {
       _commandTicketClick = new RelayCommand(param => 
      HandleUserClick(param));
}
return _commandTicketClick;
}
}

private void HandleUserClick(object item)
{
     MyModelClass selectedItem = item as MyModelClass;
    if (selectedItem != null)
    {
    //...
    }
}

View model

MV VM view model is the "VM". This class acts as a middleman, model disclosed to the user interface (view), and processes the request from the view, such as a button click command initiated. This is a basic view model:

public class CustomerEditViewModel
{
    /// <summary>
    /// 编辑客户
    /// </summary>
    public Customer CustomerToEdit { get; set; }

    /// <summary>
    /// “ApplyChanges”命令
    /// </summary>
    public ICommand ApplyChangesCommand { get; private set; }

    /// <summary>
    /// 构造函数
    /// </summary>
    public CustomerEditViewModel()
    {
        CustomerToEdit = new Customer
                         {
                             Forename = "John",
                             Surname = "Smith"
                         };

        ApplyChangesCommand = new RelayCommand(
            o => ExecuteApplyChangesCommand(), 
            o => CustomerToEdit.IsValid);
    }

    /// <summary>
    /// 执行 "apply changes" 命令.
    /// </summary>
    private void ExecuteApplyChangesCommand()
    {
        // For example, to save a client to the database 
    } 
}

Constructor Create Customer CustomerToEdit model object and assign attributes to it which view is visible.

The constructor creates a RelayCommand further object and assign it to ApplyChangesCommand properties to visible again on the view. WPF command processing requests from view, for example, clicking a button or menu item.

RelayCommand has two parameters - the first was commissioned (for example, in response to button clicks) called when the command is executed. The second parameter is a delegate that returns a Boolean value indicating whether the command can be executed; in this case, it is connected to IsValid property client object. If it returns false, button or menu item bound to this command is disabled (the behavior of other controls may be different). This is a simple but effective functionality without having to write code to enable or disable controls based on different criteria.

If you're really up and running this example, try clearing one of the TextBox (to the Customer model put in an inactive state). When you leave you will find TextBox "Apply" button is disabled.

View model does not implement INotifyPropertyChanged (INPC). Controls This means that if you want to assign a different Customer object to the CustomerToEdit property, the view does not change to reflect the new object - a customer's forename and surname before TextBox es will contain.

It works because the sample code in the constructor Create Customer views of the model, and then DataContext was only assigned to the view (case binding is connected). In a real application, you may be retrieved from a database of customers using a method other than a constructor. To support this, VM should realize INPC, and should change CustomerToEdit properties to use what you see in the example code Model "extended" getter and setter mode, thereby raising the PropertyChanged event in the setter.

ApplyChangesCommand view of the model need not be implemented INPC, because the command is unlikely to change. You will need to implement this mode, if you create a place other than the command structure, such as some Initialize () method.

The general rule is: if any view property is bound to the control value of the property and can be changed at any position other than the constructor is achieved INPC. If the attribute value is assigned only in a constructor, it is not necessary to achieve the INPC (and you will save some input in the process).

Model
model M VVM is the first "M". The model is usually included a class you want to pass some sort of user interface of the disclosed data.

This is a very simple model class that exposes several properties: -

public class Customer : INotifyPropertyChanged
{
    private string _forename;
    private string _surname;
    private bool _isValid;

    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// 客户姓氏
    /// </summary>
    public string Forename
    {
        get
        {
            return _forename;
        }
        set
        {
            if (_forename != value)
            {
                _forename = value;
                OnPropertyChanged();
                SetIsValid();
            }
        }
    }

    /// <summary>
    /// 客户姓氏。
    /// </summary>
    public string Surname
    {
        get
        {
            return _surname;
        }
        set
        {
            if (_surname != value)
            {
                _surname = value;
                OnPropertyChanged();
                SetIsValid();
            }
        }
    }

    ///  <Summary> 
    /// model indicating active status. ///  </ Summary> 
    public  BOOL IsValid 
    { 
        GET 
        { 
            return _isValid; 
        } 
        SET 
        { 
            IF (! _IsValid = value) 
            { 
                _isValid = value; 
                the OnPropertyChanged (); 
            } 
        } 
    } 

    ///  <Summary> 
    /// Set IsValid property value.
    ///  </ Summary> 
    Private  void SetIsValid () 
    { 
        the IsValid =!string.IsNullOrEmpty(Forename) && !string.IsNullOrEmpty(Surname);
    }

    /// <summary>
    /// 引发PropertyChanged事件。
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    private void OnPropertyChanged([CallerMemberName] string propertyName = "")
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

This class implements the INotifyPropertyChanged interface, which is open PropertyChanged event. As long as a property value is changed, it will trigger this event - you can see in the above code. PropertyChanged event is a key part of the WPF data binding mechanism, because without it, the user interface will not reflect the changes made to property values.

The model also includes a very simple validation routines may be called from the attribute setter. It is a common property set, indicating active status model. I have included this feature to demonstrate "special" feature WPF command, you will soon see it. WPF framework provides many more sophisticated authentication methods, but these methods are beyond the scope of this paper.
View
View is the MV VM in the "V". This is your user interface. You can drag and drop using the Visual Studio designer, but most developers will eventually write the original XAML code - an experience similar to writing HTML.

The following is a simple view allows editing Customer model of XAML. Instead of creating a new view, simply paste it into MainWindow.xaml file WPF project, in <Window ...> and </ Window> tags: -

<StackPanel Orientation="Vertical"
            VerticalAlignment="Top"
            Margin="20">
    <Label Content="Forename"/>
    <TextBox Text="{Binding CustomerToEdit.Forename}"/>

    <Label Content="Surname"/>
    <TextBox Text="{Binding CustomerToEdit.Surname}"/>

    <Button Content="Apply Changes"
            Command="{Binding ApplyChangesCommand}" />
</StackPanel>

This code creates a simple data entry form that contains two TextBox es - one for the customer forename, and one for the surname. Each TextBox above all a Label, a "Apply" Button bottom of the form.

Find and view it first TextBox's Text property:

Text = "{Binding CustomerToEdit.Forename}"
This particular braces TextBox text syntax than a fixed value, but bind to the text "path" CustomerToEdit.Forename. What this path with respect? It is the "data context" view - is a view of the model in this embodiment. As you might expect, the binding properties of the path is CustomerToEdit view of the model, it is the Customer Type, which in turn displays a property named Forename - so is the "line" path notation.

Similarly, if you view XAML Button, which is bound to have a view of the model ApplyChangesCommand property of Command. That's all the VM button to connect to the desired command.

DataContext

So how to view model to the data context view? One method is to set it in the "code-behind" view. Press F7 see this code file and add a line in the prior instance constructor to create a view of the model, and assigned to the DataContext properties window. It should look like this:

public MainWindow()
{
     InitializeComponent();
    DataContext = new CustomerEditViewModel();
}

In real-world systems, it is common to use other methods to create a view model, such as dependency injection or MVVM framework.

Guess you like

Origin www.cnblogs.com/yigegaozhongsheng/p/12132299.html