Facebook Like Box

Main Menu

Event to Command binding using Attached properties in plain WPF without any extra dependancy

When you are developing your application in WPF using MVVM, many times you feel need for handling events in View Model. No doubt the Expression blend interactivity libraries included in Expression blend SDK provide a very simple way to achieve this using behaviors. but what if you don't have such possibility. Then It is NOT a dead end, there are 

 other ways through which you can achieve the same functionality.

I will explain such example on DatePicker in WPF. DatePicker has an event named "CalendarOpened" which is fired when Calender popup is being opened. I will bind this event to a Command in viewmodel.

This example uses Attached properties to achieve this functionality. Attached properties are used to add some extra functionality in existing WPF controls. 

First of all I will write a behavior to register "CalendarOpened" event for a given DatePicker

1- The Attached Property 

public class DateControlBehavior
{
  #region Attached property
  public static ICommand GetCalenderOpenCommand(DependencyObject obj)
  {
  	return (ICommand)obj.GetValue(CalenderOpenCommandProperty);
  }
  public static void SetCalenderOpenCommand(DependencyObject obj, ICommand value)
  {
  	obj.SetValue(CalenderOpenCommandProperty, value);
  }
  // Using a DependencyProperty as the backing store for CalenderOpenCommand. This enables animation, styling, binding, etc...
  public static readonly DependencyProperty CalenderOpenCommandProperty =
  DependencyProperty.RegisterAttached("CalenderOpenCommand", typeof(ICommand), typeof(DateControlBehavior), new PropertyMetadata(null,OnCalenderOpened));
  #endregion
  #region Attached property handler
  private static void OnCalenderOpened(DependencyObject d, DependencyPropertyChangedEventArgs e)
  {
	var datePicker = d as DatePicker;
	if (datePicker != null)
	{
	  if (e.NewValue != null)
	  {
		//attach event handler
		datePicker.CalendarOpened += datePicker_CalendarOpened;
	  }
	  else
	  {
		//detach event handler
		datePicker.CalendarOpened -= datePicker_CalendarOpened;
	  }
	}
  }
  ///
  /// Event handler for Calender Opened event.
  ///
  ///
  ///
  static void datePicker_CalendarOpened(object sender, RoutedEventArgs e)
  {
	ICommand command = GetCalenderOpenCommand(sender as DependencyObject);
	if (command != null)
	{
	  if (command.CanExecute(e))
	  {
		//executes a command
		command.Execute(e);
	  }
  	}
  }
  #endregion
}

The above class is a behavior for adding CalenderOpen event to command binding functionailty to any existing DatePicker. The region "Attached Property" is quite simple and in Visual studio you can add this template of code by just entering "propa" and then pressing Tab key 2 times.

The region "Attached property handler" is the section where the actual event is being attached. This function is called each time the AttachedProperty "CalenderOpenCommand" value is changed. In this event handler the DependancyObject passed will be a DatePicker. and "DependancyPropertyChangedEventArgs.NewValue" would be the command with which binding is being done. After some basic checks, the CalenderOpened event handler is registered which is "datePicker_CalenderOpened". In this event handler the Command of ViewModel is retreived through "GetCalenderOpenCommand" and is checked if the command can be executed or not using "CanExecuteMethod" and finaly the command is executed. Thats all you need to do for attaching Event to Command. Now on next page we will see how to use this code using Commands in your view model.

 In View you can do use the attached property like this.

 2- The XAML

<Window x:Class="WPF_Event_to_Command_Attached_Properties.MainWindow" 
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:behaviors="clr-namespace:WPF_Event_to_Command_Attached_Properties.Behaviors"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<DatePicker HorizontalAlignment="Left" Grid.Row="0" Margin="10,10,0,0" VerticalAlignment="Top" behaviors:DateControlBehavior.CalenderOpenCommand="{Binding CalenderOpen}"/>
<Label Content="{Binding StatusText}" Grid.Row="1"></Label>
</Grid>
</Window>

 The attached property is set for DatePicker like "behaviors:DateControlBehavior.CalenderOpenCommand="{Binding CalenderOpen}"" and the CalenderOpen is RelayCommand in viewmodel, it's code is

3- The View Model

using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;
using WPF_Event_to_Command_Attached_Properties.Annotations;
using WPF_Event_to_Command_Attached_Properties.Command;
namespace WPF_Event_to_Command_Attached_Properties.ViewModels
{
    public class MainViewViewModel : INotifyPropertyChanged
    {
        public MainViewViewModel()
        {
            CalenderOpen = new RelayCommand(OnCalenderOpened);
            StatusText = "Hello World! Command to Event binding example!";
        }
        
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        [NotifyPropertyChangedInvocator]
        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
        #region Commands
        public ICommand CalenderOpen { get; set; }
        #endregion
        #region Command Handlers
        ///
        /// Executed when Calender is opened.
        ///
        ///
        private void OnCalenderOpened(object obj)
        {
            RoutedEventArgs args = obj as RoutedEventArgs;
            if (args != null)
            {
                StatusText = "Calender is Opended, Event handler from ViewModel OnCalenderOpened()!";
            }
        }
        #endregion
        #region Properties
        private string _statusText;
        public string StatusText
        {
            get { return _statusText; }
            set
            {
                _statusText = value;
                OnPropertyChanged();
            }
        }
        
        #endregion
    }
}

I have used RelayCommand which is an implementation of ICommand receiving parameter, because I am passing "RoutedEventArgs" as parameter to command handler. The code of RelayCommand is

4- The RelayCommand Class

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace WPF_Event_to_Command_Attached_Properties.Command
{
    /// <summary>
    /// the relay command class
    /// </summary>
    public class RelayCommand : ICommand
    {
        #region Fields readonly
        /// <summary>
        /// can execute 
        /// </summary>
        private readonly Predicate<object> canExecute_;
        /// <summary>
        /// action to execute
        /// </summary>
        private Action<object> execute_;
        #endregion
        // Fields 
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="RelayCommand"/> class.
        /// </summary>
        /// <param name="execute">action to execute</param>
        public RelayCommand(Action<object> execute)
            : this(execute, null)
        {
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="RelayCommand"/> class.
        /// </summary>
        /// <param name="execute">the action to execute</param>
        /// <param name="canExecute">can execute</param>
        public RelayCommand(Action<object> execute, Predicate<object> canExecute)
        {
            if (execute == null)
            {
                throw new ArgumentNullException("execute");
            }
            execute_ = execute;
            canExecute_ = canExecute;
        }
        #endregion
        // Constructors 
        #region ICommand Members
        /// <summary>
        /// can execute change event
        /// </summary>
        public event EventHandler CanExecuteChanged
        {
            add
            {
                CommandManager.RequerySuggested += value;
            }
            remove
            {
                CommandManager.RequerySuggested -= value;
            }
        }
        /// <summary>
        /// the can execute 
        /// </summary>
        /// <param name="parameter">the parameter</param>
        /// <returns>true or false</returns>
        [DebuggerStepThrough]
        public bool CanExecute(object parameter)
        {
            return canExecute_ == null ? true : canExecute_(parameter);
        }
        /// <summary>
        /// the execute method
        /// </summary>
        /// <param name="parameter">the parameter</param>
        public void Execute(object parameter)
        {
            execute_(parameter);
        }
        #endregion
        // ICommand Members 
    }
}

 

I hope It would be a fun for you to follow MVVM in your WPF application. Do share your feedback or enhancement suggestions.

Add comment


Security code
Refresh