Posts mit dem Label MVVM werden angezeigt. Alle Posts anzeigen
Posts mit dem Label MVVM werden angezeigt. Alle Posts anzeigen

Donnerstag, 2. Mai 2013

A nice new feature for your ViewModel base class

Back in June 2010 I blogged about a View Model base class for WPF and Silverlight. With slight modifications I still use it today when I have to deal with MVVM.

With .NET 4.5 however, Microsoft added an new feature to the .NET Framework, that can make writing View Models even sweeter: the [CallerMemeberName]-Attribute.

Just a short recall: the view model base class uses two Methods Get and Set to manage property values. In your view model you can write something like

public class MyViewModel : ViewModelBase
{

  public string Name
  {
    get { return Get(() => Name); }
    set { Set(() => Name, value); }
  }

}

This way you get strongly typed properties in your view model, but you get rid of the backing field. And the base class takes care about change notifications via INotifyPropertyChanged. But to many people the lambda expression () => Name was confusing and hard to understand.Would it not be sweet if we could derive the calling property name from the context?

This was possible ever since by using the StackFrame class like this:

var stackTrace = new StackTrace();
var frame = stackTrace.GetFrame(1);
var callerName = frame.GetMethod().Name;

But this approach seems a bit cumbersome and has performance penalties, becuase building up the stack trace is an expensive operation. And for a property setter the the delivered method name is like "set_propertyName" - so additional work is needed to fiddle out the pure method name.

Thing get more handy with the new [CallerMemeberName] attribute. You can place it on a methods string parameter and this parameter will be filled with the name of the caller. Bummer. So your OnPropertyChanged-Method can look like this:

protected void RaiseNotifyPropertyChanged([CallerMemberName] string propertyName = "")
{
  if (PropertyChanged != null)
    PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}

This can be incorporated easily in our view model base class. It will look like this (a lot of stuff is ommited for brevity):

public class ViewModelBase : INotifyPropertyChanged
{
  public event PropertyChangedEventHandler PropertyChanged;

  private Dictionary<string, object> _value = new Dictionary<string, object>(); 

  protected T Get<T>([CallerMemberName] string callerMemberName = "")
  {
    if (!_value.ContainsKey(callerMemberName))
      return default(T);

    return (T)_value[callerMemberName];
  }

  protected void Set<T>(T value, [CallerMemberName] string callerMemberName = "")
  {
    if (!_value.ContainsKey(callerMemberName))
      _value.Add(callerMemberName, value);

      _value[callerMemberName] = value;

    RaiseNotifyPropertyChanged(callerMemberName);
  }

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

We still have our methods Get / Set around, but they get an additional optional parameter for the caller member name. With this at hand, our above property example looks like this:

public class MyViewModel : ViewModelBase
{

  public string Name
  {
    get { return Get<string>(); }
    set { Set(value); }
  }

}

The one thing to do now, is explicitly stating the return type in the Get()-Method, because it can no longer be derived from the lambda expression. But you never get anything for free, right? Another downside is, that the attribute is not available in Silverlight.

It's a kind of magic - impress your colleagues with this one :-)


Mittwoch, 13. Juni 2012

Get hold of the view in the view model

The Problem


Triggered by a tweet from Vaughn Vernon about the question on how to get a reference to the view in a view model I decided to post the solution I used for this.

In general it is not a good practice though, but I needed it sometime as well (mostly because of some part of the system that was not designed with MVVM in mind).

The Solution


I defined an interface for the view and let the view implement it. The interface contains a a reference to the view model through an interface. The view model resides in the views DataContext so we route the property to this:

public interface IView
{ 
  IViewModel ViewModel { get; set; }
}

public partial class View : UserControl, IView
{ 
  public IViewModel ViewModel 
  { 
    get { return DataContext as IViewModel; }
    set { this.DataContext = value; } 
  }
}

The interface for the view model contains a reference to the view through its interface.

public interface IViewModel
{ 
    IView View { get; set; }
}

public class ViewModel : VMBase, IViewModel
{ 
    public IView View { get; set; }
}

Now in the XAML of the view I wire up the view model

<UserControl>
    <UserControl.DataContext>
        <local:ViewModel />
    </UserControl.DataContext>
</UserControl>

In the loaded-event of the view I set the reference back to the view model:

public void Control_Loaded(Object sender, EventArgs e)
{
  if (ViewModel != null)
    ViewModel.View = this;
}

And thats it.

What I like about this solution is that your view / view model know each other only by means of an interface, which gives you a certain amount of decoupling. But it is quite a bit of additional code...

The Drawbacks

As I mentioned earlier it is generally not a good practice to try to do this. Mostly it points to the fact that the system is not MVVMable - but sometimes reality knocks at the door...
A problem with this apporach is timing. If you need the view reference say in the constructor of your view model you wont be successful with the above technique. But you can use it in commands or any time after the view was loaded which was always sufficient for me.
Finally I don't claim that this is a general purpose solution to all cases one might want to use this - it is just what did the job for me.

Maybe it might be helpful to someone else...





Donnerstag, 17. Juni 2010

A great MVVM view model base class for SL4 - Part II

In my previous post I mentioned the view model base class of Brian Genisio. It uses a dictinary as a value storage for your view models properties. In your view model you write the following:

public class MyViewModel : ViewModelBase
{
  public string Name
  {
    get { return Get(() => Name); }
    set { Set(() => Name, value); }
  }
}

The expression "() => Name" is used instead of the string "Name" to identify the property in a strong typed manner (that means you could have also written Get("Name") instead).

All this works fine as long as your view model orignially provides all the values you need. In my case I have view models that use model classes as data storage like so:

public class MyViewModel : ViewModelBase
{
  private Person m_Person;
  public string Name
  {
    get { return m_Person.Name; }
    set
    {
      if (m_Person.Name != value)
      {
        m_Person.Name = value;
        OnPropertyChanged("Name");
      }
    }
  }
}

That forces me to write all the set-code myself again. My idea to be able to use all the base class goodies again was to add an additional but optional business object to the base class as well as some new overloads to the Get / Set methods:

public class ViewModelBase : INotifyPropertyChanged
{
  private Object m_BusinessObject;

  public ViewModelBase() : this(null)
  { }

  public ViewModelBase(Object BusinessObject)
  {
  m_BusinessObject = BusinessObject;
  ...
  }

  protected T Get<TBusniessObject, T>(Expression<Func<T>> expression, T DefaultValue)
  {
    if (m_BusinessObject == null)
      return DefaultValue;

    String propName = GetPropertyName(expression);
    return (T)m_BusinessObject.GetType().GetProperty(propName).GetValue(m_BusinessObject, new object[] { });
  }

  protected void Set<TBusinessObject, T>(Expression<Func<T>> expression, T value)
  {
    if (m_BusinessObject == null)
      return;

    if (Get<TBusinessObject, T>(expression).Equals(value))
      return;

    String propName = GetPropertyName(expression);
    m_BusinessObject.GetType().GetProperty(propName).SetValue(m_BusinessObject, value, new object[] { });
    Set(propName, value);
  }

}


The idea is simple: as long as the business object is not null we get / set the value out of it via reflection. In the setter we first compare the current value with the acutal one to leave if nothing has changed. After having set the value to the business object, I pass the value through to the normal Set method in order to get all the other gadgets like dependent methods working.

With that code in place I am able to write the Property from my above example like so:

public class MyViewModel : ViewModelBase
{
  private Person m_Person;
  public string Name
  {
    get { Get<Person, string>(() => Name); }
    set { Set<Person, string>(() => Name, value); }
  }
}

Brians code is at codeplex. My suggestions can easily be incorporated.

Have fun.