What is the best way to simulate a Click with MouseUp & MouseDown events or otherwise? What is the best way to simulate a Click with MouseUp & MouseDown events or otherwise? wpf wpf

What is the best way to simulate a Click with MouseUp & MouseDown events or otherwise?


I would use a Button control and overwrite the Button.Template to just show the content directly.

<ControlTemplate x:Key="ContentOnlyTemplate" TargetType="{x:Type Button}">    <ContentPresenter /></ControlTemplate><Button Template="{StaticResource ContentOnlyTemplate}">    <Label Content="Test"/></Button>


Here is a behavior you can add to any element so that it will raise the ButtonBase.Click event using the normal button logic:

public class ClickBehavior : Behavior<FrameworkElement>{    protected override void OnAttached()    {        AssociatedObject.MouseLeftButtonDown += (s, e) =>        {            e.Handled = true;            AssociatedObject.CaptureMouse();        };        AssociatedObject.MouseLeftButtonUp += (s, e) =>        {            if (!AssociatedObject.IsMouseCaptured) return;            e.Handled = true;            AssociatedObject.ReleaseMouseCapture();            if (AssociatedObject.InputHitTest(e.GetPosition(AssociatedObject)) != null)                AssociatedObject.RaiseEvent(new RoutedEventArgs(ButtonBase.ClickEvent));        };    }}

Notice the use of mouse capture/release and the input hit test check. With this behavior in place, we can write click handlers like this:

<Grid>    <Rectangle Width="100" Height="100" Fill="LightGreen" ButtonBase.Click="Rectangle_Click">        <i:Interaction.Behaviors>            <utils:ClickBehavior/>        </i:Interaction.Behaviors>    </Rectangle></Grid>

and the code behind:

    private void Rectangle_Click(object sender, RoutedEventArgs e)    {        Debug.WriteLine("Code-behind: Click");    }

It's easy enough to convert this to all code-behind; the important part is the capture and click logic.

If you are not familiar with behaviors, install the Expression Blend 4 SDK and add this namespace:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"

and add System.Windows.Interactivity to your project.

Edit:

Here's how to attach the click behavior to an element in code-behind and add a handler for the click event:

void AttachClickBehaviors(){    AttachClickBehavior(rectangle1, new RoutedEventHandler(OnAttachedClick));}void OnAttachedClick(object sender, RoutedEventArgs e){    Debug.WriteLine("Attached: Click");}// Reusable: doesn't need to be in the code-behind.static void AttachClickBehavior(FrameworkElement element, RoutedEventHandler clickHandler){    Interaction.GetBehaviors(element).Add(new ClickBehavior());    element.AddHandler(ButtonBase.ClickEvent, clickHandler);}


Ok, just thought i'd play around with attached events and see where i'll get with it, the following seems to work in most cases, could use some refinement/debugging:

public static class Extensions{    public static readonly RoutedEvent ClickEvent = EventManager.RegisterRoutedEvent(        "Click",        RoutingStrategy.Bubble,        typeof(RoutedEventHandler),        typeof(UIElement)        );    public static void AddClickHandler(DependencyObject d, RoutedEventHandler handler)    {        UIElement element = d as UIElement;        if (element != null)        {            element.MouseLeftButtonDown += new MouseButtonEventHandler(element_MouseLeftButtonDown);            element.MouseLeftButtonUp += new MouseButtonEventHandler(element_MouseLeftButtonUp);            element.AddHandler(Extensions.ClickEvent, handler);        }    }    public static void RemoveClickHandler(DependencyObject d, RoutedEventHandler handler)    {        UIElement element = d as UIElement;        if (element != null)        {            element.MouseLeftButtonDown -= new MouseButtonEventHandler(element_MouseLeftButtonDown);            element.MouseLeftButtonUp -= new MouseButtonEventHandler(element_MouseLeftButtonUp);            element.RemoveHandler(Extensions.ClickEvent, handler);        }    }    static void element_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)    {        UIElement uie = sender as UIElement;        if (uie != null)        {            uie.CaptureMouse();        }    }    static void element_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)    {        UIElement uie = sender as UIElement;        if (uie != null && uie.IsMouseCaptured)        {            uie.ReleaseMouseCapture();            UIElement element = e.OriginalSource as UIElement;            if (element != null && element.InputHitTest(e.GetPosition(element)) != null) element.RaiseEvent(new RoutedEventArgs(Extensions.ClickEvent));        }    }}

Usage:

<TextBlock local:Extensions.Click="TextBlock_Click" Text="Test"/>

Edit: Changed it to use mouse-capture instead of storing sender/source which did not get cleared out sufficiently. Remaining problem is that you cannot add the event in code using UIElement.AddHandler(Extensions.ClickEvent, handler) because that omits the attachments of the other mouse events which are needed for raising the click event (you need to use Extensions.AddClickHandler(object, handler) instead).