2012-03-14 18 views
5

Quando l'utente seleziona valori da una casella combinata, se scelgono un valore, l'evento "SelectionChanged" si attiva e il nuovo valore viene impostato e tutto va bene. Se, tuttavia, decidono di non modificare il valore e fare clic in un altro punto dell'interfaccia utente (come una casella di testo che desiderano modificare), devono fare clic due volte: il primo clic semplicemente chiude la finestra popup e il clic successivo focalizzerà l'attenzione elemento che volevano attivare al primo clic.Come ottenere una combobox per impostare correttamente lo stato attivo direttamente dopo la chiusura del popup

Come posso impedire al popup della casella combinata di dirottare il target di messa a fuoco al primo clic in quel modo?

Ho provato a monitorare l'evento ComboBox_LostFocus, ma questo si attiva nel momento sbagliato. Quando l'utente fa clic sul menu a discesa e viene visualizzato l'elenco popup, l'evento ComboBox_LostFocus si attiva: sta perdendo l'attenzione sul proprio elenco a discesa. Non voglio fare nulla per cambiarlo. Quando l'utente fa clic e il popup si chiude, il ComboBox non riprende mai l'attenzione (l'attenzione è solo "persa" su tutto) e quindi questo evento è inutile.

+0

Ciao @Alain, il problema qui è che stai cercando di deviare dal solito comportamento di un controllo standard .. Anche se pensi che sia meglio farlo nel modo in cui descrivi, sarà incoerente con il modo in cui le persone vengono utilizzate alle combobox che funzionano, che nella maggior parte dei casi è una cattiva idea. – joshuahealy

+1

Il vero problema è che l'utente si lamenta di "aver bisogno di fare clic due volte prima che l'interfaccia utente risponda dopo aver visualizzato un elenco di caselle combinate". Se torno indietro all'utente e gli dico che è di design, troveranno un altro designer. – Alain

+0

Ecco perché ho detto nella maggior parte dei casi ... come tutti abbiamo avuto a che fare con i clienti prima ... Speriamo che un guru del WPF possa aiutare! – joshuahealy

risposta

5

penso che potrei aver trovato una soluzione. I combobox hanno un evento DropDownClosed - il problema è che non si tratta di un oggetto RoutedEvent, quindi non è possibile creare uno stile per i ComboBox e far sì che tutti ereditino l'evento tramite un EventSetter. (È possibile ottenere l'errore 'DropDownClosed' must be a RoutedEvent registered with a name that ends with the keyword "Event")

Tuttavia, il Loaded evento è un RoutedEvent, in modo che possiamo collegare in che nello stile:

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

Ora che abbiamo un evento che sarà attivo sempre, prima di Tutto il resto è fatto con la ComboBox, siamo in grado di agganciare in caso abbiamo effettivamente a cuore:

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

Ora che ho finalmente l'accesso alla manifestazione che viene generato quando la discesa si sta chiudendo, Sono in grado di eseguire tutte le azioni necessarie per assicurarmi che il focus sia terminato sul fastidioso ComboBox. Nel mio caso, il seguente:

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

Ho avuto un ComboBox come modello di una DataCell in un GridView, e questo particolare problema è stato impedendo il DataRow di finire modifica quando l'utente si aprì una casella combinata poi cliccato al di fuori di la griglia.

Questo è stato il mio più grande problema con questo bug.Un problema secondario che impostava lo stato attivo in questo evento iff l'utente ha fatto clic. Anche la casella combinata potrebbe essere stata chiusa solo perché l'utente ha colpito la scheda o l'escape, quindi non possiamo semplicemente impostare la messa a fuoco sulla posizione del mouse. Avremmo bisogno di maggiori informazioni su cosa ha causato l'evento DropDownClosed. Probabilmente significa aggancio ad altri eventi non alterati nel gestore di eventi _Loaded.

2

C'è un evento 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(); 
} 

Questo sarà solo impostare attenzione a qualsiasi controllo di cui hanno cliccato. Se fanno clic su un controllo TextBox, ad esempio, il punto di inserimento si troverà a sinistra anziché sul punto in cui è stato fatto clic. Se fanno clic su un altro ComboBox, si concentrerà lì, ma non mostrerà il suo popup. Tuttavia, sono sicuro che potresti gestire questi casi in questo gestore di eventi, se necessario.

MODIFICA: Whoops, stai usando WPF! Non importa, allora; questo è come lo faresti in WinForms. Tuttavia, hai ancora l'evento DropDownClosed in WPF.

EDIT 2: sembra farlo. Non ho familiarità con WPF quindi non so quanto sia robusto, ma si concentrerà su un TextBox, per esempio. Questa è un'app WPF predefinita con una finestra chiamata MainWindow. Quando si chiude la discesa del comboBox, sarà messa a fuoco il controllo più in alto attivabile in corrispondenza della posizione del mouse che non è 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

Punti per lo sforzo, serio. – Alain

Problemi correlati