Каков наилучший способ симулировать события Click with MouseUp & MouseDown или нет? - PullRequest
29 голосов
/ 28 января 2011

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

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

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


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

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

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

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

Ответы [ 5 ]

17 голосов
/ 28 января 2011

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

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

<Button Template="{StaticResource ContentOnlyTemplate}">
    <Label Content="Test"/>
</Button>
12 голосов
/ 29 января 2011

Вот поведение, которое вы можете добавить к любому элементу, чтобы он вызывал событие 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:

Вот как прикрепить поведение щелчка к элементу в выделенном фрагменте кода и добавить обработчик для события щелчка:

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);
}
4 голосов
/ 28 января 2011

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

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), поскольку при этом пропускаются вложения других событий мыши, которые необходимы для вызова события click (вместо этого необходимо использовать Extensions.AddClickHandler(object, handler)).

3 голосов
/ 28 января 2011

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

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

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

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

0 голосов
/ 24 апреля 2014

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

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

Добавьте событие клика к одному из ваших элементов управления 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);
}
...