2009-05-08 8 views
9

Étant donné que vous avez un contrôle qui déclenche une commande:Eviter la double-cliquez sur la double tir d'une commande

<Button Command="New"/> 

est-il un moyen d'éviter que la commande soit tiré deux fois si l'utilisateur double-clique sur la commande ?

EDIT: Ce qui est significatif dans ce cas est que j'utilise le modèle Commanding dans WPF.

Il apparaît que chaque fois que le bouton est enfoncé, la commande est exécutée. Je ne vois aucun moyen d'empêcher cela en plus de désactiver ou de masquer le bouton.

+0

Il ne semble pas y avoir d'ajustement ou de remplacement de la gestion interne de l'événement click par WPF. –

Répondre

1

Peut-être que le bouton devrait être désactivé après le premier clic et jusqu'à ce que le traitement soit terminé?

+0

Je crois que c'est essentiellement ce que je vais devoir faire. –

+0

Je ne comprends vraiment pas comment la réponse a été acceptée. Comment après le premier clic devrait être désactivé si l'événement a été modifié en tant qu'async? Et autre; Le processus de synchronisation n'empêche pas le double clic. –

+0

Cette réponse est incorrecte car l'événement est asynchrone. – TeoVr81

3

Vous pouvez définir un drapeau

bool boolClicked = false; 
button_OnClick 
{ 
    if(!boolClicked) 
    { 
     boolClicked = true; 
     //do something 
     boolClicked = false; 
    } 
} 
+2

J'ajouterais un try {boolClicked = true; Faire quelque chose(); } Enfin {boolClicked = false;} pour vous assurer de réinitialiser l'indicateur. À votre santé. – SRO

+0

@nathan koop n'a pas essayé de tester votre code sur une vraie application Xamarin Forms. –

0

Si votre contrôle dérive de System.Windows.Forms.Control, vous pouvez utiliser le double click event.

Si elle ne vient pas de System.Windows.Forms.Control, puis wire up mousedown au lieu et confirmer le nombre de clics == 2:

private void Button_MouseDown(object sender, MouseButtonEventArgs e) 
{ 
    if (e.ClickCount == 2) 
    { 
     //Do stuff 
    } 
} 
+0

Ceci est un contrôle WPF ... Il dérive de System.Windows.Controls.Control –

+0

Ensuite, vous pouvez utiliser ceci: http://msdn.microsoft.com/en-us/library/system.windows.input. mousebuttoneventargs.clickcount.aspx Ou est-ce que j'ai mal compris? –

+0

Le but n'était pas de gérer l'événement de double clic; mon intention est de le supprimer. –

3

En supposant que WPF commandant ne vous donne pas assez de contrôle pour désordre avec le gestionnaire de clic, pourriez-vous mettre du code dans le gestionnaire de commande qui se souvient de la dernière fois que la commande a été exécutée et quitte si elle est demandée dans un délai donné? (exemple de code ci-dessous)

L'idée est que si c'est un double-clic, vous recevrez l'événement deux fois en quelques millisecondes, alors ignorez le deuxième événement.

Quelque chose comme: (à l'intérieur du commandement)

 

// warning: I haven't tried compiling this, but it should be pretty close 
DateTime LastInvoked = DateTime.MinDate; 
Timespan InvokeDelay = Timespan.FromMilliseconds(100); 
{ 
    if(DateTime.Now - LastInvoked <= InvokeDelay) 
    return; 

    // do your work 
} 
 

(note: si elle était juste un gestionnaire vieux clic simple, je dirais que suivre ce conseil: http://blogs.msdn.com/oldnewthing/archive/2009/04/29/9574643.aspx)

+0

Bon lien. La technique anti-rebondissement que vous vous référez est précisément ce dont je parle. Il s'avère que notre programme WPF se comporte parfois mal si vous double-cliquez sur les boutons qui devaient être cliqués individuellement. –

-2

Ce contrôle si la validation est passée et si c'est le cas, le bouton est désactivé.

private void checkButtonDoubleClick(Button button) 
    { 
     System.Text.StringBuilder sbValid = new System.Text.StringBuilder(); 
     sbValid.Append("if (typeof(Page_ClientValidate) == 'function') { "); 
     sbValid.Append("if (Page_ClientValidate() == false) { return false; }} "); 
     sbValid.Append("this.value = 'Please wait...';"); 
     sbValid.Append("this.disabled = true;"); 
     sbValid.Append(this.Page.ClientScript.GetPostBackEventReference(button, "")); 
     sbValid.Append(";"); 
     button.Attributes.Add("onclick", sbValid.ToString()); 
    } 
+0

Wow. Pas sûr de ce qui se passe ici, mais on dirait que vous assumez ASP.NET ou quelque chose. Il n'y a pas de page, pas de ClientScript, et pas de post-retour. –

+0

Oui, le mauvais endroit pour poster ceci. C'est pour asp.net standard, pas wpf. – ta4ka

3

Vous pouvez utiliser la classe EventToCommand dans le MVVMLightToolkit pour empêcher cela.

Gérez l'événement Click et envoyez-le par EventToCommand à partir de votre vue à votre viewmodel (vous pouvez utiliser EventTrigger pour ce faire).
Définissez MustToggleIsEnabled="True" dans votre vue et implémentez une méthode CanExecute() dans votre viewmodel.
Définissez CanExecute() pour renvoyer false lorsque la commande commence à s'exécuter et revenir à true lorsque la commande est terminée.

Ceci désactivera le bouton pendant la durée du traitement de la commande.

+1

La viande de ce que vous dites: Si le bouton est lié à une commande, désactivez la commande (CanExecute() = false) jusqu'à ce que la commande soit terminée. –

+0

@Josh G: oui en effet! – stombeur

7

J'ai eu le même problème et cela a fonctionné pour moi:

<Button> 
    <Button.InputBindings> 
      <MouseBinding Gesture="LeftClick" Command="New" /> 
    </Button.InputBindings> 
</Button> 
+0

Wow. Si cela fonctionne, ce serait fantastique. Je vais devoir l'essayer un jour. –

+0

Je peux confirmer que cela a fonctionné pour moi aussi, merci! – Simone

+0

Sérieusement, comment avez-vous trouvé cela? – Snake

4

On pourrait penser que ce serait aussi simple que d'utiliser un Command et faire CanExecute() return false alors que la commande est en cours d'exécution. Tu aurais tort. Même si vous soulevez CanExecuteChanged explicitement:

public class TestCommand : ICommand 
{ 
    public void Execute(object parameter) 
    { 
     _CanExecute = false; 
     OnCanExecuteChanged(); 
     Thread.Sleep(1000); 
     Console.WriteLine("Executed TestCommand."); 
     _CanExecute = true; 
     OnCanExecuteChanged(); 
    } 

    private bool _CanExecute = true; 

    public bool CanExecute(object parameter) 
    { 
     return _CanExecute; 
    } 

    private void OnCanExecuteChanged() 
    { 
     EventHandler h = CanExecuteChanged; 
     if (h != null) 
     { 
      h(this, EventArgs.Empty); 
     } 
    } 

    public event EventHandler CanExecuteChanged; 
} 

Je pense que si cette commande avait une référence à Dispatcher de la fenêtre, et utilisé Invoke quand il a appelé OnCanExecuteChanged, il travaillerait.

Je peux penser à plusieurs façons de résoudre ce problème. L'approche de JMarsch est la suivante: il suffit de suivre quand le Execute est appelé, et de renflouer sans rien faire s'il a été appelé au cours des dernières centaines de millisecondes.

Une façon plus robuste pourrait être d'avoir la méthode Execute démarrer une BackgroundWorker à faire le traitement proprement dit, ont CanExecute retour (!BackgroundWorker.IsBusy), et augmenter CanExecuteChanged lorsque la tâche est terminée. Le bouton devrait requery CanExecute() dès que Execute() retourne, ce qui se fera instantanément.

+0

Je pense que vous n'avez pas essayé beaucoup de taps (assez vite) quand vous l'avez testé. Cela ne fonctionne pas ... –

14

La réponse vérifiée à cette question, soumise par vidalsasoon, est fausse et elle est erronée pour toutes les différentes façons dont cette même question a été posée.

Il est possible que tout gestionnaire d'événement qui contient du code nécessitant un temps de traitement important puisse retarder la désactivation du bouton en question; indépendamment de l'endroit où la ligne de code de désactivation est appelée dans le gestionnaire. Essayez les preuves ci-dessous et vous verrez que désactiver/activer n'a aucune corrélation avec l'enregistrement des événements. L'événement de clic sur le bouton est toujours enregistré et est toujours géré.

Preuve par Contradiction 1

private int _count = 0; 

private void btnStart_Click(object sender, EventArgs e) 
{ 
    btnStart.Enabled = false; 

    _count++; 
    label1.Text = _count.ToString(); 

    while (_count < 10) 
    {    
     btnStart_Click(sender, e);    
    }   

    btnStart.Enabled = true; 

} 

Preuve par Contradition 2

private void form1_load(object sender, EventArgs e) 
{ 
    btnTest.Enabled = false; 
} 

private void btnStart_Click(object sender, EventArgs e) 
{ 
    btnTest.Enabled = false; 

    btnTest_click(sender, e); 

    btnTest_click(sender, e); 

    btnTest_click(sender, e); 

    btnTest.Enabled = true; 

} 

private int _count = 0; 

private void btnTest_click(object sender, EventArgs e) 
{ 
    _count++; 
    label1.Text = _count.ToString(); 
} 
+0

L'OP utilise MVVM (liaison) et les commandes pour WPF. Vous utilisez l'approche "événements" de WinForms qui ne s'applique pas ici. – vidalsasoon

+1

@vidalsasoon Vous avez raison d'utiliser Winforms comme démonstration. Leur point clé, cependant, est que l'enregistrement des événements est isolé des autres contrôles/boutons activer ou désactiver la propriété et la désactivation d'un bouton n'empêchera pas un gestionnaire d'événements souscrit d'exécuter du code. Cela est vrai pour WPF et Winforms. J'essayais simplement de faire remarquer que la réponse cochée sur ce post n'est pas correcte. Aucune infraction prévue. J'ai fait l'erreur moi-même. – JDennis

5

simple & efficace pour le blocage double, triple, quadruple et clique

<Button PreviewMouseDown="Button_PreviewMouseDown"/> 

private void Button_PreviewMouseDown(object sender, MouseButtonEventArgs e) 
{ 
    if (e.ClickCount >= 2) 
    { 
     e.Handled = true; 
    } 
} 
2

Nous avons résolu comme ça ... avec async nous ne pouvions pas trouver un autre moyen de bloquer efficacement des clics supplémentaires sur le bouton qui invoque cette Cliquez:

private SemaphoreSlim _lockMoveButton = new SemaphoreSlim(1); 
private async void btnMove_Click(object sender, RoutedEventArgs e) 
{ 
    var button = sender as Button; 
    if (_lockMoveButton.Wait(0) && button != null) 
    { 
     try 
     {      
      button.IsEnabled = false; 
     } 
     finally 
     { 
      _lockMoveButton.Release(); 
      button.IsEnabled = true; 
     } 
    } 
} 
2

Une solution simple et élégante est de créer un comportement réaction désactivante lors du deuxième clic dans le scénario de double-clic. Ce qui est assez facile à utiliser:

<Button Command="New"> 
      <i:Interaction.Behaviors> 
      <behaviors:DisableDoubleClickBehavior /> 
      </i:Interaction.Behaviors> 
    </Button> 

comportement (plus sur les comportements - https://www.jayway.com/2013/03/20/behaviors-in-wpf-introduction/)

using System.Windows.Controls; 
using System.Windows.Input; 
using System.Windows.Interactivity; 

public class DisableDoubleClickBehavior : Behavior<Button> 
{ 
    protected override void OnAttached() 
    { 
     base.OnAttached(); 
     AssociatedObject.PreviewMouseDoubleClick += AssociatedObjectOnPreviewMouseDoubleClick; 
    } 

    private void AssociatedObjectOnPreviewMouseDoubleClick(object sender, MouseButtonEventArgs mouseButtonEventArgs) 
    { 
     mouseButtonEventArgs.Handled = true; 
    } 

    protected override void OnDetaching() 
    { 
     AssociatedObject.PreviewMouseDoubleClick -= AssociatedObjectOnPreviewMouseDoubleClick; 
     base.OnDetaching(); 
    } 
} 
0

eu le même problème, il résolu à l'aide d'un comportement attaché.

namespace VLEva.Core.Controls 
{ 
    /// <summary></summary> 
    public static class ButtonBehavior 
    { 
     /// <summary></summary> 
     public static readonly DependencyProperty IgnoreDoubleClickProperty = DependencyProperty.RegisterAttached("IgnoreDoubleClick", 
                                typeof(bool), 
                                typeof(ButtonBehavior), 
                                new UIPropertyMetadata(false, OnIgnoreDoubleClickChanged)); 

     /// <summary></summary> 
     public static bool GetIgnoreDoubleClick(Button p_btnButton) 
     { 
      return (bool)p_btnButton.GetValue(IgnoreDoubleClickProperty); 
     } 

     /// <summary></summary> 
     public static void SetIgnoreDoubleClick(Button p_btnButton, bool value) 
     { 
      p_btnButton.SetValue(IgnoreDoubleClickProperty, value); 
     } 

     static void OnIgnoreDoubleClickChanged(DependencyObject p_doDependencyObject, DependencyPropertyChangedEventArgs e) 
     { 
      Button btnButton = p_doDependencyObject as Button; 
      if (btnButton == null) 
       return; 

      if (e.NewValue is bool == false) 
       return; 

      if ((bool)e.NewValue) 
       btnButton.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(btnButton_PreviewMouseLeftButtonDown); 
      else 
       btnButton.PreviewMouseLeftButtonDown -= btnButton_PreviewMouseLeftButtonDown; 
     } 

     static void btnButton_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
     { 
      if (e.ClickCount >= 2) 
       e.Handled = true; 
     } 

    } 
} 

et puis juste définissez la propriété TRUE soit directement en XAML en déclarant un style de sorte qu'il peut affecte tous vos boutons à la fois. (ne pas oublier la déclaration de l'espace de noms XAML)

<Style x:Key="styleBoutonPuff" TargetType="{x:Type Button}"> 
    <Setter Property="VLEvaControls:ButtonBehavior.IgnoreDoubleClick" Value="True" /> 
    <Setter Property="Cursor" Value="Hand" /> 
</Style> 
Questions connexes