Quelle est la meilleure façon de simuler un clic avec MouseUp & MouseDown événements ou autrement?

dans WPF la plupart des contrôles ont des événements MouseUp et MouseDown (et les variations spécifiques aux boutons de la souris) mais pas un simple événement Click qui peut être utilisé immédiatement. Si vous voulez avoir un comportement de clic-comme utilisant ces événements que vous devez gérer les deux que je considère comme un peu d'une douleur.

le problème évident est que vous ne pouvez pas simplement omettre l'événement MouseDown parce que si votre clic est lancé sur un autre contrôle et il est libéré sur le contrôle qui ne manipule que MouseUp votre clic supposé se déclenchera alors qu'il ne devrait pas vraiment: les deux MouseUp et MouseDown devraient se produire sur le même contrôle.

donc je serais intéressé par une solution plus élégante à ce problème général s'il y a.


Notes: il y a plusieurs bonnes solutions à cela comme on peut le voir ci-dessous, j'ai choisi d'accepter la réponse de Rachel parce qu'il semble soyez bien reçu, mais en plus j'aimerais ajouter les annotations suivantes:

le bouton de Rachel answer est assez propre et simple, mais vous devez envelopper votre commande réelle dans un bouton et dans certains cas, vous pourriez ne pas vraiment considérer votre commande d'être un bouton juste parce qu'il peut être cliqué (par exemple, si elle est plus comme un hyperlien), plus vous devez faire référence à un modèle à chaque fois.

Rick Comportement de Sladkey réponse répond plus directement à la question d'origine de la façon de simuler un clic/Faire un contrôle cliquable, l'inconvénient est que vous avez besoin de référence System.Windows.Interactivity et comme solution de Rachel, il gonfle le code Xaml un peu.

Mon événement attaché réponse a l'avantage d'être assez proche de la normale sur l'événement en termes de Xaml de Balisage qui peut être fait avec un seul attribut, le seul problème que je vois avec c'est que l'événement-pièce jointe dans le code n'est pas proprement encapsulé (si quelqu'un connaît une solution pour que, s'il vous plaît ajouter un commentaire à la réponse).

29
demandé sur Community 2011-01-28 18:50:22

5 réponses

j'utiliserais un Button pour contrôler et remplacer le Button.Template pour afficher le contenu directement.

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

<Button Template="{StaticResource ContentOnlyTemplate}">
    <Label Content="Test"/>
</Button>
17
répondu Rachel 2012-06-29 11:12:34

voici un comportement que vous pouvez ajouter à n'importe quel élément pour qu'il élève l'événement ButtonBase.Click en utilisant le bouton normal 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));
        };
    }
}

remarquez l'utilisation de la capture/libération de la souris et la vérification du test de succès d'entrée. Avec ce comportement en place, Nous pouvons écrire les gestionnaires de clic comme ceci:

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

et le code derrière:

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

il est assez facile de convertir ceci à tout le code-derrière; la partie importante est la logique de capture et de clic.

si vous n'êtes pas familier avec les behaviors, installez L'Expression Blend 4 SDK et ajoutez ce namespace:

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

et ajouter System.Windows.Interactivity à votre projet.

Edit:

Voici comment attacher le comportement de clic à un élément dans le code-behind et Ajouter un handler pour l'événement de clic:

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);
}
12
répondu Rick Sladkey 2011-01-29 17:23:28

Ok, j'ai juste pensé que je pourrais jouer avec des événements attachés et voir où je vais avec elle, ce qui suit semble fonctionner dans la plupart des cas, pourrait utiliser un certain raffinement/débugging:

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

Utilisation:

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

Edit: Changé d'utiliser la souris de capture au lieu de stocker de l'expéditeur/source qui ne se purifient pas suffisamment. Le problème restant est que vous ne pouvez pas ajouter l'événement dans le code en utilisant UIElement.AddHandler(Extensions.ClickEvent, handler) parce que cela omet les pièces jointes des autres événements de la souris qui sont nécessaires pour soulever l'événement de clic (vous devez utiliser Extensions.AddClickHandler(object, handler) à la place).

4
répondu H.B. 2011-01-30 12:38:26

Voici une solution simple que j'utilise assez régulièrement. Il est facile, et fonctionne dans toutes sortes de scénarios.

dans votre code, placez ceci.

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

maintenant, tout ce que vous avez à faire est de configurer les gestionnaires pour tous les contrôles que vous vous souciez i.e.

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

si vous avez une tonne de commandes, et que vous ne voulez pas tout faire dans XAML, vous pouvez le faire dans votre programme controls / window constructor.

cela couvrira la plupart de vos scénarios, et il peut facilement être modifié pour traiter des cas spéciaux.

3
répondu A.R. 2011-01-28 17:37:09

ajouter D'abord une fonction de clic d'événement de souris:

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

Ajouter un événement click à l'un de vos contrôles WPF:

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

enfin, appelez l'événement de clic de n'importe quelle fonction:

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

pour simuler un double clic, ajoutez un événement double clic comme PreviewMouseDoubleClick et assurez-vous que n'importe quel code commence dans une fonction séparée:

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

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

pour invoquer l'événement de double clic, il suffit d'appeler il d'une autre fonction (comme KeyDown):

private void someControl_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
    if (e.Key == Key.Enter)
        DoMouseDoubleClick(e);
}
-2
répondu Marcel Pennock 2014-04-24 14:17:47