2009-11-06 17 views
10

Tengo un botón en mi aplicación que está vinculado a un comando. Este botón se encuentra dentro de otro control que también reacciona a los clics del mouse. Cuando el botón está habilitado, obtengo el comportamiento que esperaba: haga clic en el botón y se activa el comando, haga clic fuera del botón pero dentro del control del contenedor y en su lugar se active.Deshabilitar un botón WPF, pero todavía tragar los eventos de clic

Desafortunadamente cuando el botón está deshabilitado (a través del método CanExecute del comando) los clics en el botón se borran hasta el control del contenedor. No quiero esto, quiero que se traguen los clics, ni desencadenar el comando ni burbujear.

Me trataron de superar esto creó una nueva clase que hereda de botón, pero ninguno de los métodos siguientes, incluso parece conseguir llamados en un botón desactivado:

  • OnPreviewMouseDown
  • OnPreviewMouseUp
  • OnPreviewMouseLeftButtonDown
  • OnPreviewMouseLeftButtonUp
  • OnMouseDown
  • onmouseup
  • OnMouseLeftButtonDown
  • OnMouseLeftButtonUp
  • OnClick

es un control desactivado completamente ignorado por el sistema de eventos de WPF encamina? Y si es así, ¿hay alguna forma de que pueda obtener el comportamiento que estoy buscando?

Respuesta

9

respuesta de RCGoforth me consiguió el 90% del camino, pero la solución no es poner un rectángulo detrás el botón , porque el evento burbujeante sube por el árbol y no por sus hermanos. Al final me rodearon el botón con un ContentControl (puesto del rectángulo no pueden tener hijos), que se tragó el evento antes de que podría subir más lejos:

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

en el código detrás:

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

O para hacer esto completamente en XAML (y para aumentar el nivel de hack del código ...)

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

No es realmente limpio, pero podría colocar un rectángulo transparente detrás de su botón que se traga los eventos de clic.

0

Lo que quiere hacer es registrar un manejador de eventos enrutados usando the overload that takes the handledEventsToo parameter y especificando un valor de verdadero para ese parámetro. De esta forma, su controlador externo recibirá el evento independientemente de si el botón realmente maneja el evento o no. Eso va a ser algo como esto:

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

Y luego en el controlador siempre se puede comprobar lo que se ha hecho clic, ya sea que se manejó, etc a través de la MouseButtonEventArgs se le entregó. Por ejemplo, para comprobar si otro control en realidad maneja el evento ya que puede hacer:

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

Gracias, pero yo he estado buscando en esto y creo que podría haber interpretado mal mi pregunta. No quiero que el control externo * siempre * reciba el evento, quiero que solo reciba el evento si el clic está fuera del botón, incluso si el botón está desactivado. –

+0

Tienes razón, entendí mal tu intención. Voy a revisar. –

0

Un limpiador, la solución más reutilizables sería implementar esta funcionalidad como una propiedad adjunta.

Utilizando el patrón de servicio/acción:

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

    } 

    } 

} 

Luego de usar, importar el espacio de nombres.

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

o

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

no he probado esto (no hay tiempo en el momento). Creo que la acción seguirá recibiendo los eventos del mouse incluso si está deshabilitado.

HTH,

Dennis

+0

Esto no se compila, y si solo lo refactoriza lo suficiente como para compilarlo, no funciona. –

+0

@KellyElton, dice en la respuesta "No he probado esto (no hay tiempo en este momento)". ¿Qué errores obtuviste? Tal vez puedo ayudar. – Dennis

+0

Sí, sabía que no lo había probado. Los errores son errores de tiempo de compilación ... una vez que los arreglas y los ejecutas, el código corregido no resuelve el problema. –

0

que creó un comportamiento (que realmente funciona) que inserta un ContentPresenter entre el Button y su matriz. El ContentPresenter se traga clics del mouse cuando el Button está deshabilitado. El comportamiento utiliza un par de métodos de extensión basados ​​en el código de this answer. El uso es muy simple:

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

Y aquí está la fuente:

// 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 
    } 
} 
Cuestiones relacionadas