2012-03-14 11 views
5

Cuando el usuario selecciona valores de un cuadro combinado, si eligen un valor, se desencadena el evento "SelectionChanged" y se establece el nuevo valor y todo está bien. Sin embargo, si deciden no cambiar el valor y hacer clic en otro lugar en la interfaz de usuario (como un cuadro de texto que desean editar), tienen que hacer clic dos veces: el primer clic simplemente cierra la ventana emergente del cuadro combinado y el siguiente clic enfocará elemento que querían activar en el primer clic.Cómo obtener un cuadro combinado para establecer el foco correctamente directamente después de que se cierra la ventana emergente

¿Cómo puedo evitar que el cuadro emergente combobox secuestrar el objetivo de enfoque en el primer clic de esa manera?

He intentado controlar el evento ComboBox_LostFocus, pero esto se dispara en el momento equivocado. Cuando el usuario hace clic en el menú desplegable y se muestra la lista emergente, el evento ComboBox_LostFocus se dispara; está perdiendo enfoque en su propia lista desplegable. No quiero hacer nada para cambiar eso. Cuando el usuario hace clic y la ventana emergente se cierra, el ComboBox nunca recupera el foco (el foco simplemente se "pierde" en todo) y este evento es inútil.

+0

Hola @Alain, el problema aquí es que estás tratando de desviarse del comportamiento habitual de un control estándar. Incluso si crees que es mejor hacerlo de la manera que describes, será inconsistente con la forma en que se usan las personas Comboboxes funcionando, que en la mayoría de los casos es una mala idea. – joshuahealy

+1

El verdadero problema aquí es que el usuario se queja de que "necesitan hacer clic dos veces antes de que la UI responda después de ver una lista de combobox". Si vuelvo al usuario y les digo que es por diseño, encontrarán otro diseñador. – Alain

+0

Es por eso que dije en la MAYORÍA de los casos ... ya que todos hemos tenido que tratar con clientes antes ... ¡Ojalá algún gurú de WPF pueda ayudar! – joshuahealy

Respuesta

5

Creo que podría haber encontrado una solución. Los cuadros combinados tienen un evento DropDownClosed; el problema es que no es un evento RoutedEvent, por lo que no puede crear un estilo para ComboBoxes y hacer que todos ellos hereden el evento a través de un EventSetter. (Se obtiene el error 'DropDownClosed' must be a RoutedEvent registered with a name that ends with the keyword "Event")

Sin embargo, el Loaded caso es un RoutedEvent, por lo que se puede enganchar en que en el estilo:

<Style x:Key="ComboBoxCellStyle" TargetType="ComboBox"> 
    <EventSetter Event="Loaded" Handler="ComboBox_Loaded" /> 
</Style> 

Ahora que tenemos un evento que siempre se disparará antes cualquier otra cosa que se hace con el cuadro combinado, que puede enganchar en el caso de que realmente se preocupan por:

private void ComboBox_Loaded(object sender, RoutedEventArgs e) 
{ 
    ((ComboBox)sender).DropDownClosed -= ComboBox_OnDropDownClosed; 
    ((ComboBox)sender).DropDownClosed += new System.EventHandler(ComboBox_OnDropDownClosed); 
} 

Ahora que por fin tengo acceso al evento que se activa cuando el menú desplegable se está cerrando, Puedo realizar cualquier acción que necesite para asegurarme de que el enfoque finalice en el molesto ComboBox. En mi caso, lo siguiente:

void ComboBox_OnDropDownClosed(object sender, System.EventArgs e) 
{ 
    FrameworkElement visualElement = (FrameworkElement)sender; 

    while(visualElement != null && !(visualElement is DataCell)) 
     visualElement = (FrameworkElement)visualElement.TemplatedParent; 
    if(visualElement is DataCell) 
    { 
     DataCell dataCell = (DataCell)visualElement; 
     dataCell.EndEdit(); 
     if(!(dataCell.ParentRow is InsertionRow)) dataCell.ParentRow.EndEdit(); 
    } 
} 

que tenía un ComboBox como la plantilla de un DataCELL en un GridView, y este problema particular fue la prevención de la DataRow de terminar de edición cuando el usuario se abrió un ComboBox a continuación, se hace clic fuera del la cuadrícula.

Ese fue mi mayor problema con este error.Un problema secundario al establecer el foco en este evento iff el usuario hizo clic. El combobox también podría haberse cerrado porque el usuario tocó la pestaña o escapó, por lo que no podemos simplemente poner el foco en la posición del mouse. Necesitaríamos más información sobre qué causó que el evento DropDownClosed se disparara. Probablemente significa conectarse a más eventos sin enrutar en el controlador de eventos _Loaded.

2

Hay un caso DropDownClosed:

private void comboBox_DropDownClosed(object sender, EventArgs e) 
{ 
    Point m = Control.MousePosition; 
    Point p = this.PointToClient(m); 
    Control c = this.GetChildAtPoint(p); 
    c.Focus(); 
} 

Esto sólo establecer el foco en lo que han hecho clic en el control. Si hacen clic en un cuadro de texto, por ejemplo, el símbolo de intercalación estará a la izquierda en lugar de donde hicieron clic. Si hacen clic en otro ComboBox, se enfocará allí, pero no mostrará su ventana emergente. Sin embargo, estoy seguro de que podría tratar esos casos en este controlador de eventos si lo necesita.

EDITAR: ¡Vaya, está utilizando WPF! Olvidalo entonces; así es como lo harías en WinForms. Sin embargo, aún tienes el evento DropDownClosed en WPF.

EDIT 2: Esto parece hacerlo. No estoy familiarizado con WPF, así que no sé qué tan robusto es, pero se centrará en un TextBox, por ejemplo. Esta es una aplicación WPF predeterminada con una ventana llamada MainWindow. Cuando se cierra el menú desplegable del ComboBox, que va a centrar el control de más arriba enfocable en la posición del ratón que no se MainWindow:

private void comboBox_DropDownClosed(object sender, EventArgs e) 
{ 
    Point m = Mouse.GetPosition(this); 
    VisualTreeHelper.HitTest(this, new HitTestFilterCallback(FilterCallback), 
     new HitTestResultCallback(ResultCallback), new PointHitTestParameters(m)); 
} 

private HitTestFilterBehavior FilterCallback(DependencyObject o) 
{ 
    var c = o as Control; 
    if ((c != null) && !(o is MainWindow)) 
    { 
     if (c.Focusable) 
     { 
      c.Focus(); 
      return HitTestFilterBehavior.Stop; 
     } 
    } 
    return HitTestFilterBehavior.Continue; 
} 

private HitTestResultBehavior ResultCallback(HitTestResult r) 
{ 
    return HitTestResultBehavior.Continue; 
} 
+1

Puntos por esfuerzo amigo, en serio. – Alain

Cuestiones relacionadas