2012-04-09 4 views
6

У меня есть ComboBox, чьи свойства ItemsSource и SelectedValue привязаны к модели. Иногда модели нужно настроить выбранный элемент на другой, но когда я делаю это в модели, значение модели не отражается в представлении, даже если выбранное значение правильно установлено (отмечено как с помощью snoop, так и в SelectionChanged обработчик события).Почему WPF ComboBox не отражает правильное значение привязки?

Чтобы проиллюстрировать эту проблему, вот простой XAML:

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}}"> 
    <Grid> 
     <ComboBox Height="25" Width="120" SelectedValue="{Binding SelectedValue}" SelectedValuePath="Key" ItemsSource="{Binding PossibleValues}" DisplayMemberPath="Value"/> 
    </Grid> 
</Window> 

А вот модель:

using System.Collections.Generic; 
using System.Windows; 
using System.ComponentModel; 

namespace WpfApplication1 
{ 
    public partial class MainWindow : Window, INotifyPropertyChanged 
    { 
     int m_selectedValue = 2; 
     Dictionary<int, string> m_possibleValues = new Dictionary<int, string>() { { 1, "one" }, { 2, "two" }, { 3, "three" }, {4,"four"} }; 

     public int SelectedValue 
     { 
     get { return m_selectedValue; } 
     set 
     { 
      if (value == 3) 
      { 
       m_selectedValue = 1; 
      } 
      else 
      { 
       m_selectedValue = value; 
      } 
      PropertyChanged(this, new PropertyChangedEventArgs("SelectedValue")); 
     } 
     } 
     public Dictionary<int, string> PossibleValues 
     { 
     get { return m_possibleValues; } 
     set { m_possibleValues = value; } 
     } 


     public MainWindow() 
     { 
     InitializeComponent(); 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
    } 
} 

I Ожидаемое поведение быть следующим:

  1. Первоначально выбрано два значения:
  2. Выберите «один» -> ComboBox movea YS "один"
  3. Выберите "два" -> ComboBox отображает "два"
  4. Выберите "три" -> ComboBox отображает "один"
  5. Выберите "четыре" -> ComboBox отображает "четыре"

Однако в №4 отображается «три». Зачем? Значение в модели было изменено на 1 («один»), но в представлении по-прежнему отображается 3 («три»).

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

+0

Не должно ли ComboBox отображать текущее состояние модели? Это может быть неинтуитивно, но это правильная вещь для отображения. Это на самом деле упрощенный пример. Фактический пример использования немного сложнее, но он сводится к той же проблеме. – Rado

+0

Вы пытались сделать SelectedValue явно двусторонним? {Binding Path = SelectedValue, Mode = TwoWay} –

+0

@ Режим JacobProffitt по умолчанию имеет значение TwoWay, но я просто попытался установить его явно, и проблема не устранена. – Rado

ответ

5

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

Система предполагает, что сеттер не изменит значение, которое было передано. Обходной путь здесь прост: используйте Dispatcher.BeginInvoke() или установите IsAsync=True на свое обязательство, чтобы получить поведение, которое вы ищете. Тем не менее, я лично сильно обескураживаю вам это нужно. Не только потому, что он вводит целую кучу новых проблем, связанных с таймингом, но главным образом потому, что это обходной путь к проблеме, которая не должна существовать в первую очередь.

Просто не делайте этого. Это относится не только к WPF: все, что меняет установщик, может ожидать, что значение, которое оно только установило, было правильно установлено, не выбрасывается исключение. Изменение значения внутри сеттера является интуитивно понятным и может укусить вас позже. Кроме того, для пользователя вашего приложения может быть очень неприятно видеть, что выпадающий список игнорирует его выбор.

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

+0

'IsAsync = True' работает. Читая описание, я не уверен, почему он делает, поскольку он не предназначен для этого, но он делает :). Вы упомянули, что вы препятствуете этой реализации. Любые предложения, что я могу сделать вместо этого? Фактический прецедент заключается в следующем: у меня есть предопределенное количество профилей значений, которые я должен попытаться приблизиться. Это очень хорошо сочетается с парадигмой combobox, поскольку пользователь должен выбрать одно из предопределенных значений. Однако есть определенные случаи, когда я не могу достичь выбранного значения, поэтому, чтобы не вводить пользователя в заблуждение, мне нравится менять значение на «промежуточное» значение. – Rado

+0

Чтобы добавить к вышеуказанному комментарию, другой способ думать о том, что делает этот сеттер, - это думать об этом как о «желании». Как и в: «Я хочу установить это значение, но если вы не можете, приблизитесь как можно ближе» – Rado

+1

Я не согласен. Весь смысл иметь свойство «сеттер» - это защита поля поддержки (которое может быть обнародовано). Имущество ДОЛЖНО выполнять проверки и проверку на значение и гарантировать его правильность. Если это не так, бросание исключения не всегда является практическим решением. –

2

У меня были постоянные проблемы с правильным обновлением элемента управления при связывании с SelectedValue и/или SelectedItem свойствами управления ComboBox.

Чтобы решить эту проблему, мне пришлось привязать к свойству SelectedIndex. Было больше хлопот касаться свойства «index» в моей ViewModel, но он решил проблему с обновлением ComboBox.

+0

Я на самом деле пробовал все SelectedValue, SelectedItem и SelectedIndex с тем же результатом. – Rado

Смежные вопросы