Почему метод Convert используется для ConvertBack значения в поле со списком WPF? - PullRequest
0 голосов
/ 06 июля 2011

Точный вопрос - почему вместо ConvertBack используется Convert, и почему действительно ConvertBack нужен здесь, в первую очередь?

Проблема

Ниже ПРИМЕР моей проблемы, я попытался упростить вещи. Это обычный WPF, никаких сторонних библиотек, сама проблема - классическая мастер-деталь с небольшим поворотом.

У меня есть список (список городов) и сетка данных (в которой перечислены все мои друзья + телефоны). Когда я выбираю список, сетка данных должна обновиться и показать ребят из выбранного города. Сетка данных выглядит следующим образом:

Name | Phone | PhoneType
Mark | 76447 | cellphone
...

Ключевой вопрос здесь - это столбец PhoneType. Каждая ячейка должна представлять собой комбинированный список, заполненный предопределенными типами телефонов (выбирается из базы данных). И поворот заключается в структуре БД. Это что-то вроде этого:

typeID | PhoneType | PhoneDescription
1      | cellphone | NULL
2      | neighbour | call only in case of emergency

В моем комбинированном ящике данных должен отображаться PhoneType НО Если присутствует PhoneDescription, его следует использовать вместо обычного PhoneType.

Конец проблемы. Вы со мной?

Осуществление

Чтобы иметь комбинированный список в сетке данных, я должен использовать столбец шаблона с комбинированным списком внутри, а не столбец комбинированного списка данных (вот почему: WPF Datagrid ComboBox DataBinding ). Итак, вот мой комбобокс:

<ComboBox ItemsSource="{Binding Path=PhoneTypesList, 
          RelativeSource={RelativeSource AncestorType={x:Type Window}}}"  

Хорошо, здесь (выше) я определяю, что должно быть указано в выпадающем списке Combobox. Список записей PhoneType (в смысле базы данных).

          SelectedValuePath="typeID"
          SelectedValue="{Binding Path=typeID}">

Combobox должен знать о текущем значении типа телефона моего друга, поэтому здесь (выше), как я связываю эти значения - с одной стороны, я установил, что typeID из записи PhoneType должен использоваться для сопоставления, а с другой вручную я установил, что typeID из записи Friend должен быть использован. Таким образом, WPF знает, какие записи типов телефонов следует использовать в качестве текущего значения.

(Если вы не на 100% знакомы с этим переплетом, вот хорошее объяснение: Разница между SelectedItem, SelectedValue и SelectedValuePath )

Btw. typeID используется дважды, потому что в целом я предпочитаю использовать одно и то же имя для связанных полей (полей внешнего ключа) в базе данных.

У меня есть список, у меня есть сопоставление, теперь - для отображения я не могу использовать только DisplayMemberPath, потому что мне нужно что-то более динамичное.

  <ComboBox.ItemTemplate>
    <DataTemplate>
      <TextBlock Text="{Binding Converter={StaticResource PhoneTypeConverter}}"/>
    </DataTemplate>
  </ComboBox.ItemTemplate>
</ComboBox>

Таким образом, чтобы отобразить PhoneType, я получаю всю запись и выбираю соответствующую строку.

[ValueConversion(typeof(PhoneTypeRecord), typeof(string))]
public class PhoneTypeConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
        if (value == null)
            return null;

        var record = ((PhoneTypeRecord)value); // crash!
        return record.PhoneDescription ?? record.PhoneType;
    }

    // we don't do any conversion back
    public object ConvertBack(object value, Type targetType,
        object parameter, CultureInfo culture)
    {
            return null;
    }
}

Error

Я компилирую все приложение, запускаю его, нажимаю на любой город. Работает как положено, отлично.

Тогда я нажимаю на ДРУГОЙ город. И приложение немного подумает, а затем перед обновлением списка городов или сетки данных оно аварийно завершает работу (см. Отмеченную строку выше), говоря:

Невозможно привести объект типа 'System.String' для ввода 'MyBuilderApp.PhoneTypeRecord'.

И с этим я понятия не имею, что происходит. Почему строка передается в Convert ??? Это больше похоже на ConvertBack.

Ответы [ 2 ]

1 голос
/ 03 августа 2011

Я думаю, что в этом случае вам будет намного проще использовать PriorityBinding . Он будет обрабатывать случай, когда описание имеет значение null, и использовать для вас тип:

<ComboBox>
  <ComboBox.ItemTemplate>
    <DataTemplate>
      <TextBlock>
         <TextBlock.Text>
           <PriorityBinding>
             <Binding Path="PhoneDescription" />
             <Binding Path="PhoneType" />
           </PriorityBinding>
         </TextBlock.Text>
      </TextBlock>
    </DataTemplate>
  </ComboBox.ItemTemplate>
</ComboBox>
0 голосов
/ 06 июля 2011

На мой взгляд, текстовый блок является привязкой к тому, что возвращается из PhoneTypesList. А затем использует конвертер, который ожидает PhoneTypeRecord. Я предполагаю, что PhoneTypesList - это список PhoneTypeRecord, верно? Я заметил, что в конвертере я получаю полный список, а не каждый элемент списка. Таким образом, вы действительно получаете список / коллекцию / observableCollection PhoneTypeRecord. Проверьте это.

Вы также можете добавить подобное условие в конвертер:

if(value != typeof(what_you_are_expecting) throw new InvalidOperationException("something is wrong");
...