Posts mit dem Label WPF werden angezeigt. Alle Posts anzeigen
Posts mit dem Label WPF 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 :-)


Dienstag, 21. August 2012

Portable code between Silverlight, WPF and more

I had some talks about multi-targeting between WPF and Silverlight. The usual approach to achive this was to have one projects for each platform and use file linking to avoid duplicated code. This was working quite well but was a bit of a hassle as well.

With .NET 4.5 Micorsoft gave developers a new tool to solve the problem: portable libraries! There is a new project template called Portable Class Library. If you select it, a dialog box appears asking you which platforms should be targeted:


The selected settings can later be changed using the project properties. The class library can be directly referenced in a Silverlight project as well as in WPF, which was not possible before: an assembly targeted to the .NET Framework could not be referenced from Silverlight.

Some drawbacks...

As every nice feature, this has its drawbacks as well. Of course in the PCL project you can only use a subset of all supported plattforms. On MSDN is a list of supported features. A PCL project can not reference platform specific assemblies - it is limited to other PCL assemblies.
A major thing I miss in contrast to the old school multiple DLL with shared files approach is, that you can no longer use compiler directives to use platform specific code. Neither can you effectivly determine on what platform your code is running. On the one hand that is the idea of PCL, on the other hand it seems like a limitiation to me.

And a conclusion...

I think it all comes down to careful plannig and using the right tool for the right case. I think you can not discard the mulit-assembly approach completely if you want to get most out of mulit targetign. But PCL are a handy way to have real common code - the one that does not need to know the platform it is running on - in a common location. As suggested on MSDN, platform specific code can then be implemented in platform specific assemblies that inherit from your PCL base classes. This yields to a mixed mode between the old and the new approach.

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...