Defining a ViewModel
A ViewModel should be a ObservableObject
How Avalonia Do by Default
Default template of avalonia.mvvm
generates a class ViewModelBase
as base observable type to share across the project.
cs
using CommunityToolkit.Mvvm.ComponentModel;
namespace ToDoList.ViewModels;
public class ViewModelBase : ObservableObject // nah I don't like it.
{
}
1
2
3
4
5
6
7
2
3
4
5
6
7
But this is rather confusing for beginners. so manually inherit from ObservableObject
is a good start.
cs
using CommunityToolkit.Mvvm.ComponentModel;
namespace ToDoList.ViewModels;
public partial class MainWindowViewModel : ObservableObject // [!code highlight]
{
}
1
2
3
4
5
6
7
2
3
4
5
6
7
What's behind ObservableObject
ObservableObject
is both INotifyPropertyChanged
and INotifyPropertyChanging
, implementing the two events.
cs
public abstract class ObservableObject : INotifyPropertyChanged, INotifyPropertyChanging { /* ... */ }
1
There's also a SetProperty
method for performing the observer pattern. Notice a ref
modifier appears here, it's just for making sure we can bind a value type instance.
cs
protected bool SetProperty<T>([NotNullIfNotNull(nameof(newValue))] ref T field, T newValue, [CallerMemberName] string? propertyName = null)
{
// We duplicate the code here instead of calling the overload because we can't
// guarantee that the invoked SetProperty<T> will be inlined, and we need the JIT
// to be able to see the full EqualityComparer<T>.Default.Equals call, so that
// it'll use the intrinsics version of it and just replace the whole invocation
// with a direct comparison when possible (eg. for primitive numeric types).
// This is the fastest SetProperty<T> overload so we particularly care about
// the codegen quality here, and the code is small and simple enough so that
// duplicating it still doesn't make the whole class harder to maintain.
if (EqualityComparer<T>.Default.Equals(field, newValue)) // [!code highlight]
{ // [!code highlight]
return false; // [!code highlight]
} // [!code highlight]
OnPropertyChanging(propertyName);
field = newValue;
OnPropertyChanged(propertyName);
return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
NOTE
There's multiple overloads of SetProperty
, it's just one of the most commonly used.