Sunday, February 12, 2012

IsPressed Trigger on any WPF control

WPF provides IsPressed Property on button that can be used to write triggers based on its value. There is no such propery available on other control. I recently had to show a DropShadowEffect when a Border is pressed. I used solution described below which can be used for any other WPF control as well that derives from FrameworkElement. I have used attached properties for this.


public class FrameworkElementExt
{
public static readonly DependencyProperty IsPressedProperty = DependencyProperty.RegisterAttached("IsPressed", typeof(bool),
typeof(FrameworkElementExt), new PropertyMetadata(false));

public static readonly DependencyProperty AttachIsPressedProperty = DependencyProperty.RegisterAttached("AttachIsPressed", typeof(bool), typeof(FrameworkElementExt), new PropertyMetadata(false, PropertyChangedCallback));

public static void PropertyChangedCallback(DependencyObject depObj, DependencyPropertyChangedEventArgs args)
{
   FrameworkElement element = (FrameworkElement)depObj;
   if (element != null)
   {
     if ((bool)args.NewValue)
     {
       element.MouseDown +=
new MouseButtonEventHandler(element_MouseDown);
       element.MouseUp += new MouseButtonEventHandler(element_MouseUp);
       element.MouseLeave += new MouseEventHandler(element_MouseLeave);
     }
     else
     {
       element.MouseDown -=
new MouseButtonEventHandler(element_MouseDown);
       element.MouseUp -= new MouseButtonEventHandler(element_MouseUp);
      element.MouseLeave -= new MouseEventHandler(element_MouseLeave);
      }
   }
}

static void element_MouseLeave(object sender, MouseEventArgs e)
{
  FrameworkElement element = (FrameworkElement)sender;
  if (element != null)
  {
    element.SetValue(IsPressedProperty,
false);
  }
}
static void element_MouseUp(object sender, MouseButtonEventArgs e)
{
  FrameworkElement element = (FrameworkElement)sender;
  if (element != null)
  {
    element.SetValue(IsPressedProperty,
false);
  }
}
static void element_MouseDown(object sender, MouseButtonEventArgs e)
{
  FrameworkElement element = (FrameworkElement)sender;
  if (element != null)
  {
    element.SetValue(IsPressedProperty,
true);
  }
}
public static bool GetIsPressed(UIElement element)
{
  return (bool)element.GetValue(IsPressedProperty);
}
public static void SetIsPressed(UIElement element, bool val)
{
  element.SetValue(IsPressedProperty, val);
}
public static bool GetAttachIsPressed(UIElement element)
{
  return (bool)element.GetValue(AttachIsPressedProperty);
}
public static void SetAttachIsPressed(UIElement element, bool val)
{
  element.SetValue(AttachIsPressedProperty, val);
}
}

Effect is defined in Resorces
<DropShadowEffect x:Key="effect" ShadowDepth="0" Color="White" BlurRadius="20" />


Now Define The Trigger as below, notice that AttachIsPressed needs to be set to true. 

<Border CornerRadius="10" local:FrameworkElementExt.AttachIsPressed="True">
<Border.Style>
  <Style TargetType="{x:Type Border}">
    <Style.Triggers>
       <Trigger Property="local:FrameworkElementExt.IsPressed" Value="True">
         <Setter Property="Effect" Value="{StaticResource effect}"/>
      </Trigger>
    </Style.Triggers>
  </Style>
</Border.Style>

</Border>

Wednesday, January 18, 2012

Run WPF application without App.xaml

Delete the existing App.xaml file and write a new class file(lets call it StartUp.cs) that looks like below:
    public class StartUp : Application
    {
        [System.STAThreadAttribute()]
        [System.Diagnostics.DebuggerNonUserCodeAttribute()]
        public static void Main()
        {
            StartUp app = new StartUp();
            app.InitializeComponent();
            app.Run();
        }
        public void InitializeComponent()
        {
            this.StartupUri = new Uri("MainWindow.xaml", System.UriKind.Relative);
        }
    }

Note that this class inherits System.Windows.Application

Tuesday, January 10, 2012

Wire any WPF Event to Command on ViewModel in MVVM

In the last post I explained how to write a behaviour to hook a commmand to an event. That works well but a new behaviour is needed to written for each event. I tried a generic approach to have a single behaviour that can be used to wire any event to a command. It comes witha little performance penalty as reflection is used to get the event and attached a delegate to that event. Below is the code:

using System.Windows.Input;
using System.Windows.Controls.Primitives;
using System.Windows.Controls;
using System.Reflection;
using System.Diagnostics;

public class CommandExecuter
{
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(CommandExecuter), new PropertyMetadata(CommandPropertyChangedCallback));

public static readonly DependencyProperty OnEventProperty = DependencyProperty.RegisterAttached("OnEvent", typeof(string), typeof(CommandExecuter));

public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(CommandExecuter));

public static void CommandPropertyChangedCallback(DependencyObject depObj, DependencyPropertyChangedEventArgs args)
{
  string onEvent = (string)depObj.GetValue(OnEventProperty);
  Debug.Assert(onEvent != null, "OnEvent must be set.");
  var eventInfo = depObj.GetType().GetEvent(onEvent);
  if (eventInfo != null)
  {
    var mInfo = typeof(CommandExecuter).GetMethod("OnRoutedEvent", BindingFlags.NonPublic | BindingFlags.Static);
    eventInfo.GetAddMethod().Invoke(depObj, new object[] { Delegate.CreateDelegate(eventInfo.EventHandlerType, mInfo) });
  }
  else
  {
    Debug.Fail(string.Format("{0} is not found on object {1}", onEvent, depObj.GetType()));

}

}
public static ICommand GetCommand(UIElement element)
{
   return (ICommand)element.GetValue(CommandProperty);
}
public static void SetCommand(UIElement element, ICommand command)
{
   element.SetValue(CommandProperty, command);
}
public static string GetOnEvent(UIElement element)
{
return (string)element.GetValue(OnEventProperty);
}
public static void SetOnEvent(UIElement element, string evnt)
{
   element.SetValue(OnEventProperty, evnt);
}
public static object GetCommandParameter(UIElement element)
{
  return (object)element.GetValue(CommandParameterProperty);
}
public static void SetCommandParameter(UIElement element, object commandParam)
{
   element.SetValue(CommandParameterProperty, commandParam);
}
private static void OnRoutedEvent(object sender, RoutedEventArgs e)
{
  UIElement element = (UIElement)sender;
  if (element != null)
  {    ICommand command = element.GetValue(CommandProperty) as ICommand;
    if (command != null && command.CanExecute(element.GetValue(CommandParameterProperty)))
    {
      command.Execute(element.GetValue(CommandParameterProperty));
    }
  }
}
}


Below code shows how to set the command on the controls for any event:   (local: is namespace prefix)
<ComboBox local:CommandExecuter.Command="{Binding CommandImpl}" local:CommandExecuter.OnEvent="SelectionChanged" ></ComboBox>

<Button local:CommandExecuter.Command="{Binding AnotherCommandImpl}" local:CommandExecuter.OnEvent="MouseEnter" local:CommandExecuter.CommandParameter="{x:Static null}"></Button>


Monday, January 9, 2012

Execute Command on ComboBox Selection changed

If you are developing WPF application MVVM way you might have noticed that Button Provide a Command property that can be set to a ICommand instance and the command will be executed when button is clicked. There is no inbuilt way to set a Command that can be executed when ComboBox selection is changed. I have coded a behaviour that can be used to achieve this. Below is the code for the behaviour.

using System.Windows.Input;
using System.Windows.Controls.Primitives;
using System.Windows.Controls;
public class SelectionChangedBehaviour
{
 
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached("Command", typeof(ICommand),
  typeof(SelectionChangedBehaviour), new PropertyMetadata(PropertyChangedCallback));

  public static void PropertyChangedCallback(DependencyObject depObj, DependencyPropertyChangedEventArgs args)
  {
   
Selector selector = (Selector)depObj;
    if (selector != null)
    {
      selector.SelectionChanged +=
new SelectionChangedEventHandler(SelectionChanged);
    }
  }


 
public static ICommand GetCommand(UIElement element)
  {
   
return (ICommand)element.GetValue(CommandProperty);
  }

 
public static void SetCommand(UIElement element, ICommand command)
  {
    element.SetValue(CommandProperty, command);
  }

  private static void SelectionChanged(object sender, SelectionChangedEventArgs e)
  {
   
Selector selector = (Selector)sender;
    if (selector != null)
    {
     
ICommand command = selector.GetValue(CommandProperty) as ICommand;
      if (command != null)
      {
        command.Execute(selector.SelectedItem);
      }
    }
  }
}


Set the command in xaml as shown below:

<ComboBox SelectionChangedBehaviour.Command="{Binding CommandImpl}">ComboBox>

This can be used for ListBox and ListView as well.