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

血红的双手。 提交于 2019-11-27 03:44:48
Rachel

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).

Here is a simple solution that I use on a fairly regular basis. It is easy, and works in all sorts of scenarios.

In your code, place this.

private object DownOn = null;

private void LeftButtonUp(object sender, MouseButtonEventArgs e)
{
  if (DownOn == sender)
  {
    MessageBox.Show((sender as FrameworkElement).Name);
  }
  DownOn = null;
}

private void LeftButtonDown(object sender, MouseButtonEventArgs e)
{
  DownOn = sender;
}

Now all you have to do is setup handlers for all of the controls that you care about i.e.

<Border Background="Red"  MouseLeftButtonUp="LeftButtonUp" MouseLeftButtonDown="LeftButtonDown".../>
<Border Background="Blue" MouseLeftButtonUp="LeftButtonUp" MouseLeftButtonDown="LeftButtonDown".../>

If you have a ton of controls, and don't want to do it all in XAML, you can do it in your controls / window constructor programatically.

This will cover most of your scenarios, and it can easily be tweaked to handle special cases.

First add a Mouse event click function:

/// <summary>
/// Returns mouse click.
/// </summary>
/// <returns>mouseeEvent</returns>
public static MouseButtonEventArgs MouseClickEvent()
{
    MouseDevice md = InputManager.Current.PrimaryMouseDevice;
    MouseButtonEventArgs mouseEvent = new MouseButtonEventArgs(md, 0, MouseButton.Left);
    return mouseEvent;
}

Add a click event to one of your WPF controls:

private void btnDoSomeThing_Click(object sender, RoutedEventArgs e)
{
    // Do something
}

Finally, Call the click event from any function:

btnDoSomeThing_Click(new object(), MouseClickEvent());

To simulate double clicking, add a double click event like PreviewMouseDoubleClick and make sure any code starts in a separate function:

private void lvFiles_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    DoMouseDoubleClick(e);
}

private void DoMouseDoubleClick(RoutedEventArgs e)
{
    // Add your logic here
}

To invoke the double click event, just call it from another function (like KeyDown):

private void someControl_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
    if (e.Key == Key.Enter)
        DoMouseDoubleClick(e);
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!