Подтвердить что ты не робот

WPF MVVM ComboBox SelectedItem или SelectedValue не работает

Обновление

После нескольких исследований. Кажется, что проблема заключается в том, что SelectedValue/SelectedItem происходит до того, как исходный объект Item завершил загрузку. Если я сижу в точке останова и жду несколько секунд, он работает так, как ожидалось. Не знаю, как я собираюсь обойти этот.

Окончательное обновление

У меня есть приложение, использующее в WPF с использованием MVVM с ComboBox. Ниже приведен пример ViewModel. Проблема, с которой я столкнулась, - это когда мы покидаем нашу страницу и возвращаем обратно, ComboBox не выбирает текущее значение, которое выбрано.

Показать модель

public class MyViewModel
{
     private MyObject _selectedObject;
     private Collection<Object2> _objects;
     private IModel _model;

     public MyViewModel(IModel model)
    {
         _model = model;
         _objects = _model.GetObjects();
    }

    public Collection<MyObject> Objects
    {
         get
         {
              return _objects;
         }
         private set
         {
              _objects = value;
         }
     }

     public MyObject SelectedObject
     {
          get
          {
              return _selectedObject;
          }
          set
          {
               _selectedObject = value;
          }
      }
 }

Для этого примера можно сказать, что MyObject имеет два свойства (текст и идентификатор). Мой XAML для ComboBox выглядит следующим образом.

XAML

<ComboBox Name="MyComboBox" Height="23"  Width="auto" 
    SelectedItem="{Binding Path=SelectedObject,Mode=TwoWay}" 
    ItemsSource="{Binding Objects}"
    DisplayMemberPath="Text"
    SelectedValuePath="Id">

Независимо от того, каким образом я настрою это, когда вернусь на страницу, и объект будет повторно собран, ComboBox не будет выбирать значение. Объект возвращает правильный объект через get в свойстве.

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

4b9b3361

Ответ 1

Пробовали ли вы реализовать INotifyPropertyChanged в своей модели viewmodel, а затем поднять событие PropertyChanged, когда набор SelectedItem установлен?

Если это само по себе не исправить, тогда вы сможете вручную поднять событие PropertyChanged самостоятельно при переходе на страницу, и этого должно быть достаточно, чтобы WPF мог перерисовать себя и показать правильный выбранный пункт.

Ответ 2

Настройка IsSynchronizedWithCurrentItem="True" работала для меня!

Ответ 3

Вам нужно поместить свойство ItemsSource перед свойством SelectedItem. Несколько дней назад я наткнулся на блог, отметив проблему.

Ответ 4

У меня были подобные проблемы, и это было решено, убедившись, что я правильно выполняю IEquatable. Когда происходит привязка, он пытается увидеть, соответствуют ли объекты, поэтому убедитесь, что вы правильно выполняете проверку равенства.

Ответ 5

В этом случае привязка selecteditem не работает, потому что хэш-идентификатор объектов отличается.

Одно из возможных решений:

На основе идентификатора выбранного элемента восстановите объект в коллекции itemsource и установите для него свойство выбранного элемента.

Пример:

<ctrls:ComboBoxControlBase SelectedItem="{Binding Path=SelectedProfile, Mode=TwoWay}" ItemsSource="{Binding Path=Profiles, Mode=OneWay}" IsEditable="False" DisplayMemberPath="Name" />

Свойство, привязанное к ItemSource, следующее:

public ObservableCollection<Profile> Profiles
{
   get { return this.profiles; }
   private set { profiles = value; RaisePropertyChanged("Profiles"); }
}

Свойство, привязанное к SelectedItem, равно:

public Profile SelectedProfile 
{
    get { return selectedProfile; }
    set
    {
        if (this.SelectedUser != null)
        {
            this.SelectedUser.Profile = value; 
            RaisePropertyChanged("SelectedProfile");  
        } 
    } 
}

Код восстановления:

    [Command("SelectionChanged")]
    public void SelectionChanged(User selectedUser)
    {
        if (selectedUser != null)
        {
            if (selectedUser is User)
            {
                if (selectedUser.Profile != null)
                {
                    this.SelectedUser = selectedUser;
                    this.selectedProfile = this.Profiles.Where(p => p.Id == this.SelectedUser.Profile.Id).FirstOrDefault();
                    MessageBroker.Instance.NotifyColleagues("ShowItemDetails"); 
                }
            }
        }            
    }

Надеюсь, это вам поможет. Я потратил много времени на поиск ответов, но я не мог найти.

Ответ 6

При выходе из текущей страницы очищается CollectionView, связанная с свойством ItemsSource ComboBox. И поскольку свойство ComboBox IsSyncronizedWithCurrent истинно по умолчанию, свойства SelectedItem и SelectedValue reset.
Это, по-видимому, является проблемой внутреннего типа данных в привязке. Как было предложено выше, если вы используете SelectedValue вместо этого, привязавшись к свойству int на viewmodel, он будет работать. Яркой для вас было бы переопределить оператор Equals в MyObject, чтобы при сравнении двух объектов MyObject сравнивались фактические свойства Id.

Другой намек: если вы реструктурируете свои модели просмотра и используете SelectedValue, используйте его только тогда, когда SelectedValuePath=Id, где Id есть int. Если вы используете строковый ключ, привязывайтесь к свойству Text ComboBox вместо SelectedValue.

Ответ 7

Я тоже заметил это поведение. Я заметил, что свойство SelectedIndex не вызывает такой же ошибки. Если вы можете реструктурировать ViewModel, чтобы выставить индекс выбранного элемента и привязать его к этому, вам должно быть хорошо идти.

Ответ 8

У меня была та же проблема. Дело в том. Выбранный элемент не знает, какой объект он должен использовать из коллекции. Поэтому вы должны сказать выбранному элементу, чтобы использовать элемент из коллекции.

public MyObject SelectedObject
 {
      get
      {
          Objects.find(x => x.id == _selectedObject.id)
          return _selectedObject;
      }
      set
      {
           _selectedObject = value;
      }
 }

Надеюсь, это поможет.

Ответ 9

У меня есть очень простой ответ на эту проблему. Сначала добавьте следующий код в View IsSynchronizedWithCurrentItem = "True".

Далее, когда вы назначаете новый объект в ViewModel для этого свойства SelectedObject, необходимо сохранить его в этом Свойстве, а не в частном члене.

Прозвище viewmodel должно выглядеть следующим образом

    public Role SelectedObject 
    {
        get { return object; }
        set
        {
            if (value != null)
            {
                if (!object.Equals(value))
                {
                    object = value;
                    OnPropertyChanged(() => SelectedObject );
                }
            }
        }
    }

Это должно устранить проблему.

Ответ 10

Я некоторое время боролся с этой проблемой. В моем случае я использовал в сложном типе (List) как Item Item и использовал KeyType в качестве выбранного значения. В событии загрузки KeyType получает значение null. Это заставило все сломаться. Ни один из вспомогательных элементов не будет обновляться при изменении ключа. Оказалось, что когда я добавил чек, чтобы убедиться, что предлагаемое значение для KeyType не было нулевым, все работало, как ожидалось.

    #region Property: SelectedKey
    // s.Append(string.Format("SelectedKey : {0} " + Environment.NewLine, SelectedKey.ToString()));

    private KeyType _SelectedKey = new KeyType();
    public KeyType SelectedKey
    {
        get { return _SelectedKey; }
        set
        {
            if(value != null )
                if (!_SelectedKey.Equals(value))
                {
                    _SelectedKey = value;
                    OnPropertyChanged("SelectedKey");
                }
        }
    }
    #endregion SelectedKey

Ответ 11

Тип SelectedValuePath и SelectedValue должен быть ТОЧНО одинаковым.

Если, например, тип SelectedValuePath равен Int16, а тип свойства, который привязан к SelectedValue, равен int, он не будет работать.

Я трачу часы, чтобы найти это, и вот почему я отвечаю здесь после того, как много времени задал вопрос. Возможно, это может показаться другим бедным парнем, похожим на меня с той же проблемой.

Ответ 12

У меня была эта проблема с ComboBox, отображающим список цветов (List <Brush> ).
Выбор цвета был возможен, но он не отображался при закрытии выделения (хотя свойство было изменено!)

Исправление переписывало метод Equals (object obj) для типа, выбранного в ComboBox (Brush), который не был простым, потому что Brush запечатан. Поэтому я написал класс EqualityBrush, содержащий Brush и реализующий Equals:

public class EqualityBrush
{
    public SolidColorBrush Brush { get; set; }

    public override bool Equals(object o)
    {
        if (o is EqualityBrush)
        {
            SolidColorBrush b = ((EqualityBrush)o).Brush;
            return b.Color.R == this.Brush.Color.R && b.Color.G == this.Brush.Color.G && b.Color.B == this.Brush.Color.B;
        }
        else
            return false;
    }
}

Использование списка моего нового класса EqualityBrush вместо обычного класса Brush устраняет проблему!

My Combobox XAML выглядит следующим образом:

<ComboBox ItemsSource="{Binding BuerkertBrushes}" SelectedItem="{Binding Brush, Mode=TwoWay}" Width="40">
    <ComboBox.Resources>
        <DataTemplate DataType="{x:Type tree:EqualityBrush}">
            <Rectangle Width="20" Height="12" Fill="{Binding Brush}"/>
        </DataTemplate>
    </ComboBox.Resources>
</ComboBox>

Помните, что мой "Brush" -Property в ViewModel теперь должен быть типа EqualityBrush!

Ответ 13

Это может быть способ применения DataContext к странице. В WPF каждый раз, когда вы переходите на страницу, все становится повторно инициализированным, конструктор получает вызываемые, загруженные методы, все. поэтому, если вы устанавливаете свой DataContext в своем представлении, вы, несомненно, сбрасываете тот SelectedItem, который выбран пользователем. Во избежание использования свойства KeepAlive ваших страниц.

<Page KeepAlive="True" ...>
   ...
</Page>

Это приведет к запуску только события Loaded при переходе на страницу, которую вы уже посетили. Поэтому вам нужно будет убедиться, что вы устанавливаете DataContext в Initialize (либо извне, либо внутри конструктора), а не Load.

Однако это будет работать только для этого экземпляра страницы. Если вы перейдете к новому экземпляру этой страницы, конструктор будет вызван снова.

Ответ 14

ComboBox.SelectionBoxItem.ToString()

Ответ 15

IsSyncronizedWithCurrent = False заставит его работать.

Ответ 16

Использовать загруженное событие:

private void cmb_Loaded(object sender, RoutedEventArgs e) {
    if (cmb.Items.Count > 0) cmb.SelectedIndex = 0;          
}

Это работает для меня.

Ответ 17

Я решил проблему, добавив диспетчера в событие UserControl_Loaded

 Dispatcher.BeginInvoke(DispatcherPriority.Loaded, new Action(() =>
 {
     combobox.SelectedIndex = 0;
 }));