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>

6 comments:

  1. Naveen, thank you for shareing your code-stuff! But I beat my head about question - why there is a AttachIsPressedProperty. What kind of role of this property? I can't find relation between AttachIsPressedProperty and IsPressedProperty, but if i cut 'local:FrameworkElementExt.AttachIsPressed="True"' from Xaml, my IsPressed property won't work.... can you understand please.

    ReplyDelete
  2. FrameworkElementExt.AttachIsPressed="True" is needed because we use the PropertyChangeCallabck of this property to hook to MouseUp/Down/Leave events. Without setting it to true PropertyChangeCallabck will not be called.

    ReplyDelete
  3. ok, thanks for your explanation, and sorry for my English. But why we can't get PropertyChangeCallaback from the IsPressedProperty implementation. For what we need to set additional property if we already have IsPressedProperty?

    ReplyDelete
  4. IsPressed is used on Triggers and it is set in this behaviour based on Mouse events, that is why we need to hook to mouse events using AttachIsPressed Property.

    ReplyDelete
  5. This comment has been removed by the author.

    ReplyDelete
  6. Would this work with touch screen? Should there be additional handlers for Touch* (TouchDown, TouchUp etc), Stylus* and Manipulation* events? How would you make it more universal? Thanks.

    ReplyDelete

Type based templating

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