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. Связывание текстового поля, которое мы делаем, работает правильно.
Ответы
Ответ 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;
}));