Skip to content
Jan M edited this page Aug 12, 2020 · 41 revisions

Overview

In this package contains default MVVM implementations like PropertyChanged and some "convenience implementations" to make life a little easier.

BindingBase

The BindingBase is our default INotifyPropertyChanged implementation.

  • implementation of INofityPropertyChanged
  • convenient methods GetValue and SetValue to easily declare a property and use INotifyPropertyChanged
  • INotifyPropertyChanged for nested classes
  • INotifyPropertyChanged for dependent properties (e.g. update message for Age property when DateOfBirth has changed)
  • controlling the INotifyPropertyChanged events via attributes
  • automatically updating ICommand.CanExecute if there is any ICommand in the class

How to create a property

public class Model :
    BindingBase
{
    // simple property with PropertyChanged support
    public string Name
    {
        get => return GetValue<string>();
        set => SetValue(value);
    }

    // add custom PropertyChanged handler
    public DateTime DateOfBirth
    {
        get => return GetValue<DateTime>();
        set => SetValue(value, OnDateOfBirthChanged);
    }
    private void OnDateOfBirthChanged (object sender, PropertyChangedEventArgs eventArgs) { ... }

    // add custom PropertyChanged handler
    // and custom PropertyChanging handler
    public Guid Id
    {
        get => return GetValue<Guid>();
        set => SetValue(value, OnIdChanged, OnIdChanging);
    }
    private void OnIdChanged (object sender, PropertyChangedEventArgs eventArgs) { ... }
    private void OnIdChanging (object sender, PropertyChangingEventArgs eventArgs) { ... }

    // add custom PropertyChanged action
    public string Url
    {
        get => return GetValue<string>();
        set => SetValue(value, (url) => 
            {
                if (!url.EndsWith('/'))
                    url += "/";
            });
    }
}

Using our ICommand implementations

The namespace of our Commands is CodeMonkeys.MVVM.Commands, so your using directive would look like this:

using CodeMonkeys.MVVM.Commands;

1. Keeping it simple: Command

public class LoginViewModel
{
    public string UserName
    {
        get => return GetValue<string>();
        set => SetValue(value);
    }

    public string Password
    {
        get => return GetValue<string>();
        set => SetValue(value);
    }

    public ICommand ClearUserNameCommand => new Command(() => UserName = string.Empty);
    public ICommand ClearPasswordCommand { get; private set; }    

    public LoginViewModel()
    {
        ClearPasswordCommand = new Command(ClearPasswordField);
    }

    private void ClearPasswordField() { ... }
}

2. Defining the parameter type: Command<TParameter>

public class ItemsListViewModel
{
    public IList<Item> Items
    {
        get => return GetValue<IList<Item>>();
        set => SetValue(value);
    }

    public ICommand SelectItemCommand => new Command<Item>((item) => SelectItem(item));   

    private void SelectItem (Item selectedItem) { ... }
}

3. Want to do some asynchronous stuff? AsyncCommand

public class LoginViewModel
{
    public ICommand LoginCommand => new AsyncCommand(Login);   

    private async Task Login () { ... }
}

4. Async and defined: AsyncCommand<TParameter>

public class ItemsListViewModel
{
    public IList<Item> Items
    {
        get => return GetValue<IList<Item>>();
        set => SetValue(value);
    }

    public ICommand ShowItemDetailsCommand => new AsyncCommand<Item>((item) => ShowItemDetails(item));   

    private async Task ShowItemDetails (Item selectedItem) { ... }
}

Attributes

In the namespace CodeMonkeys.MVVM.Attributes we provide you some functionality to adapt the INotifyPropertyChanged process to your needs:

Target: Property
Can use multiple times: yes
Is inherited: yes

Indicates that this property's value relies on some other property (or properties).
This is the case for calculated properties like age.

In the following example, whenever DateOfBirth changes, the PropertyChanged event is also raised for Age.

public class Person :
    ModelBase
{
    public DateTime DateOfBirth
    {
        get => return GetValue<DateTime>();
        set => SetValue(value);
    }

    // we want a PropertyChanged event for Age whenever the DateOfBirth changes
    [DependsOn(nameof(DateOfBirth)]
    public int Age
    {
        get => return (DateTime.Now - DateOfBirth).Years;
    }
}

Target: Property
Can use multiple times: no
Is inherited: yes

Use this attribute when you want PropertyChanged support for a property, but dont want it to affect your model's IsDirty flag.

public class Item
{
    // IsDirty should not be set to true when IsSelected changes
    [DontAffectIsDirty]
    public bool IsSelected
    {
        get => return GetValue<bool>();
        set => SetValue(value);
    }
}

Target: Class, Property
Can use multiple times: no
Is inherited: yes

This attribute prevents PropertyChanged events from being raised. You can use it on both classes and properties and can even disable the event for a whole class but exclude one property.

public class Model
{
    // we dont want to raise a PropertyChanged event when Name changes
    [SuppressNotifyPropertyChanged]
    public string Name
    {
        get => return GetValue<string>();
        set => SetValue(value);
    }
}
-----------------------------------------------------------------------------
// we dont want to raise a PropertyChanged event for any of Model's properties
[SuppressNotifyPropertyChanged]
public class Model
{
    ...
}
-----------------------------------------------------------------------------
// we dont want to raise a PropertyChanged event for any of Model's properties...
[SuppressNotifyPropertyChanged]
public class Model
{
    // ... but we want one for Name
    [SuppressNotifyPropertyChanged(false)]
    public string Name
    {
        get => return GetValue<string>();
        set => SetValue(value);
    }
}
Clone this wiki locally