WPF ComboBox SelectedItem

左心房为你撑大大i 提交于 2019-11-30 14:06:59

I've the exact same problem, and till now I couldn't figure what the problem is. I tested in 4 different machines with the same OS, .Net version and hardware specifications and could reproduce the issue in two of them, in the other ones worked just fine. The workaround I could find that works for me is to define the SelectedItem binding before the ItemsSource. Strangely if I follow this pattern, everything works as expected. That said, you just have to do the following:

<Window x:Class="ComboBoxInTabItemSpike.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <TabControl>
            <TabItem Header="1">
                <ComboBox SelectedItem="{Binding MySelect}" ItemsSource="{Binding MyList}"/>
            </TabItem>
            <TabItem Header="2"/>
        </TabControl>
        <TextBlock Text="{Binding MySelect}"/>
    </StackPanel>
</Window>

I would recommend checking the bindings. If anything else in your app is changing the selected item or the items source, then your binding will break. You can also look in Visual Studio at the output window to see if there are any errors.

I think what you may be missing here is a TwoWay binding on the SelectedItem. When you bind your ViewModel class which contains the MyList(bound ItemsSource) and MyListSelection(Bond to SelectedItem in your Case) will always have those information even though you went to different tabs. So when you come back to this Tab the MyListSelection will bind back to the ComboBoc.SelectedItem again and you will be good. Try this and let me know.

EDITED after change in OP. Hi Jose, I am unable to reproduce the error you mention. So your assumption about the Control being destroyed is wrong. The Combobox behaves as expected with the code behind below even it is now using a reference type. Some other piece of your code must kick in when you change TabItems.

<Window x:Class="ComboBoxInTabItemSpike.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <StackPanel>
        <TabControl>
            <TabItem Header="1">
                <ComboBox ItemsSource="{Binding MyList}"
                          SelectedItem="{Binding MySelect}"/>
            </TabItem>
            <TabItem Header="2"/>
        </TabControl>
        <TextBlock Text="{Binding MySelect}"/>
    </StackPanel>
</Window>

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;

namespace ComboBoxInTabItemSpike
{
    public partial class Window1 : Window, INotifyPropertyChanged
    {
        public Window1()
        {
            InitializeComponent();
            MyList=new ObservableCollection<TestObject>(
                new[]{new TestObject("1"),new TestObject("2"),new TestObject("3") });
            DataContext = this;
        }

        public ObservableCollection<TestObject> MyList { get; set; }

        private TestObject mySelect;
        public TestObject MySelect
        {
            get { return mySelect; }
            set{ mySelect = value;
            if(PropertyChanged!=null)
                PropertyChanged(this,new PropertyChangedEventArgs("MySelect"));} 
        }

        public TestObject MySelectedItem
        {
            get { return (TestObject)GetValue(MySelectedItemProperty); }
            set { SetValue(MySelectedItemProperty, value); }
        }

        public static readonly DependencyProperty MySelectedItemProperty =
            DependencyProperty.Register("MySelectedItem",
                                typeof(TestObject),
                                typeof(Window1),
                                new UIPropertyMetadata(null));

        public event PropertyChangedEventHandler PropertyChanged;
    }

    public class TestObject
    {
        public string Name { get; set; }

        public TestObject(string name)
        {
            Name = name;
        }

        public override string ToString()
        {
            return Name;
        }
    }
}

I think this could be solved with a simple null check.

public TestViewModel SelectedTest
{
    get { return _SelectedTest; }
    set
    {
        if(value != null)
            _SelectedTest = value;
        OnPropertyChanged("SelectedTest");
    }
}

This is because ComboBox has a tendency to reset its SelectedIndex when recycled. This simple null check will force it to rebind to the last valid item.

I was having the exact same problem with a reference type in my list. The solution was to override Equals() in my TestViewModel so that WPF would be able to do a value equality check (instead of a reference check) between the objects to determine which one is the SelectedItem. Mine happened to have an ID field that was really the identifying feature of a TestViewModel.

This behavior by the combobox, should be implemented by the compiler in a better fashion than it is... IE the compiler should check and see if the types for the ItemsSource and the type reference value of the property that the SelectedItem is bound to will EVER return the a value that is comparable

It should warn that you might consider overriding the Equals() and GetHashCode() methods...

Wasted a lot of time on this today !!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!