WPF ComboBox SelectedItem

Ok работает с WPF некоторое время, но мне нужна помощь.

у меня есть ComboBox, как показано ниже:

<TabControl>
    <TabItem Header="1">
        <ComboBox ItemsSource="{Binding MyList}" SelectedItem="{Binding MyListSelection}"/>
    </TabItem>
    <TabItem Header="2"/>
</TabControl>

всякий раз, когда я удаляюсь от вкладки 1, а затем возвращаюсь к ней, выбор удаляется. Я думаю, причина этого в том, что элементы управления разрушаются, когда они выходят из поля зрения, а затем возвращаются. Но в процессе этого SelectedItem становится null, что на самом деле не то, что хотел пользователь, это событие из-за жизненного цикла пользовательского интерфейса.

так что я интересно, какой маршрут лучше всего выбрать? Я создаю это приложение с MVVM, чтобы я мог игнорировать вызов set на свойстве MyListSelection в моей ViewModel, но у меня есть ComboBoxes повсюду и мне не нравится изменять мою ViewModel для того, что я считаю ошибкой WPF.

Я мог бы подкласс WPF ComboBox, но нет события SelectedItemChanging я могу добавить только обработчик при изменении SelectedItem.

какие идеи?

обновление:

ладно, после ударившись головой о стену, я понял, почему моя проблема не может быть воспроизведена. Если тип элемента списка по какой-то причине является классом, SelectedItem получает значение null от WPF, но если это тип значения, это не так.

вот мой тестовый класс(VMBase - это просто абстрактный класс, который реализует INotifyPropertyChanged):

public class TestListViewModel : VMBase
{
    public TestListViewModel()
    {
        TestList = new List<TestViewModel>();
        for (int i = 0; i < 10; i++)
        {
            TestList.Add(new TestViewModel(i.ToString()));
        }
    }

    public List<TestViewModel> TestList { get; set; }

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

public class TestViewModel : VMBase
{
  public string Name {get;set;}
}

поэтому, когда я меняю TestList на тип int и иду туда и обратно между вкладками SelectedItem остается тем же. Но когда он имеет тип TestViewModel SelectedTest получает значение null, когда tabitem выходит из фокуса.

что происходит?

7 ответов


У меня точно такая же проблема, и до сих пор я не мог понять в чем проблема. Я тестировал на 4 разных машинах с одной и той же ОС, версией .Net и спецификациями оборудования и мог воспроизвести проблему в двух из них, в других работал просто отлично. Обходной путь, который я мог бы найти, работает для меня, чтобы определить привязку SelectedItem перед ItemsSource. Странно, если я следую этой схеме, все работает так, как ожидалось. Тем не менее, вы просто должны сделать следующий:

<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>

Я бы рекомендовал проверить привязки. Если что-то еще в вашем приложении изменяет выбранный элемент или источник элементов, ваша привязка будет нарушена. Вы также можете посмотреть в Visual Studio в окне вывода, чтобы увидеть, если есть какие-либо ошибки.


Я думаю, что вам может не хватать здесь TwoWay привязки к SelectedItem. Когда вы привязываете свой класс ViewModel, который содержит MyList(связанный ItemsSource) и MyListSelection (связь с SelectedItem в вашем случае), всегда будет иметь эту информацию, даже если вы перешли на другие вкладки. Поэтому, когда вы вернетесь на эту вкладку, MyListSelection будет привязываться к ComboBoc.SelectedItem снова, и вы будете хорошо. Попробуй и дай мне знать.


отредактировано после изменения в OP. Привет, Хосе, я не могу воспроизвести ошибку, которую вы упомянули. Таким образом, ваше предположение об уничтожении контроля неверно. Combobox ведет себя так, как ожидалось, с кодом ниже, даже теперь он использует ссылочный тип. При изменении 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;
        }
    }
}

Я думаю, что это можно решить с помощью простой проверки null.

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

это потому, что ComboBox имеет тенденцию сбрасывать свой SelectedIndex при рециркуляции. Эта простая проверка null заставит его повторно привязаться к последнему допустимому элементу.


У меня была точно такая же проблема со ссылочным типом в моем списке. Решение состояло в том, чтобы переопределить Equals() в моем TestViewModel, чтобы WPF мог выполнить проверку равенства значений (вместо проверки ссылки) между объектами, чтобы определить, какой из них является SelectedItem. У моего было поле ID, которое действительно было идентифицирующей особенностью TestViewModel.


Это поведение с помощью combobox должно быть реализовано компилятором лучше, чем есть... Т. е. компилятор должен проверить и посмотреть, будут ли типы для ItemsSource и ссылочное значение типа свойства, к которому привязан SelectedItem, когда-либо возвращать сопоставимое значение

Он должен предупредить, что вы можете рассмотреть возможность переопределения методов Equals() и GetHashCode ()...

потратил много времени на это сегодня !!