How to Write a Viewmodelbase in Mvvm

How to write a ViewModelBase in MVVM

It's worth nothing to use MVVM frameworks if you don't know what's going on inside.

So let's go step by step and build your own ViewModelBase class.

  1. ViewModelBase is class common for all your viewmodels. Let's move all common logic to this class.

  2. Your ViewModels should implement INotifyPropertyChanged (do you understand why?)

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

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    }

    the [CallerMemberName] attribute is not required, but it will allow you to write:
    OnPropertyChanged(); instead of OnPropertyChanged("SomeProperty");, so you will avoid string constant in your code. Example:

     public string FirstName
    {
    set
    {
    _firstName = value;
    OnPropertyChanged(); //instead of OnPropertyChanged("FirstName") or OnPropertyChanged(nameof(FirstName))
    }
    get{ return _firstName;}
    }

    Please note, that OnPropertyChanged(() => SomeProperty) is no more recommended, since we have nameof operator in C# 6.

  3. It's common practice to implement properties that calls PropertyChanged like this:

     public string FirstName
    {
    get { return _firstName; }
    set { SetProperty(ref _firstName, value); }
    }

    Let's define SetProperty in your viewmodelbase:

     protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
    {
    if (EqualityComparer<T>.Default.Equals(storage, value))
    return false;
    storage = value;
    this.OnPropertyChanged(propertyName);
    return true;
    }

    It simply fires PropertyChanged event when value of the property changes and returns true. It does not fire the event when the value has not changed and returns false. The basic idea is, that SetProperty method is virtual and you can extend it in more concrete class, e.g to trigger validation, or by calling PropertyChanging event.

This is pretty it. This is all your ViewModelBase should contain at this stage. The rest depends on your project. For example your app uses page base navigation and you have written your own NavigationService for handling navigation from ViewModel. So you can add NavigationService property to your ViewModelBase class, so you will have access to it from all your viewmodels, if you want.

In order to gain more reusability and keep SRP, I have class called BindableBase which is pretty much the implementation of INotifyPropertyChanged as we have done here. I reuse this class in every WPF/UWP/Silverligt/WindowsPhone solution because it's universal.

Then in each project I create custom ViewModelBase class derived from BindableBase:

public abstract ViewModelBase : BindableBase
{
//project specific logic for all viewmodels.
//E.g in this project I want to use EventAggregator heavily:
public virtual IEventAggregator () => ServiceLocator.GetInstance<IEventAggregator>()
}

if I have app, that uses page based navigation I also specify base class for page viewmodels.

public abstract PageViewModelBase : ViewModelBase
{
//for example all my pages has title:
public string Title {get; private set;}
}

I could have another class for dialogs:

public abstract DialogViewModelBase : ViewModelBase
{
private bool? _dialogResult;

public event EventHandler Closing;

public string Title {get; private set;}
public ObservableCollection<DialogButton> DialogButtons { get; }

public bool? DialogResult
{
get { return _dialogResult; }
set { SetProperty(ref _dialogResult, value); }
}

public void Close()
{
Closing?.Invoke(this, EventArgs.Empty);
}
}

MVVM - Is there an open source View Model base class?

Microsoft Prism. Full access to the source code. A bit of a steep learning curve but once you get a handle of it, it works really well.

Using base View Model Class with GalaSoft MVVM Light

You are trying to access a Non Static Method From a Static Method... It does not have access to this value, you have to make your method non static.

here is a web page which explains about static methods if you want to have a better understanding of why you can't do what you are trying to do.

Link

Is it a correct approach to create static viewModel in MVVM?

Your explanations are not enough for a precise answer.

To do this, we need to know why you need a global reference to the ViewModel instance.

Therefore, I will describe several options.

1) If:

  • in general, in principle, under no circumstances is it assumed that a ViewModel can have several instances at the assembly level in which it is created;
  • if this does not create any security problems, since the static instance can be accessed by everyone;
  • if static values are sufficient to create a single instance. In most cases, this means that the ViewModel has only one non-parameterized constructor.

Then in this case it is worth using Singleton.

Example:

public class MainWindowViewModel : ViewModelBase
{
// The only instance available outside of this class.
public static MainWindowViewModel Instanse { get; }
= new MainWindowViewModel();

// All constructors must be MANDATORY HIDDEN.
private MainWindowViewModel()
{
// Some code
}

// Some code
}

To get this instance in XAML, x: Static is used.

You can get the entire instance, or create a binding to a separate property.

<SomeElement
DataContext="{x:Static vm:MainWindowViewModel.Instance}"/>
<SomeElement
Command="{Binding ButtonCommandEvent,
Source={x:Static vm:MainWindowViewModel.Instance}}"/>

2) If:

  • ViewModel is in another assembly, it has open constructors, but the current assembly needs only one instance of it;
  • if this does not create any security problems, since the static instance can be accessed by everyone;
  • if static values ​​are sufficient to create a single instance. In most cases, this means that the ViewModel has only one non-parameterized constructor.

In this case, you should use a static class with a single instance.

This static class is created in the current assembly.

Usually it is a View project.

Your "class Globals" is an example of such an implementation.

Example usage in XAML:

<SomeElement
DataContext="{x:Static local:Globals.MainWindowViewModel}"/>
<SomeElement
Command="{Binding ButtonCommandEvent,
Source={x:Static local:Clobals.MainWindowViewModel}}"/>

3) If:

  • instances of ViewModel can replace each other, but only one instance is used at a time;
  • if this does not create any security problems, since the static instance can be accessed by everyone.

In this case, it is worth using a static class with the current instance of the ViewModel and providing notification of the replacement of this instance.

An example of such an implementation:

public static class Globals
{
private static MainWindowViewModel _mainWindowViewModel = new MainWindowViewModel();

public static MainWindowViewModel MainWindowViewModel
{
get => _mainWindowViewModel;
set => Set(ref _mainWindowViewModel, value);
}
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
private static void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
StaticPropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}

private static void Set<T>(ref T propertyFiled, in T newValue, [CallerMemberName] in string propertyName = null)
{
if (!Equals(propertyFiled, newValue))
{
propertyFiled = newValue;
RaisePropertyChanged(propertyName);
}
}
}

Example usage in XAML:

<SomeElement
DataContext="{Binding Path=(local:Globals.MainWindowViewModel)}"/>
<SomeElement
Command="{Binding Path=(local:Globals.MainWindowViewModel).ButtonCommandEvent}"/>

4) If this is only needed for all windows in the application, then it is better to instantiate the ViewModel in the App Resources.

An example of such an implementation:

<Application x:Class="***.App"
---------------------
--------------------->
<Application.Resources>
<local:MainWindowViewModel x:Key="mainViewModel"/>
</Application.Resources>
</Application>

Example usage in XAML:

<SomeElement
DataContext="{StaticResource mainViewModel}"/>
<SomeElement
Command="{Binding ButtonCommandEvent,
Source={StaticResource mainViewModel}}"/>

5) If this is needed for all windows in the application, but you need the ability to replace an instance, or to create an instance you need a constructor with parameters that are calculated after the application starts, then it is better to create an additional class that will provide this instance and other data necessary for all Windows.

An example of such an implementation:

public class Locator : INotifyPropertyChanged
{
private MainWindowViewModel _mainWindowViewModel = new MainWindowViewModel();

public MainWindowViewModel MainWindowViewModel
{
get => _mainWindowViewModel;
set => Set(ref _mainWindowViewModel, value);
}

#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(null, new PropertyChangedEventArgs(propertyName));
}

private void Set<T>(ref T propertyFiled, in T newValue, [CallerMemberName] in string propertyName = null)
{
if (!Equals(propertyFiled, newValue))
{
propertyFiled = newValue;
RaisePropertyChanged(propertyName);
}
}
#endregion
}
<Application x:Class="***.App"
---------------------
--------------------->
<Application.Resources>
<local:Locator x:Key="locator">
<local:Locator.MainWindowViewModel>
<local:MainWindowViewModel/>
</local:Locator.MainWindowViewModel>
</local:Locator>
</Application.Resources>
</Application>

Or:

<Application x:Class="***.App"
---------------------
---------------------
Startup="OnStartup">
<Application.Resources>
<local:Locator x:Key="locator"/>
</Application.Resources>
</Application>
public partial class App : Application
{

private void OnStartup(object sender, StartupEventArgs e)
{
Locator locator = (Locator)Resources["locator"];

// Some Code

locator.MainWindowViewModel = new MainWindowViewModel(/* Some Parameters*/);
}
}

Example usage in XAML:

<SomeElement
DataContext="{Binding MainWindowViewModel,
Source={StaticResource locator}}"/>
<SomeElement
Command="{Binding MainWindowViewModel.ButtonCommandEvent,
Source={StaticResource locator}"/>

With regard to the "Communication" class.
It does not work directly with UI elements, so there is no need to use a "DispatcherTimer" in it.

The main thread (namely it the DispatcherTimer uses) performs many of its tasks and does not need to load it with your tasks own unnecessarily.

Replace DispatcherTimer with any asynchronous timer:
System.Timers.Timer, System.Threading.Timer, etc.

Microsoft's ViewModelBase class for MVVM?

If you take a trip up the namespace hierarchy

http://msdn.microsoft.com/en-us/LIBRARY/bb130146.aspx

You can see these types are designed for use when extending Team Foundation Server. So, no, this is not meant to be used for your WPF applications.

However, you probably could create WPF applications which use these types. But then, I'm not sure if the licensing for these assemblies would allow you to deploy them on a machine without TFS, or if you can deliver the assemblies outside of the SDK installer. A quick search didn't reveal anything. Probably the SDK has licensing details, which I don't currently have installed.

Assuming you can and they do, does the effort this has already cost overcome replacing code which is, essentially, trivial to write? Or that has been written tens of times in different frameworks, practically all of which are available through NuGet?

MVVM Light Locator: Register all derivates of ViewModelBase

Like usually, formulating a proper question required me dig in deeper, so after some time I came up with this code for the ViewModelLocator constructor. And I would like to share it, since I feel this is an at least somewhat common MVVM Light problem that I haven't found any solution to, yet.

  • First, all types of ViewModelBase are retrieved from all assemblies.
  • Then, we need to get the Register method of SimpleIoc. For now, I have no better idea than to "find" it using .Where. Please feel free to improve this statement!
  • Finally, I used MakeGenericMethod with the type of the ViewModelBase derivate.

Type[] viewModels = AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(assembly => assembly.GetTypes())
.Where(type => typeof(ViewModelBase).IsAssignableFrom(type) && type != typeof(ViewModelBase))
.ToArray();

MethodInfo registerMethod = typeof(SimpleIoc)
.GetMethods()
.Where(method => method.Name == nameof(SimpleIoc.Default.Register) && method.GetParameters().Length == 0 && method.GetGenericArguments().Length == 1)
.First();

foreach (Type viewModel in viewModels)
{
registerMethod
.MakeGenericMethod(viewModel)
.Invoke(SimpleIoc.Default, new object[0]);

...And with the properties at the top of the ViewModelLocator class: I don't think it can be done with reflection and misusing XAML for this doesn't seem like good practice to me. But, to make life easier, I created a simple method.

The singleton parameter defines whether or not the instance should be single or get a unique key each time. I defined the MainWindow as singleton in my application and the other ViewModels are not singletons in my scenario.

public WindowMainViewModel WindowMain => GetInstance<WindowMainViewModel>(true);
public WindowAboutViewModel WindowAbout => GetInstance<WindowAboutViewModel>(false);

private static TService GetInstance<TService>(bool singleton)
{
return ServiceLocator.Current.GetInstance<TService>(singleton ? null : Guid.NewGuid().ToString());
}


Related Topics



Leave a reply



Submit