Generically Typed ViewModelBase

Oct 15, 2010 at 5:56 AM
Edited Oct 15, 2010 at 5:59 AM

I am building an Mvvm base class library for use in lots of future Silverlight data-entry projects. Once it's done, I will put it up on CodePlex, but for now I would like to know what you think about this suggestion.

Let me show you what I am doing in my kit. Here is the basic structure for my base ViewModel class:

 

using System;
using System.Collections.Generic;
using System.ComponentModel;

namespace Dc.Mvvm
{

	public interface IViewModel : INotifyPropertyChanged { }

	public abstract class ViewModel : IViewModel
	{
		// Code for PropertyChanged, Diagnostics (Log), DependsOn
		// etc.
	}

	public abstract class ViewModel<TViewModel> : ViewModel
			where TViewModel : ViewModel<TViewModel>
	{
		// Small amount of code for PropertyChanged and Log
	}

}

So first, I have a base interface and you may be wondering what this has to do with the suggestion for a generic ViewModelBase. It is part of the motif that I'm developing into this kit. You are not forced to use the base classes here (ViewModel and ViewModel<TViewModel>) and you can still take advantage of some of the kit's code (not shown) by just implementing IViewModel. Similarly, you don't have to inherit from ViewModel<TViewModel> if you have some reason not to. (I actually have 2 read only properties defined on my IViewModel interface, but I'm considering removing them.)

Most of the implementation code is in my ViewModel class. As you can see below, only a small part of the implementation is in the generically typed ViewModel<TViewModel> class. Here is my entire generic base class as of right now:

 

public abstract class ViewModel<TViewModel> : ViewModel
	where TViewModel : ViewModel<TViewModel>
{
	protected ViewModel() : base(typeof(TViewModel)) { }
	
	#region PropertyChangeEventArgs

	// Static dictionary of property changed eventargs.
	static readonly Dictionary<string, PropertyChangedEventArgs>
		_propChangedArgs = new Dictionary<string, PropertyChangedEventArgs>(4);

	static PropertyChangedEventArgs GetStaticPropertyChangedArgs(string propertyName)
	{
		PropertyChangedEventArgs args;
		if (!_propChangedArgs.TryGetValue(propertyName, out args))
		{
			args = new PropertyChangedEventArgs(propertyName);
			_propChangedArgs.Add(propertyName, args);
		}
		return args;
	}

	// The ViewModel base class has it's own implementation of this which simply creates
	// a new PropertyChangedEventArgs. This one returns from the static collection.
	internal override PropertyChangedEventArgs GetPropertyChangedArgs(string propertyName)
	{
		return GetStaticPropertyChangedArgs(propertyName);
	}

	#endregion

	#region Diagnostics

	// I have a logging interface which wraps Log4Net in my full .net apps.
	// I ported it to Silverlight and so I'm using it here to get a log
	// for the concrete class type.
	static readonly Dc.Diagnostics.ILog _Log = Dc.Diagnostics.LogManager.GetLogger(typeof(TViewModel));
	// The ViewModel base class has it's own implementation of this as well,
	// but here we'll return a logger that outputs the concrete type name with every entry.
	protected override Dc.Diagnostics.ILog Log { get { return _Log; } }

	#endregion
	
	// Besides being able to manage static resources per concrete type,
	// the generic ViewModel base allows you to add Fluent style methods
	// to your base implementation if you like:
	public TViewModel DoSomething() { OnDoSomething(); return (TViewModel)this; }
}
// Example of inheriting from the generic base:
public class HomeViewModel : ViewModel<HomeViewModel>
{
	
}

 

I have not ported ViewModelSupport's DependsOn implementation into my base class yet, but when I do, I will make all of the maps static in the ViewModel<TViewModel> class, similar to what is being done with the _propChangedArgs static dictionary.

What do you think? I don't know if anyone is actively updating ViewModelSupport, but maybe you'd like to consider it.

 

Oct 18, 2010 at 11:05 PM

Just wanted to leave an update on this in case anyone was misled into using the proposed idea. I scrapped the generically typed ViewModel base class in favor of a simpler object model. I remembered a better way to store metadata about an instance's concrete type.

Basically, I implemented usage of a new internal class called TypeMetadata which contains a static dictionary of TypeMetadata objects keyed by type. TypeMetadata is lazy loaded in the constructor of the base ViewModel class via _meta = TypeMetadata.GetOrAdd(GetType());. So each instance of a given ViewModel class type now has a private _meta field that all reference the same static instance of TypeMetadata for that type. TypeMetadata contains a property called ObjectType which is it's key and then it has all sorts of properties for anything you need to know about that view model type. (For instance, Cache of PropertyChangedEventArg instances keyed by name, A lazy loaded logger object for that type (think Log4Net LogManager.GetLogger(ObjectType)) and property/method/command dependencies. This feels much better.

Feb 8, 2011 at 10:22 AM

I don't understand.

I am looking for a way to eliminate the need for proxy properties.

Instead I want the ViewModel to automatically expose all the properties of the model except those marked [DisplayAttribute(AutoGenerateField=False)].

Any breakthru? please show your example along with the XAML usage of the binding.