1

Using WPF my team is attempting to seperate the design and the code using the MVVM design patterns. To accomplish this, we are slowly moving away from the UserControl approach as this has a high ammount of coupling between design and code. We have been investigating using Control Templates, Styles and DataTemplates, in combination with a ViewModel class. We have gotten a large amount of this working to date.

The issues we are having are in relation to communications/notifications between View and ViewModel. At present, we have "solved" the View -> Viewmodel communication issues using ICommand. i.e. We create a button and bind its' "Command" parameter to the name of a RelayCommand defined in the ViewModel. By doing so, a button click or other Command event raised from the View calls a function defined in the ViewModel. This works.

Where we're having our main issue is in getting the notification to run the other way around: i.e. a change in data in the ViewModel needs to trigger an update in the View. We were doing this using NotifyPropertyChanged and DataTriggers, however this does not satisfy our needs. What we need is to be able to raise an event of some kind in the Viewmodel and have the View subscribe to said event. We have been searching for an answer to this and have found out about both RoutedEvents and AttachedBehaviors. RoutedEvents seemed like a winner of a solution, however from our research RoutedEvents can't be registered onto a ViewModel that doesn't extend from UIElement, and we are particularly trying to keep our code separated from the design.

Ultimately what we're trying to do is set up a ViewModel where a parameter can be set or a function can be called that will raise an event or behavior on the View, and subsequently run an animation. We did have this working with DataTriggers, however we are attempting to seperate our Animations out into the ControlTemplate and this raises issues as the DataTemplate that contains the DataTriggers has no access to the Storyboards defined in the ControlTemplate.

Can someone please point us in the right direction? Specifically the raising of an event in a ViewModel (that does not require extending UIElement) and subscribing to this event in the View and activating a Storyboard.

Thanks

  • Managed to get it working by defining the storyboard as a resource, and then binding that storyboard resource into a ControlTemplate.Triggers Datatrigger. By doing so, the NotifyPropertyChanged event triggers an update and the ControlTemplate is responsible for handling its own animations. By doing all of this, the ControlTemplate is responsible for maintaining the animations and animation state based on a property defined in the ViewModel. – user2961221 Nov 08 '13 at 15:04

6 Answers6

1

I use MVVM myself and if I want to change something on screen, I use INotifyPropertyChanged interface.

Whenever some UI element has to change, OnPropertyChanged event is called out and UI is updated.

user2667069
  • 132
  • 1
  • 1
  • 7
  • Yeah, I mentioned in the original question that I was using NotifyPropertyChanged events. However, using NotifyPropertyChanged to trigger an animation requires the use of a DataTrigger, and you cannot use the "TargetName" within a storyboard in a style. And to clarify, we do not want to embed the animations into the DataTemplate – user2961221 Nov 08 '13 at 14:38
0

The best way is to create a base view that automatically subscribes to the INotifyPropertyChanged on the VM as soon as the property is set (don't forget to unsubscribe!). Keep away from RoutedEvents (or any UI-related events) on the view model. They should only implement INotifyPropertyChanged.

Catel provides an out-of-the-box solution for you for these kind of things:

https://catelproject.atlassian.net/wiki/display/CTL/Mapping+properties+from+view+to+view+model

If you can't use Catel, just copy/paste the parts you need into your own framework. However, don't waste your time reinventing the wheel over and over again ;-)

Geert van Horrik
  • 5,689
  • 1
  • 18
  • 32
  • What exactly is the purpose of this Catel lib? As far as I can tell, it's just an alternate way of doing binding. The specific case we're looking for is to run an animation in the View when the ViewModel has a property change. I don't see how Catel helps with this. – user2961221 Nov 08 '13 at 14:36
  • Catel provides application components that are required in any application, such as logging, argument checking, service locators and more. One of these features is also MVVM where Catel provides years of experience by the developers in the MVVM world. One of the things was the (automatic) mapping of the VM to View properties and events. Note that it is not an alternative way of binding because it still uses the WPF binding system. It just gives you the tools you need to build serious MVVM apps. Let me know if I should wrap up a small example for you. – Geert van Horrik Nov 09 '13 at 00:38
0

You could also use the mediator pattern to start an animation from the viewmodel. Just send a message from the viewmodel and subscribe to that kind of message on the view.

metacircle
  • 2,438
  • 4
  • 25
  • 39
0

I use delegate to communicate from ViewModel to View. Let me explain. In my framework, I have this abstract class :

public abstract class NotificationObject : INotifyPropertyChanged
{
    // very usefull to order view (if exist) to close 
    public delegate void ActionClose(Boolean closeResult);

    public delegate void ActionUpdate();

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void RaisePropertyChanged(string propertyName)
    {
        // take a copy to prevent thread issues
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    // other implementation for RaisePropertyChange (not the point here)
    // protected virtual void RaisePropertyChanged(params string[] propertyNames)
    // protected virtual void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
}

Now, all my ViewModel can inherit from NotificationObject

public class MyViewModel : NotificationObject
{
    public MyViewModel ()
    {
        // initialisation
    }

    // bindable action UI -> VM
    public ICommand Command1 { get; set; }
    public ICommand Command2 { get; set; }

    // VM -> View
    public ActionUpdate Action1 { get; set; }
    public ActionUpdate Action2 { get; set; }

    // in this method, we need to "notify" view that it must do something (refresh, close, modify UI ...)
    private void MyMethod()
    {
        // never forget that UI can not exist (testing for exemple), or not UI (command-line process), so Action1 can be null
        if (this.Action1 != null)
        {
            this.Action1 ();
        }        
    }
}

Now, in the View, we can declare the delegates (in Window_Loaded, the ViewModel is loaded) :

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    MyViewModel context = this.DataContext as MyViewModel ;

    if (context != null)
    {
        NotificationObject.ActionUpdate myAction1 = new NotificationObject.ActionUpdate(this.MyAction1);
        context.Action1 = myAction1 ;
        NotificationObject.ActionUpdate myAction2 = new NotificationObject.ActionUpdate(this.MyAction2);
        context.Action2 = myAction2 ;
    }
}

private void MyAction1()
{
    // do something
}

private void MyAction2()
{
    // do something
}

That's all. ^^

Xaruth
  • 4,034
  • 3
  • 19
  • 26
  • While I can understand what you're suggesting, the act of binding the delegates in code behind a view expressly contradicts the approach we're trying to take. – user2961221 Nov 08 '13 at 14:34
0

A more all purpose solution that allows even complex code to be decoupled is the View Services pattern. The long and short of it is, that similar to Views, View Services are platform dependent, but unlike Views, are kind of known to the ViewModel. A practical implementation is having the ViewModels Constructor expect an Interface of the desired ViewService, while the "View" side of the code handles the implementation of said interface with View specific code like Dialogs, Notifications, etc. This works very well with Inversion of Control too. Not something that helps if there is supposed to be no code behind on View at all though.

OmarAj
  • 1
-1

I have found that MVVM isn't a very good choice for loose coupling. By default Microsoft Model-View-Whatever solution templates throw everything into one project. To be truly loosely coupled, try moving everything but the GUI elements into their own libraries and avoid all references to a specific target. A loosely coupled system would have the System.Windows or System.Web references only on the target (client/console/web serving) platform and have reusable components in model and business layer libraries. You will probably end up with some facade and adapter classes that facilitate generic implementations. I suppose it depends what you are trying to decouple. Frameworks in general add more restrictions. I know that it's not a very popular viewpoint, but that's my 2 cents.

CZahrobsky
  • 762
  • 7
  • 7