I really love Expression Blend now that we have version 3. I have been spending quite a bit of time in the prototyping tools and been thinking about ways I can get some of those goodies to integrate with real world solutions. One of the big pain points when implementing MVVM in Silverlight (and WPF for that matter) is you need to invoke some logic on the ViewModel. In MVVM, the idea is to eliminate tight coupling between the View and the ViewModel but many times there is no simple way to get something to fire on the ViewModel, particularly if you are trying to respond to events that are not command sources.

The ChangePropertyAction is an excellent way to invoke some actions within the View (like kick off some animations as described in Adam Kinney’s post on the subject) but there isn’t a good way to invoke anything on the ViewModel.

So I said to myself, “It sure would be great if I could use that same couple lines of XAML to loosely associate an event with a method on my ViewModel” and set off to find a solution. It was actually very simple. I was able to re-use the ChangePropertyAction class and just by adding a dependency property for the method on the ViewModel I wanted to invoke and overriding the ChangePropertyAction.Invoke method.

See my code below:

    public class InvokeViewModelMethod : ChangePropertyAction
    {
        #region MethodName
        public static readonly DependencyProperty MethodNameProperty = 
            DependencyProperty.Register(
                "MethodName", 
                typeof(string), 
                typeof(ChangePropertyAction), 
                new PropertyMetadata(new PropertyChangedCallback(OnMethodNamePropertyChanged))
                );
        public string MethodName
        {
            get { return GetValue(MethodNameProperty) as string; }
            set { SetValue(MethodNameProperty, value); }
        }
        private static void OnMethodNamePropertyChanged(object sender, DependencyPropertyChangedEventArgs args) 
        {
        }
        #endregion

        protected override void Invoke(object parameter)
        {
            base.Invoke(parameter);

            // Obtain a reference to the associated FrameworkElement
            FrameworkElement associatedElement = this.AssociatedObject as FrameworkElement;
            // Obtain a reference to the DataContext object
            object dataContext = associatedElement.DataContext;

            // Grab the DataContext object's type
            Type viewModelType = dataContext.GetType();
            // Make sure we have the method specified by our "MethodName" dependency property
            MethodInfo targetMethod = viewModelType.GetMethod(MethodName);
            /* TODO: Add another dependency property of type IEnumerable to support passing 
             * input parameters to the view model method. 
             * 
             *  *May not be necessary since the ViewModel already has all the state we need. 
             *  
             */
            object[] parameters = new object[] { };
            // Invoke the method on the ViewModel
            targetMethod.Invoke(dataContext, parameters);
        }
    }