ComboBox- O evento SelectionChanged tem um valor antigo, não um valor novo

90

C #, .NET 4.0, VS2010.

Novo no WPF. Eu tenho uma ComboBox na minha MainWindow. Eu enganchei o evento SelectionChanged da referida caixa de combinação. No entanto, se eu examinar o valor da caixa de combinação no manipulador de eventos, ele terá o valor antigo. Isso soa mais como um evento "SelectionChanging" do que um evento SelectionChanged.

Como obtenho o novo valor da ComboBox após a seleção realmente acontecer?

Atualmente:

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

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

Observe, obtenho o mesmo comportamento se usar o objeto que está sendo passado no argumento de evento, egeOriginalSource.

Matt
fonte
2
Acabei de me deparar com o mesmo problema - obrigado! Isso é realmente um bug e deveria ter sido nomeado SelectionChangingem primeiro lugar?
janeiro

Respostas:

109

De acordo com o MSDN e.AddedItems:

Obtém uma lista que contém os itens selecionados.

Então você pode usar:

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

Você também pode usar SelectedItemse usar stringvalores para o Itemsde sender:

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

ou

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

Uma vez que Contente SelectedItemsão objetos, uma abordagem mais segura seria usar em .ToString()vez deas string

SwDevMan81
fonte
11
interessante ... tem o novo valor. E RemovedItems tem o antigo. Esse nome de evento é um pouco, se um nome impróprio, pelo menos IMHO. Quando vejo SelectionChanged, espero que o estado do objeto, bem, tenha mudado. Posso ver como isso nos dá um pouco mais de informações.
Matt
1
Sim, acho que é porque a mudança já ocorreu, mas ainda não se comprometeu? Isso é apenas um palpite. Você pode conseguir o texto do item selecionado, veja minha edição.
SwDevMan81
3
ComboBox.SelectedItemnão tem uma propriedade chamada Text, mas você pode fazer ComboBox.SelectedItem as string(embora isso só funcione se você usar stringpara Items- não testado nada mais)
musefan
Apenas string text = (string) e.AddedItems [0];
Igor Semin
Não complique as coisas sem motivo. Usando a propriedade SelectedValue, você pode obter facilmente um valor ComboBox selecionado como este: YourComboBoxName.SelectedValue.ToString (). Nos bastidores, a propriedade SelectedValue é definida como: SelectedValue {get; set;} isso significa que você pode usá-lo para obter ou definir o valor de uma ComboBox. Usar SelectedItem não é uma maneira eficiente de obter um valor de ComboBox, pois requer muitas ramificações.
Sam Tomashi
59

O valor correto a ser verificado aqui é a propriedade SelectedItem .

Um ComboBox é um controle composto com duas de suas partes sendo:

  1. A Parte do Texto : o valor desta parte corresponde à propriedade Texto do ComboBox.
  2. A parte do seletor (ou seja, a parte "suspensa"): O item selecionado nesta parte corresponde à propriedade SelectedItem .

Peças ComboBox Expandidas

A imagem acima foi obtida imediatamente após a expansão do ComboBox (ou seja, antes de selecionar um novo valor). Neste ponto, Text e SelectedItem são "Info", assumindo que os itens do ComboBox eram strings. Se os itens ComboBox fossem, em vez disso, todos os valores de um Enum chamado "LogLevel", SelectedItem seria atualmente LogLevel.Info .

Quando um item no menu suspenso é clicado, o valor de SelectedItem é alterado e o evento SelectionChanged é gerado. A propriedade Text ainda não foi atualizada, já que a parte do texto não é atualizada até que o manipulador SelectionChanged seja concluído. Isso pode ser observado colocando um ponto de interrupção no manipulador e olhando para o controle:

ComboBox no ponto de interrupção no manipulador SelectionChanged

Como a parte do texto ainda não foi atualizada, a propriedade Text retorna o valor selecionado anteriormente.

Dave Kidder
fonte
2
A expansão completa e ajudou a perceber que meu Binding estava na propriedade Text em vez de no SelectedItem correto.
cmousset de
1
@DaveKidder Ótimo exemplo! +1
Ryan Wilson
46

Use o evento DropDownClosed em vez de selectionChanged se desejar o valor atual da caixa de combinação.

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

É realmente tão simples.

escondido
fonte
10
@jvelez Acho que não dispara ao usar um teclado.
NoviceProgrammer
isso é péssimo. NoviceProgrammer que sabia ...!
oculto em
10

Isso funcionou para mim:

private void AppName_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
   ComboBoxItem cbi = (ComboBoxItem)AppName.SelectedItem;
   string selectedText = cbi.Content.ToString();
}
Ramon
fonte
de alguma forma, apenas SelectedItem é preenchido com o novo item, não SelectedValue.
mauris
7

Isso funcionou para mim:

private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
    var text = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;            
}
Бранко Пејић
fonte
Isto é muito importante. A resposta aceita não mostra explicitamente que sendercontém o correto SelectedItem.
Jess
3

O evento seguinte é disparado para qualquer alteração do texto na ComboBox (quando o índice selecionado é alterado e quando o texto é alterado pela edição também).

<ComboBox IsEditable="True" TextBoxBase.TextChanged="cbx_TextChanged" />
Petr Voborník
fonte
1
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
    string newItem = ((DataRowView) e.AddedItems[0]).Row.ItemArray[0].ToString();
}
Buratino
fonte
5
Não forneça respostas apenas de código. Explique por que sua solução é a resposta.
Lee Taylor
1

A segunda opção não funcionou para mim porque o elemento .Text estava fora do escopo (C # 4.0 VS2008). Esta foi a minha solução ...

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

Eu precisava resolver isso no VB.NET. Aqui está o que eu tenho que parece funcionar:

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
zzMzz
fonte
0

É estranho que SelectedItem mantenha os dados novos, enquanto SelectedValue não. Parece um bug para mim. Se seus itens no Combobox forem objetos diferentes de ComboBoxItems, você precisará de algo assim: (my ComboBoxcontains KeyValuePair)

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.SelectedItempode ser nulo, enquanto o Visual Studio fica me dizendo que a KeyValuePairnão pode ser nulo. É por isso que lancei o SelectedItempara um anulável KeyValuePair<string, string>?. Então eu verifico se selectedItemtem um valor diferente de null. Essa abordagem deve ser aplicável a qualquer tipo de seu item selecionado.

desculpemissjackson
fonte
0

Se você realmente precisa do SelectionChangedevento, a melhor resposta é a resposta de SwDevMan81. No entanto, se você está começando com WPF, então você pode querer aprender como fazer as coisas da maneira WPF, que é diferente dos velhos dias do Windows Forms que costumavam depender de eventos como SelectionChanged, com WPF e o padrão Model View ViewModel, você deve usar ligações. Aqui está um exemplo de código:

// 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"); }
    }

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

O uso de e.AddedItems[0] as kProjectwhere kProject é uma classe que mantém os dados trabalhados para mim, já que era padronizado para RemovedItems [0] antes de eu fazer essa distinção explícita. Obrigado SwDevMan81 pelas informações iniciais que responderam a esta pergunta para mim.

Kyjote
fonte
0

Não complique as coisas sem motivo. Usando a propriedade SelectedValue, você pode obter facilmente um valor ComboBox selecionado como este: YourComboBoxName.SelectedValue.ToString ().

Nos bastidores, a propriedade SelectedValue é definida como: SelectedValue {get; set;} isso significa que você pode usá-lo para obter ou definir o valor de uma ComboBox.

Usar SelectedItem não é uma maneira eficiente de obter um valor de ComboBox, pois requer muitas ramificações.

Sam Tomashi
fonte
0

Você pode verificar a propriedade SelectedIndex ou SelectedValue ou SelectedItem no evento SelectionChanged do controle Combobox.

user7347514
fonte
-2

Isso deve funcionar para você ...

int myInt= ((data)(((object[])(e.AddedItems))[0])).kid;
soulexit
fonte
2
Você pode explicar como isso responde à pergunta?
Nathan Tuggy
-3

Resolvi isso usando o evento DropDownClosed porque ele dispara um pouco depois que o valor é alterado.

user5028920
fonte