Привязать текстовое поле к словарю с помощью переменного ключа

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

Я пытаюсь реализовать двустороннюю привязку данных с помощью ReactiveUI.

Проблема, с которой я столкнулся, заключается в том, что мне, кажется, разрешено привязывать текстовые поля к записи в словаре, только если я использую литеральную строку в качестве ключа, но как только я пытаюсь использовать строковую переменную для настройки привязки, Я получаю следующую ошибку:

Index expressions are only supported with constants.

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

string PropNum = "22017";
this.Bind(ViewModel, vm => vm.ExcelData[PropNum], view => view.tbCreate.Text);

В этом примере ExcelData — это Dictionary<string, object>, определенный в ViewModel.

С другой стороны, следующее работает нормально:

this.Bind(ViewModel, vm => vm.ExcelData["22017"], view => view.tbCreate.Text);

Мне нужно, чтобы ключ поиска был переменной. Любые способы достижения этого результата?

Спасибо


person Luc Morin    schedule 22.08.2018    source источник


Ответы (1)


Нет, вы не можете делать то, что хотите, с помощью Bind().

Мы используем Expression>, чтобы определить, откуда должна исходить привязка. Как сказано в сообщении об ошибке, вы должны использовать константы, если хотите использовать индексы.

Чтобы дать вам представление о том, что делает Expression, Expression позволяет вам использовать сведения о лямбда-выражении, предоставляемые пользователем.

this.Bind(this.ViewModel, vm => vm.Property1.Property2.Property3)

С Expression вы получаете подробную информацию о каждом свойстве/объекте на этом пути.

Что мы делаем, так это подписываемся на событие PropertyChanged каждого объекта по пути (если объект не нулевой), а также соответствующим образом устанавливаем значения для другого свойства.

У выражения есть ограничение на то, что оно должно быть постоянным, поскольку это в основном функция времени компиляции.

С точки зрения того, чего вы хотите достичь, вам придется делать все после вашего динамического значения. Таким образом, вы можете подписаться на события PropertyChange после разрешения индекса ExcelData.

Вы потенциально можете использовать WhenAnyValue() для разрешенного объекта,

myViewModel.ExcelData[propertyIndex].WhenAnyValue(x => x.Value).BindTo(this.ViewModel, view => view.TextBox.Text);
this.WhenAnyValue(view => view.TextBox.Text).BindTo(myViewModel.ExcelData[propertyIndex], x => x.Value);

Вам нужны два BindTo из-за того, что он OneWayOnly, и я использовал предположение, что у вас есть свойство Value в вашем объекте ExcelData.

Имейте в виду, что ваш объект ExcelData, содержащийся в propertyIndex, также должен быть производным от INotifyPropertyChanged.

person Glenn Watson    schedule 22.08.2018
comment
Я могу ошибаться, и документы не на 100% ясны по этому поводу, но разве BindTo не является только односторонней привязкой? - person Luc Morin; 22.08.2018
comment
Да, это правильно, но ничто не мешает вам сделать два утверждения. Что делает Bind/WhenAnyValue, так это то, что они берут Expression‹T›, из которого они затем считывают каждое свойство, содержащееся внутри, и подписываются на изменения свойств (у нас есть механизм, который позволяет нам определить лучший механизм привязки). Так что ваша техника использования непостоянного значения там не сработает. Таким образом, вам фактически остается построить эту цепочку самостоятельно, и вы можете сделать это, создав цепочки WhenAnyValue. В прошлом я создавал методы расширения, чтобы помочь с этим типом сценария. - person Glenn Watson; 22.08.2018
comment
Я видел, как перегрузка принимает Expression, но я признаю, что мне нужно увидеть какой-нибудь пример, чтобы понять, как его использовать. Можете ли вы отредактировать свой ответ с помощью примера кода? - person Luc Morin; 22.08.2018
comment
Ответ есть ответ, то, что вы хотите, невозможно с Bind(), вам придется использовать какой-то другой механизм для получения обновлений свойств. - person Glenn Watson; 22.08.2018
comment
Я согласен, но я безуспешно пытался заставить ваше предложение ViewModel.ExcelData[Row].WhenAnyValue() работать. Ничего не срабатывает. Если вы делали что-то подобное в прошлом, не могли бы вы обновить свой ответ некоторым кодом? - person Luc Morin; 22.08.2018
comment
Помогает ли измененный ответ? - person Glenn Watson; 22.08.2018
comment
Я думаю, что ваша последняя фраза пригвоздила меня. Как упоминалось в моем OP, ExcelData имеет тип Dictionary<string, object>, поэтому нет INPC. Я должен пересмотреть свой подход, если требуется INPC. - person Luc Morin; 22.08.2018
comment
Что забавно, так это то, что Bind, используя литеральный ключ, улавливает изменения в объекте, даже если он не реализует INPC. - person Luc Morin; 22.08.2018
comment
Да, BindTo() будет принимать только изменения значений, а затем привязываться. Bind() также захватит начальное значение для типов POCO. Вы можете просто добиться того же, установив значение непосредственно на свой целевой элемент управления. - person Glenn Watson; 22.08.2018
comment
Гленн, то, что вы говорите о получении начального значения, очень интересно. Знаете ли вы ресурсы, которые помогли бы мне узнать об этих скрытых жемчужинах знаний о RxUI? Я не против купить книги, если они нужны. - person Luc Morin; 23.08.2018
comment
Кент Бугарт (один из моих товарищей по поддержке RxUI) выпустил книгу в начале этого года. Стоит получить копию и пройти через множество этих трюков kent-boogaart.com/you -i-и-реактивуи - person Glenn Watson; 23.08.2018