ComboBox - SelectionChanged事件具有旧值而不是新值

108

C#,.NET 4.0,VS2010。

我是WPF的新手。 我在我的MainWindow上有一个ComboBox。 我已经连接了该组合框的SelectionChanged事件。 但是,如果我在事件处理程序中检查组合框的值,则会显示旧值。 这更像是一个“SelectionChanging”事件,而不是SelectionChanged事件。

在选择实际发生后,如何获取ComboBox的新值?

当前:

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

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

注意,如果我使用事件参数中传递的对象(例如e.OriginalSource),我会获得相同的行为。


2
我刚刚遇到了同样的问题 - 谢谢!这实际上是一个错误吗?它一开始应该被命名为*SelectionChanging*吗? - Jan
检查ComboBox.OnSelectionChanged方法的源代码,显示它发布事件,然后处理所选项目。在一个相关的问题中,我使用反射来强制它从我的SelectionChanged事件处理程序中处理所选项目。 - Tony Pulokas
19个回答

127
根据MSDN文档,e.AddedItems表示:

获取一个包含已被选择项的列表。

因此可以使用以下代码:

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

如果您使用string值为sender中的Items,您也可以使用SelectedItem

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

由于 ContentSelectedItem 都是对象,更安全的方法是使用 .ToString() 而不是 as string


13
有趣......它具有新的值。RemovedItems 具有旧值。那个事件名称有点不准确,至少在我看来是这样。当我看到 SelectionChanged 时,我期望对象状态已经改变。不过我可以理解,这样做会给我们略微更多的信息。 - Matt
1
是的,我认为这可能是因为更改已经发生,但尚未提交?这只是一个猜测。你可以尝试获取所选项目的文本,看看我的编辑。 - SwDevMan81
3
ComboBox.SelectedItem 没有名为 Text 的属性,但可以使用 ComboBox.SelectedItem as string(如果您在 Items 中使用的是 string,则可能仅适用于此 - 未测试其他情况)来达到相同的效果。 - musefan
2
只需将文本字符串 text = (string)e.AddedItems [0]; - Igor Semin
2
希望我能给两个赞,你的回答在两个不同的场合都帮了我两次忙。 - Upulie Han
显示剩余2条评论

79
这里需要检查的正确值是SelectedItem属性。
ComboBox是一个复合控件,它有两个部分:
  1. 文本部分:该部分的值对应于ComboBox的Text属性。
  2. 选择器部分(即“下拉”部分):该部分中选定的项目对应于SelectedItem属性。

Expanded ComboBox Parts

上面的图片是在ComboBox展开后(即在选择新值之前)拍摄的。此时,假设ComboBox的项是字符串,则文本SelectedItem均为“Info”。如果ComboBox的项代表名为“LogLevel”的枚举类型的所有值,则SelectedItem目前将是LogLevel.Info
当下拉列表中的某个项被单击时,SelectedItem的值会更改,并引发SelectionChanged事件。但是,Text属性尚未更新,因为Text部分要等到SelectionChanged处理程序完成后才会更新。可以通过在处理程序中设置断点并查看控件来观察到这一点:

ComboBox at breakpoint in SelectionChanged handler

由于此时尚未更新文本部分,因此文本属性将返回先前选择的值。


3
完整的解释让我意识到我的绑定是在 Text 属性上而不是正确的 SelectedItem 上。 - cmousset
2
@DaveKidder 很好的示例!+1 - Ryan Wilson

54

如果你想获取组合框的当前值,请使用DropDownClosed事件,而不是SelectionChanged事件。

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

真的那么简单。


10
我认为使用键盘时它不会触发。 - NoviceProgrammer
这太糟糕了。新手程序员也知道......! - hidden
这对我有用。令人发笑的是,当选择发生变化时,很难轻松地获取所选值。我相信这背后有一个很好的理由,但天哪。 - Action Dan

11

这对我有用:

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

不知何故,只有SelectedItem会填充新项,而SelectedValue没有。 - mauris

8
这对我起作用了:
private void OnMyComboBoxChanged(object sender, SelectionChangedEventArgs e)
{
    var text = ((sender as ComboBox).SelectedItem as ComboBoxItem).Content as string;            
}

这非常重要。被接受的答案没有明确显示sender包含正确的SelectedItem - Jess
这对我抛出了一个异常:System.NullReferenceException:“对象引用未设置为对象的实例。”(...作为System.Windows.Controls.ComboBoxItem)返回了null。 - Ali Safari
@Jess - 我完全同意。被接受的答案对我没用,而这个答案确实有用。 - BoundForGlory

6

ComboBox中的文本发生任何更改时都会触发以下事件(当选定的索引更改和通过编辑更改文本时也会触发)。

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

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

使用 e.AddedItems[0] as kProject 这一语句,其中 kProject 是一个保存数据的类,在我明确区分之前它默认为 RemovedItems[0]。感谢 SwDevMan81 提供的最初信息,为我解答了这个问题。


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

7
请不要仅提供代码答案。请解释为什么您的解决方案是正确答案。 - Lee Taylor
System.InvalidCastException:“无法将类型为'System.String'的对象强制转换为类型'System.Data.DataRowView'。” - Ali Safari

1
第二个选项对我没用,因为.Text元素超出了作用域(C#4.0 VS2008)。这是我的解决方案...
string test = null;
foreach (ComboBoxItem item in e.AddedItems)
{
   test = item.Content.ToString();
   break;
}

1
如果你真的需要 SelectionChanged 事件,那么最好的答案是SwDevMan81 的答案。然而,如果你刚开始使用 WPF ,那么你可能想要学习如何以 WPF 方式操作,这与旧的 Windows Forms 不同,它们过去依赖像 SelectionChanged 这样的事件,使用 WPF 和 Model View ViewModel 模式,你应该使用绑定。以下是代码示例:
// 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"); }
    }

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接