Wpf 用鼠标点击模拟点击的最佳方式是什么&;鼠标下移事件还是其他?

Wpf 用鼠标点击模拟点击的最佳方式是什么&;鼠标下移事件还是其他?,wpf,controls,click,mouseevent,Wpf,Controls,Click,Mouseevent,在WPF中,大多数控件都有MouseUp和MouseDown事件(以及特定于鼠标按钮的变体),但没有可以立即使用的简单单击事件。如果你想使用类似事件的点击行为,你需要处理这两个我认为有点痛苦的事情。 明显的问题是,您不能简单地忽略MouseDown事件,因为如果您的单击在另一个控件上启动,并且它在仅处理MouseUp的控件上释放,那么您假定的单击将触发,而实际上不应该触发:MouseUp和MouseDown都应该发生在另一个控件上同样的控制 因此,我对这个普遍问题的更优雅的解决方案感兴趣,如果有

在WPF中,大多数控件都有
MouseUp
MouseDown
事件(以及特定于鼠标按钮的变体),但没有可以立即使用的简单
单击事件。如果你想使用类似事件的点击行为,你需要处理这两个我认为有点痛苦的事情。
明显的问题是,您不能简单地忽略
MouseDown
事件,因为如果您的单击在另一个控件上启动,并且它在仅处理
MouseUp
的控件上释放,那么您假定的单击将触发,而实际上不应该触发:
MouseUp
MouseDown
都应该发生在另一个控件上同样的控制

因此,我对这个普遍问题的更优雅的解决方案感兴趣,如果有的话


注意:如下所示,有几种很好的解决方案,我选择接受Rachel的答案,因为它似乎很受欢迎,但另外我想添加以下注释:

是非常干净和直接的,但是你需要把你的实际控件包在一个按钮中,在某些情况下,你可能不会真的认为你的控件仅仅是一个按钮,因为它可以被点击(例如,如果它更像一个超链接),那么你需要每次都引用一个模板。 更直接地回答了如何模拟单击/使控件可单击的原始问题,缺点是您需要引用

System.Windows.Interactivity
,并且像Rachel的解决方案一样,它大大增加了Xaml代码


就Xaml标记而言,它的优点是非常接近于普通的单击事件,而Xaml标记可以通过一个属性完成,我看到的唯一问题是代码中的事件附件没有被干净地封装(如果有人知道修复方法,请在回答中添加注释).

我会使用
按钮
控件覆盖
按钮。模板
直接显示内容

<ControlTemplate x:Key="ContentOnlyTemplate" TargetType="{x:Type Button}">
    <ContentPresenter />
</ControlTemplate>

<Button Template="{StaticResource ContentOnlyTemplate}">
    <Label Content="Test"/>
</Button>

这是一个我经常使用的简单解决方案。这很简单,适用于各种场景

在你的代码中,放置这个

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;
}
现在,您所要做的就是为您关心的所有控件设置处理程序

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

如果您有大量控件,并且不想在XAML中完成所有操作,那么可以通过编程方式在控件/窗口构造函数中完成


这将涵盖您的大多数场景,并且可以很容易地调整它以处理特殊情况。

好的,我只是想玩一下附加的事件,看看我能从中得到什么,以下内容似乎在大多数情况下都有效,可以使用一些改进/调试:

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));
        }
    }
}
用法:

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


编辑:将其更改为使用鼠标捕获,而不是存储未充分清除的发件人/源。剩下的问题是,您无法使用
UIElement.AddHandler(Extensions.ClickEvent,handler)
在代码中添加事件,因为这会忽略引发单击事件所需的其他鼠标事件的附件(您需要改为使用
Extensions.AddClickHandler(object,handler)
).

这里有一个行为,您可以添加到任何元素中,以便它将引发
按钮库。使用正常按钮逻辑单击
事件:

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));
        };
    }
}
很容易将其转换为所有代码隐藏;重要的部分是捕获和单击逻辑

如果您不熟悉行为,请安装Expression Blend 4 SDK并添加此命名空间:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
并将
System.Windows.Interactivity
添加到项目中

编辑:

下面介绍如何将单击行为附加到代码隐藏中的元素,并为单击事件添加处理程序:

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);
}

首先添加鼠标事件单击功能:

/// <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;
}
btnDoSomeThing_Click(new object(), MouseClickEvent());
private void lvFiles_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    DoMouseDoubleClick(e);
}

private void DoMouseDoubleClick(RoutedEventArgs e)
{
    // Add your logic here
}
最后,从任何函数调用click事件:

/// <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;
}
btnDoSomeThing_Click(new object(), MouseClickEvent());
private void lvFiles_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    DoMouseDoubleClick(e);
}

private void DoMouseDoubleClick(RoutedEventArgs e)
{
    // Add your logic here
}
要模拟双击,请添加类似PreviewMouseDoubleClick的双击事件,并确保所有代码都在单独的函数中启动:

/// <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;
}
btnDoSomeThing_Click(new object(), MouseClickEvent());
private void lvFiles_PreviewMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
    DoMouseDoubleClick(e);
}

private void DoMouseDoubleClick(RoutedEventArgs e)
{
    // Add your logic here
}
要调用双击事件,只需从另一个函数调用它(如KeyDown):


我正在寻找非按钮控件上的单击行为(例如,
TextBlocks
),我会将可单击项设置为按钮,并覆盖控件模板以显示标签。。。我将改变我的帖子,以展示一个例子我知道这种方法,我甚至在我的问题草稿中概述了它,但再次删除了它(最好还是把它作为一个答案)。这还不算太糟糕,因为至少你不需要再使用事件了,也许这是最好的解决方案,但也许其他人已经想到了更好的方法来实现这一点。@H.B:这是使用WPF的预期解决方案。控件是“无外观”的,因为它们可以以您选择的任何方式进行模板化,因此任何需要“可单击”的控件都可以是带有自定义模板的按钮。@Rachel:我知道这只是一个示例,但我会删除
标签
,在那里有一个
ContentPresenter
,这是创建可单击控件的一般方法。模板名称当然类似于
ClickableControl
,或者可能是
ContentOnlyTemplate
@丹·普泽伊:听起来很合理。这是我过去自己做的方式,正如在对克里斯夫回答的评论中所指出的,不太糟糕,但也不是很优雅……也许不是,但经验告诉我,必须为简单的行为制作一堆模板很快就会进入过度杀戮的领域。看看其他人有什么想法会很有趣。我自己提出了一个有趣的解决方案,如果你感兴趣,它扩展了这种方法。现在测试它,它与我的解决方案有相同的问题:它似乎不适用于
弹出式窗口
,这可能很烦人……事件处理程序通常不会像弹出窗口预期的那样工作,因为它们不是o部分