ComboBox- L'evento SelectionChanged ha un valore vecchio, non un nuovo valore


90

C #, .NET 4.0, VS2010.

Nuovo in WPF. Ho un ComboBox nella mia MainWindow. Ho agganciato l'evento SelectionChanged di detta casella combinata. Tuttavia, se esamino il valore della casella combinata nel gestore eventi, ha il vecchio valore. Suona più come un evento "SelectionChanging" che come un evento SelectionChanged.

Come ottengo il nuovo valore del ComboBox dopo che la selezione è effettivamente avvenuta?

Attualmente:

this.MyComboBox.SelectionChanged += new SelectionChangedEventHandler(OnMyComboBoxChanged);

...
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
    string text = this.MyComboBox.Text;
}

Nota, ottengo lo stesso comportamento se utilizzo l'oggetto passato nell'evento args, egeOriginalSource.


2
Sono appena incappato nello stesso problema - grazie! È davvero un bug e avrebbe dovuto essere nominato SelectionChangingin primo luogo?
Jan

Risposte:


109

Secondo MSDN, e.AddedItems:

Ottiene un elenco che contiene gli elementi selezionati.

Quindi potresti usare:

private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
    string text = (e.AddedItems[0] as ComboBoxItem).Content as string;
}

Puoi anche usare SelectedItemse usi stringvalori per Itemsda sender:

private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
    string text = (sender as ComboBox).SelectedItem as string;
}

o

private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
    string text = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;
}

Poiché entrambi Contente SelectedItemsono oggetti, un approccio più sicuro sarebbe usare al .ToString()posto dias string


11
interessante ... ha il nuovo valore. E RemovedItems ha il vecchio. Quel nome dell'evento è un po 'improprio, almeno IMHO. Quando vedo SelectionChanged, mi aspetto che lo stato dell'oggetto, beh, sia cambiato. Posso vedere come questo ci dia un po 'più di informazioni però.
Matt

1
Sì, penso sia perché il cambiamento è avvenuto, ma non è stato commesso? È solo una supposizione. Potresti essere in grado di ottenere il testo dell'elemento selezionato, vedere la mia modifica.
SwDevMan81

3
ComboBox.SelectedItemnon ha una proprietà chiamata Text, ma puoi farlo ComboBox.SelectedItem as string(anche se questo potrebbe funzionare solo se usi stringper Items- non testato nient'altro)
musefan

Solo stringa di testo = (stringa) e.AddedItems [0];
Igor Semin

Non complicare le cose senza motivo. Utilizzando la proprietà SelectedValue è possibile ottenere facilmente un valore ComboBox selezionato come questo: YourComboBoxName.SelectedValue.ToString (). Dietro le quinte la proprietà SelectedValue è definita come: SelectedValue {get; set;} questo significa che puoi usarlo per ottenere o impostare il valore di un ComboBox. L'utilizzo di SelectedItem non è un modo efficiente per ottenere un valore ComboBox poiché richiede molte ramificazioni.
Sam Tomashi,

59

Il valore corretto da controllare qui è la proprietà SelectedItem .

Un ComboBox è un controllo composito con due delle sue parti:

  1. La parte Text : il valore in questa parte corrisponde alla proprietà Text del ComboBox.
  2. La parte selettore (ovvero la parte "a discesa"): l'elemento selezionato in questa parte corrisponde alla proprietà SelectedItem .

Parti ComboBox espanse

L'immagine sopra è stata scattata immediatamente dopo l'espansione del ComboBox (ovvero prima di selezionare un nuovo valore). A questo punto sia Text che SelectedItem sono "Info", presupponendo che gli elementi ComboBox fossero stringhe. Se invece gli elementi ComboBox fossero tutti i valori di un Enum denominato "LogLevel", SelectedItem sarebbe attualmente LogLevel.Info .

Quando si fa clic su un elemento nell'elenco a discesa, il valore di SelectedItem viene modificato e viene generato l' evento SelectionChanged . La proprietà Text non è ancora aggiornata, tuttavia, poiché la parte di testo non viene aggiornata fino al termine del gestore SelectionChanged . Questo può essere osservato inserendo un punto di interruzione nel gestore e guardando il controllo:

ComboBox al punto di interruzione nel gestore SelectionChanged

Poiché la parte di testo non è stata aggiornata a questo punto, la proprietà Text restituisce il valore selezionato in precedenza.


2
Espansione completa e mi ha aiutato a capire che il mio Binding era sulla proprietà Text invece che sul SelectedItem corretto.
cmousset

1
@ DaveKidder Ottimo esempio! +1
Ryan Wilson

46

Utilizzare l'evento DropDownClosed invece di selectionChanged se si desidera il valore corrente della casella combinata.

private void comboBox_DropDownClosed(object sender, EventArgs e)
{
   MessageBox.Show(comboBox.Text) 
}

È davvero così semplice.


10
@jvelez Penso che non si attiverà quando si utilizza una tastiera.
NoviceProgrammer

che fa schifo. NoviceProgrammer che sapeva ...!
nascosto il

10

Questo ha funzionato per me:

private void AppName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
   ComboBoxItem cbi = (ComboBoxItem)AppName.SelectedItem;
   string selectedText = cbi.Content.ToString();
}

in qualche modo solo SelectedItem viene popolato con il nuovo elemento, non SelectedValue.
mauris

7

Questo ha funzionato per me:

private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
    var text = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;            
}

Questo è molto importante. La risposta accettata non mostra esplicitamente che sendercontiene il corretto SelectedItem.
Jess

3

L'evento seguente viene generato per qualsiasi modifica del testo nel ComboBox (quando viene modificato l'indice selezionato e anche quando il testo viene modificato mediante modifica).

<ComboBox IsEditable="True" TextBoxBase.TextChanged="cbx_TextChanged" />

1
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
    string newItem = ((DataRowView) e.AddedItems[0]).Row.ItemArray[0].ToString();
}

5
Si prega di non fornire risposte di solo codice. Fornisci una spiegazione del motivo per cui la tua soluzione è la risposta.
Lee Taylor

1

La seconda opzione non ha funzionato per me perché l'elemento .Text era fuori ambito (C # 4.0 VS2008). Questa era la mia soluzione ...

string test = null;
foreach (ComboBoxItem item in e.AddedItems)
{
   test = item.Content.ToString();
   break;
}

0

Avevo bisogno di risolverlo in VB.NET. Ecco cosa ho che sembra funzionare:

Private Sub ComboBox1_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs) Handles ComboBox_AllSites.SelectionChanged
   Dim cr As System.Windows.Controls.ComboBoxItem = ComboBox1.SelectedValue
   Dim currentText = cr.Content
   MessageBox.Show(currentText)
End Sub

0

È strano che SelectedItem contenga i dati aggiornati, mentre SelectedValue no. Mi sembra un insetto. Se i tuoi articoli nella Combobox sono oggetti diversi da ComboBoxItems, avrai bisogno di qualcosa del genere: (my ComboBoxcontains KeyValuePairs)

var selectedItem = (KeyValuePair<string, string>?)(sender as ComboBox).SelectedItem;
if (!selectedItem.HasValue)
    return;

string selectedValue = selectedItem.Value.Value;  // first .Value gets ref to KVPair

ComboBox.SelectedItempuò essere null, mentre Visual Studio continua a dirmi che a KeyValuePairnon può essere null. Ecco perché eseguo il cast SelectedItemdi un valore nullable KeyValuePair<string, string>?. Quindi controllo se selectedItemha un valore diverso da null. Questo approccio dovrebbe essere applicabile a qualunque tipo sia effettivamente l'elemento selezionato.


0

Se hai davvero bisogno SelectionChangeddell'evento, la risposta migliore è la risposta di SwDevMan81. Tuttavia, se stai iniziando con WPF, potresti voler imparare come fare le cose nel modo WPF, che è diverso dai vecchi tempi di Windows Form che si basavano su eventi come SelectionChanged, con WPF e il modello ViewModel di Model View, dovresti utilizzare le associazioni. Ecco un esempio di codice:

// In the Views folder: /Views/MyWindow.xaml:
// ...
<ComboBox ItemsSource="{Binding MyViewModel.MyProperties, RelativeSource={RelativeSource AncestorType=Window}}"
         SelectedItem="{Binding MyViewModel.MyProperty  , RelativeSource={RelativeSource AncestorType=Window}}" />
// ...



// In the Views folder: /Views/MyWindow.xaml.cs:
public partial class MyWindow : Window
{
    public  MyViewModelClass MyViewModel {
        get { return _viewModel; }
        private set { _viewModel = value;}
    }

    public MyWindow()
    {
        MyViewModel.PropertyChanged += MyViewModel_PropertyChanged;

    }

    void MyViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "MyProperty")
        {
            // Do Work
            // Put your logic here!
        }
    }
}

using System.ComponentModel;

// In your ViewModel folder: /ViewModels/MyViewModelClass.cs:
public class MyViewModelClass : INotifyPropertyChanged
{
    // INotifyPropertyChanged implementation:
    private void NotifyPropertyChanged(string propertyName = "") { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } }
    public event PropertyChangedEventHandler PropertyChanged;

    // Selected option:
    private string _myProperty;
    public  string  MyProperty {
        get { return _myProperty; }
        set { _myProperty = value; NotifyPropertyChanged("MyProperty"); }
    }

    // Available options:
    private List<string> _myProperties;
    public  List<string>  MyProperties {
        get { return _myProperties; }
        set { _myProperties = value; NotifyPropertyChanged("MyProperties"); }
    }

}

0
private void indBoxProject_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    int NewProjID = (e.AddedItems[0] as kProject).ProjectID;
    this.MyProject = new kProject(NewProjID);
    LoadWorkPhase();
}

L'uso di e.AddedItems[0] as kProjectdove kProject è una classe che contiene i dati ha funzionato per me poiché era predefinito per RemovedItems [0] prima di aver fatto questa distinzione esplicita. Grazie SwDevMan81 per le informazioni iniziali che hanno risposto a questa domanda per me.


0

Non complicare le cose senza motivo. Utilizzando la proprietà SelectedValue è possibile ottenere facilmente un valore ComboBox selezionato come questo: YourComboBoxName.SelectedValue.ToString ().

Dietro le quinte la proprietà SelectedValue è definita come: SelectedValue {get; set;} questo significa che puoi usarlo per ottenere o impostare il valore di un ComboBox.

L'utilizzo di SelectedItem non è un modo efficiente per ottenere un valore ComboBox poiché richiede molte ramificazioni.


0

È possibile controllare la proprietà SelectedIndex o SelectedValue o SelectedItem nell'evento SelectionChanged del controllo Combobox.


-2

Questo dovrebbe funzionare per te ...

int myInt= ((data)(((object[])(e.AddedItems))[0])).kid;

2
Puoi spiegare come questo risponde alla domanda?
Nathan Tuggy

-3

Ho risolto questo problema utilizzando l'evento DropDownClosed perché si attiva leggermente dopo la modifica del valore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.