Подтвердить что ты не робот

Каков наилучший способ имитации событий Click with MouseUp & MouseDown или иначе?

В WPF большинство элементов управления имеют события MouseUp и MouseDown (и вариации, зависящие от кнопки мыши), но не простое событие Click, которое можно использовать сразу. Если вы хотите, чтобы поведение, связанное с кликом, использовало эти события, вам нужно обрабатывать обе проблемы, которые я считаю немного больными.

Очевидная проблема заключается в том, что вы не можете просто опустить событие MouseDown, потому что если ваш клик запускается на другом элементе управления, и он выдается над элементом управления, который обрабатывает только MouseUp, ваш предполагаемый клик будет срабатывать, пока он действительно не должен: Оба параметра MouseUp и MouseDown должны выполняться над одним и тем же элементом управления.

Поэтому мне было бы интересно более элегантное решение этой общей проблемы, если оно есть.


Примечания: Есть несколько хороших решений для этого, как показано ниже, я решил принять ответ Рэйчел, потому что он, кажется, хорошо принят, но, кроме того, я хотел бы добавить следующие аннотации

Ответ кнопки Rachel довольно чист и прост, но вам нужно обернуть свой фактический элемент управления кнопкой, и в некоторых случаях вы, возможно, не считаете, что ваш контроль кнопку, потому что ее можно щелкнуть (например, если она больше похожа на гиперссылку), вам нужно постоянно ссылаться на шаблон.

Ответ на поведение Рика Сладки более непосредственно отвечает на исходный вопрос о том, как просто имитировать щелчок/сделать элемент управления кликабельным, недостатком является то, что вам нужно ссылаться на System.Windows.Interactivity и, как решение Rachel, он немного раздувает Xaml-код.

Мой прикрепленный ответ на мероприятие имеет то преимущество, что он близок к нормальному событию кликов в терминах Xaml-Markup, который может быть выполнен с одним атрибутом, единственная проблема я вижу, что вложение событий в код не является инкапсулированным (если кто-то знает об этом, добавьте комментарий к ответу).

4b9b3361

Ответ 1

Я бы использовал элемент управления Button и перезаписал Button.Template, чтобы просто показать содержимое напрямую.

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

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

Ответ 2

Вот поведение, которое вы можете добавить к любому элементу, чтобы он увеличил событие ButtonBase.Click, используя логику обычной кнопки:

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

Обратите внимание на использование захвата/выключения мыши и проверки теста ввода. При таком поведении мы можем написать обработчики кликов следующим образом:

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

и код позади:

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

Достаточно легко преобразовать это ко всему коду; важной частью является логика захвата и щелчка.

Если вы не знакомы с поведением, установите пакет Expression Blend 4 SDK и добавьте это пространство имен:

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

и добавьте System.Windows.Interactivity к вашему проекту.

Edit:

Здесь, как прикрепить поведение клика к элементу с кодом и добавить обработчик события click:

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

Ответ 3

Хорошо, просто подумал, что я буду играть со связанными событиями и посмотреть, где я буду работать с ним, похоже, что в большинстве случаев это работает, можно использовать некоторые уточнения/отладки:

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

Ответ 4

Вот простое решение, которое я использую на регулярной основе. Это легко и работает во всех сценариях.

В своем коде поместите это.

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, вы можете сделать это в своем конструкторе элементов управления/окна программно.

Это будет охватывать большинство ваших сценариев, и его можно легко настроить для обработки особых случаев.

Ответ 5

Сначала добавьте функцию щелчка мышью:

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

Добавить событие click для одного из ваших элементов управления WPF:

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

Наконец, вызовите событие click из любой функции:

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

Чтобы имитировать двойной щелчок, добавьте событие двойного щелчка, например PreviewMouseDoubleClick, и убедитесь, что любой код запускается в отдельной функции:

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

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

Чтобы вызвать событие двойного щелчка, просто вызовите его из другой функции (например, KeyDown):

private void someControl_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
    if (e.Key == Key.Enter)
        DoMouseDoubleClick(e);
}