2009-11-06 3 views
10

J'ai un bouton dans mon application qui est lié à une commande. Ce bouton se trouve à l'intérieur d'un autre contrôle qui réagit également aux coups de souris. Lorsque le bouton est activé, j'obtiens le comportement attendu - cliquez sur le bouton et la commande est déclenchée, cliquez en dehors du bouton mais à l'intérieur du contrôle de conteneur et cela est déclenché à la place.Désactiver un bouton WPF, mais toujours avaler les événements de clic

Malheureusement, lorsque le bouton est désactivé (par la méthode CanExecute de la commande) les clique sur le bouton sont barboter jusqu'à la commande du récipient. Je ne veux pas ça, je veux que les clics soient avalés - ni déclencher la commande, ni éclater.

J'ai essayé de surmonter ce problème en crée une nouvelle classe héritant de bouton, mais aucune des méthodes suivantes semblent même se appelé sur un bouton désactivé:

  • OnPreviewMouseDown
  • OnPreviewMouseUp
  • OnPreviewMouseLeftButtonDown
  • SurMise à jour
  • OnMouseUp
  • OnMouseLeftButtonDown
  • OnMouseLeftButtonUp
  • Surclic

Est-ce un contrôle désactivé complètement ignoré par le système d'événement routé WPF? Et si oui, est-ce que je peux obtenir le comportement que je cherche?

Répondre

9

réponse de RCGoforth m'a 90% du chemin, mais la solution est de ne pas mettre un rectangle derrière le bouton , parce que l'événement bouillonnant remonte l'arbre non pas aux frères et soeurs. En fin de compte, j'entouré le bouton avec un ContentControl (depuis les années rectangle ne peuvent pas avoir des enfants) qui a avalé l'événement avant qu'il ne puisse remonter plus loin:

<ContentControl MouseDown="ContentControl_MouseDown"> 
    <Button Content="Click Test" 
      Padding="2" 
      Command="{Binding TestCommand}"/> 
</ContentControl> 

Dans le code sous-jacent:

private void ContentControl_MouseDown(object sender, MouseButtonEventArgs e) 
{ 
    e.Handled = true; 
} 

Ou de le faire entièrement en XAML (et d'augmenter le niveau de piratage du code ...

<Button> 
    <Button.Template> 
     <ControlTemplate> 
      <Button Content="Click Test" 
        Command="{Binding TestCommand}"/> 
     </ControlTemplate> 
    </Button.Template> 
</Button> 
2

Il est pas vraiment propre, mais vous pouvez mettre un rectangle transparent derrière votre bouton qui engloutit les événements de clic.

0

Ce que vous voulez faire est d'enregistrer un gestionnaire d'événements routé à l'aide the overload that takes the handledEventsToo parameter et en spécifiant une valeur true pour ce paramètre. De cette façon, votre gestionnaire externe recevra l'événement, que le bouton gère ou non l'événement. Cela ressemble à quelque chose comme ceci:

this.AddHandler(Mouse.MouseUpEvent, this.MyMouseUpHandler, true); 

Et puis dans votre gestionnaire vous pouvez toujours vérifier ce qui a été cliqué, si elle a été traitée, etc. via le MouseButtonEventArgs vous êtes remis. Par exemple, pour vérifier si un autre contrôle géré en fait l'événement déjà que vous pouvez faire:

if(!args.Handled) 
{ 
    // handle it here instead 
} 
+0

Merci, mais je viens d'être à la recherche sur cette question et je pense que vous pourriez avoir mal interprété ma question. Je ne veux pas que le contrôle externe reçoive * toujours * l'événement, je veux qu'il ne reçoive l'événement que si le clic est en dehors du bouton, même si le bouton est désactivé. –

+0

Vous avez raison, j'ai mal compris votre intention. Je vais réviser. –

0

Une solution plus propre et plus réutilisable implémenterait cette fonctionnalité en tant que propriété jointe.

En utilisant le modèle de service/l'action:

namespace Control.Services 
{ 

    public class UIElementService 
    { 
    public static readonly DependencyProperty HandleMouseEventsProperty = DependencyProperty.RegisterAttached("HandleMouseEvents", 
     typeof(bool), typeof(UIElementService), new FrameworkPropertyMetadata(false, UIElementService.HandleMouseEventsPropertyChanged)); 

    static void HandleMouseEventsPropertyChanged(object sender, DependencyPropertyChangedEventArgs e) 
    { 
     FrameworkElement element = sender as FrameworkElement; 
     if (element == null) 
     return; 

     new HandleMouseEventsAction(element); 
    } 

    public static bool GetHandleMouseEvents(FrameworkElement target) 
    { 
     return (bool)target.GetValue(HandleMouseEventsProperty); 
    } 

    public static void SetHandleMouseEvents(FrameworkElement target, bool value) 
    { 
     target.SetValue(HandleMouseEventsProperty, value); 
    } 

    class HandleMouseEventsAction 
    { 
     UIElement m_Target; 
     MouseButtonEventHandler m_Handler; 

     internal HandleMouseEventsAction(FrameworkElement source) 
     { 
     m_Source = source; 
     m_Handler = new MouseButtonEventHandler(PreviewMouseLeftButtonUp); 

     m_Source.Loaded += OnSource_Loaded; 
     m_Source.Unloaded += OnSource_Unloaded; 
     } 

     void OnSource_Loaded(object sender, RoutedEventArgs e) 
     { 
     m_Source.AddHandler(Mouse.PreviewMouseUpEvent, m_Handler, true); 
     } 

     void OnSource_Unloaded(object sender, RoutedEventArgs e) 
     { 
     m_Source.RemoveHandler(Mouse.PreviewMouseUpEvent, m_Handler); 
     } 

     void PreviewMouseLeftUIElementUp(object sender, MouseUIElementEventArgs e) 
     { 
     e.Handled = true; 
     } 

    } 

    } 

} 

ensuite utiliser, importer l'espace de noms.

<Button sv:UIElementService.HandleMouseEvents="True" /> 

ou

<ContentControl sv:UIElementService.HandleMouseEvents="True"> 
    <Button Content="Click Test" Padding="2" Command="{Binding TestCommand}"/> 
</ContentControl> 

Je n'ai pas testé (pas de temps pour le moment). Je crois que l'action obtiendra toujours les événements de la souris, même si elle est désactivée.

HTH,

Dennis

+0

Cela ne compile pas, et si vous le refactornez juste assez pour le compiler, cela ne fonctionne pas. –

+0

@KellyElton, il dit dans la réponse "Je n'ai pas testé cela (pas de temps pour le moment)." Quelles erreurs avez-vous eu? Peut-être que je peux aider. – Dennis

+0

Oui j'étais conscient que vous ne l'avez pas testé. Les erreurs sont des erreurs de temps de compilation ... une fois que vous les avez corrigées et que vous les avez faites fonctionner, le code corrigé ne résout pas le problème. –

0

J'ai créé un comportement (qui fonctionne) qui insère un ContentPresenter entre le Button et son parent. Le ContentPresenter avale les clics de souris lorsque le Button est désactivé. Le comportement utilise deux méthodes d'extension basées sur le code this answer. Son utilisation est très simple:

<Button ...> 
    <i:Interaction.Behaviors> 
     <behaviors:SwallowMouseClicksWhenDisabled /> 
    </i:Interaction.Behaviors> 
</Button> 

Et voici la source:

// the behavior (could also be an attached behavior) 
public class SwallowMouseClicksWhenDisabled : Behavior<FrameworkElement> 
{ 
    protected override void OnAttached() 
    { 
     var oldParent = AssociatedObject.Parent; 

     oldParent.RemoveChild(AssociatedObject); 

     var newParent = new ContentPresenter { Content = AssociatedObject }; 

     oldParent.AddChild(newParent); 

     newParent.PreviewMouseDown += OnPreviewMouseEvent; 

     newParent.PreviewMouseUp += OnPreviewMouseEvent; 
    } 

    private void OnPreviewMouseEvent(object sender, MouseButtonEventArgs e) 
    { 
     e.Handled = AssociatedObject.IsEnabled == false; 
    } 
} 

// the extension methods 
public static class DependencyObjectExtensions 
{ 
    public static void AddChild(this DependencyObject parent, UIElement child) 
    { 
     var panel = parent as Panel; 
     if (panel != null) 
     { 
      panel.Children.Add(child); 
      return; 
     } 

     var decorator = parent as Decorator; 
     if (decorator != null) 
     { 
      decorator.Child = child; 
      return; 
     } 

     var contentPresenter = parent as ContentPresenter; 
     if (contentPresenter != null) 
     { 
      contentPresenter.Content = child; 
      return; 
     } 

     var contentControl = parent as ContentControl; 
     if (contentControl != null) 
     { 
      contentControl.Content = child; 
      return; 
     } 

     // maybe more 
    } 

    public static void RemoveChild(this DependencyObject parent, UIElement child) 
    { 
     var panel = parent as Panel; 
     if (panel != null) 
     { 
      panel.Children.Remove(child); 
      return; 
     } 

     var decorator = parent as Decorator; 
     if (decorator != null) 
     { 
      if (Equals(decorator.Child, child)) 
      { 
       decorator.Child = null; 
      } 
      return; 
     } 

     var contentPresenter = parent as ContentPresenter; 
     if (contentPresenter != null) 
     { 
      if (Equals(contentPresenter.Content, child)) 
      { 
       contentPresenter.Content = null; 
      } 
      return; 
     } 

     var contentControl = parent as ContentControl; 
     if (contentControl != null) 
     { 
      if (Equals(contentControl.Content, child)) 
      { 
       contentControl.Content = null; 
      } 
      return; 
     } 

     // maybe more 
    } 
} 
Questions connexes