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>

Type based templating

            <DataTemplate DataType="{x:Type vm:SampleViewModel}">                 <DataTemplate.Resources>       ...