2013-09-11 4 views
4

Получил жесткий. Рассмотрим ViewModel, который состоит из списка объектов, где каждый объект определяет значение int, а некоторые из этих объектов также определяют словарь пресетов для ints, введенных на основе «дружественной» строки, представляющей это значение в пользовательском интерфейсе.Каков правильный способ привязки к значению, показывая дружественное имя из списка пресетов, также определенных в этом элементе?

Вот пример ...

List<SomeItem> AllItems; 

public class SomeItem : INotifyPropertyChanged 
{ 
    public SomeItem(int initialValue, Dictionary<int,string> presets) 
    { 
     this.CurrentValue = initialValue; 
     this.Presets = presets; 
    } 
    public int CurrentValue{ get; set; } // Shortened for readability. Assume this property supports INPC 
    public Dictionary<int,string> Presets{ get; private set; } 
} 

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

Наша первая попытка была использовать TextBox и ComboBox, изменяя их обзорности в зависимости от того, есть ли предварительные настройки или нет, как это ...

<ComboBox ItemsSource="{Binding Presets}" 
    DisplayMemberPath="Key" 
    SelectedValuePath="Value" 
    SelectedValue="{Binding CurrentValue, Mode=TwoWay}" 
    Visibility={Binding HasPresets, Converter=...}"> 

<TextBox Text="{Binding CurrentValue}" 
    Visibility={Binding HasPresets, Converter...}" /> // Assume the inverse of the combo 

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

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

Однако, я не уверен, как передать пресеты в конвертер. Вы не можете установить привязку на ConverterParameter, чтобы передать их таким образом, и если вы используете множественную привязку, то я не уверен, как структурировать вызов ConvertBack, поскольку там тоже мне нужно, чтобы они проходили, а не отослал обратно.

Я думаю, правильный способ заключается в реализации UiValue в ViewModel, который мы бы просто связать это понравится ...

<TextBox Text="{Binding UiValue}" /> 

... затем переместить код, который бы уже был в конвертере для реализации этого актива/getter/setter, или просто передайте значение Value, если пресетов нет. Однако, похоже, слишком много логики идет в ViewModel, где он должен находиться в представлении (ala-конвертер или аналогичный.) И снова, может быть, это точно точка ViewModel. Не знаю. Мысли приветствуются.

+0

+1 для очень хорошо объясненной проблемы. – Sheridan

ответ

2

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

Извините, это не очень важный ответ, но я чувствовал, что ваш вопрос заслуживает хотя бы одного.

+0

LOL ... Спасибо! Да, вы и другой ответ, похоже, пришли к такому же выводу, что и я ... поместите его в ViewModel. – MarqueIV

1

Мне нравится ваш вопрос, потому что он иллюстрирует способ мышления, который стоит за существованием ViewModel в WPF. Иногда они кажутся неизбежными.

Преобразователи предназначены для безгражданства, поэтому трудно передать переменные контекста, такие как presets. ViewModel - это слой, в обязанности которого входит подготовка Model для целей привязки. Роль «модели» заключается в обработке логики. Таким образом, ViewModel может подробно описать поведение (логику) View. Это именно то, что вы хотите. Большую часть времени я вообще не нуждаюсь в конвертерах.

Иногда кажется более естественным, что логика вида должна быть в View, но тогда ViewModel кажется излишним. Однако, когда эта логика находится в ViewModel, ее обычно легче автотестировать. Я бы не боялся помещать такие вещи в ViewModel. Часто это самый простой (и правильный) способ.

У UiValue недвижимости в ViewModel и обрабатывать преобразования там:

public string UiValue{ get{/*...*/} set{/*...*/} } 

Перефразируя, в WPF нет чистого способа заменить свойство, связываться. Например. если вы хотите иметь

<TextBox Text="{Binding IntValue}" /> 

изменение в какой-то момент:

<TextBox Text="{Binding PresetValue}" /> 

вы в ловушке. Дело не в этом. Лучше иметь постоянное связывание как

<TextBox Text="{Binding UiValue}" /> 

и иметь дело с логикой за UiValue собственности.

Другой возможный подход (вместо того, чтобы играть с видимостью ComboBox и TextBox) должен иметь DataTemplateSelector, который будет решать, следует ли создавать ComboBox или TextBox для SomeItem. Если presets являются пустыми или пустыми, выберите TextBox DataTemplate, в противном случае возьмите ComboBox. Если я не ошибаюсь, вам придется исследовать свойство FrameworkElement.DataContext из селектора, чтобы найти контекст (presets).

Принимая во внимание ваши сомнения относительно метода ConvertBack, чаще всего value или Binding.DoNothing возвращается в случае, если вам не нужна конвертация в любом из направлений.

+0

Код DataTemplateSelector будет иметь ту же проблему, с которой я столкнулся сейчас ... если значение задано перед ItemsSource, значение будет отбрасываться. (Я подтвердил это в приложении «Playground», которое я использую для таких тестов.) Я думаю, что трюк заключается в привязке к свойству, определенному в представлении, а затем в коде, которое я установил для привязки на ComboBox и т. Д. Я могу убедиться, что они установлены правильно. На самом деле, я могу отказаться от привязки непосредственно к полем со списком и сделать все в настройке View code-behind, определяющей свойство, которое * будет * привязано к ViewModel. Тем не менее, это забавное упражнение. – MarqueIV