WPF Binding - должен быть лучший способ, чем я это делаю! - PullRequest
0 голосов
/ 28 октября 2009

Обзор

Я использую CompositeWPF для создания приложения на C #. Это действительно должно изменить ситуацию к ответу, так как моя проблема будет существовать вне Призмы.

У меня есть ItemsControl, который связан с ObservableCollection, содержащей мои ViewModels. Это работает нормально. В моем DataTemplate мои обязательные элементы управления связаны в XAML. Одним из таких элементов управления является кнопка «Конфигурировать». При нажатии на кнопку выдается DelegateCommand, который передает ViewModel в качестве параметра.

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

 private void ShowDialog(object obj)
        {

            ComPortPropertiesPresenter presenter = _container.Resolve<ComPortPropertiesPresenter>();
            ComPortViewModel vm = obj as ComPortViewModel;
            presenter.ComPort = vm.Clone() as ComPortViewModel;

            DialogOptions options = new DialogOptions();
            options.DialogTitle = presenter.View.DisplayName;
            options.HideOkCancelButtons = true;

            bool? result = DialogWorkspace.ShowView(presenter.View, options, () =>
            {
                return true;
            });

            if (result == true)
            {
                // TODO: There must be a better way of doing this.  The problem is if you bind the object the
                // automated bindings cause a realtime update.  If you clone the object and then set it to become obj then
                // the bindings do not update.   Need to investigate how an observablecollection triggers an update.
                int position = ComPorts.IndexOf(obj as ComPortViewModel);
                ComPorts.Remove(obj as ComPortViewModel);
                ComPorts.Insert(position, presenter.ComPort);


            }

Мой оригинальный код передал объект ведущему без клона. Проблема в том, что значение менялось в реальном времени. Если вы внесли изменение, а затем нажали «Отмена», диалоговое окно было бы закрыто, хотя из-за привязок изменения уже произошли.

Я решил клонировать виртуальную машину и передать ее в представление. Если результат диалога был равен true, я попытался скопировать клонированный vm поверх оригинала. Например: obj = Presenter.ComPort;

Это не обновляло значения в исходном представлении, которое содержало кнопку.

Затем я попробовал что-то вроде ... obj.BaudRate = Presenter.ComPort.BaudRate, которое сработало, хотя я боюсь, что оно слишком затянуто, и если виртуальная машина будет расширена, то что-то может пропустить.

Мое окончательное решение состояло в том, чтобы удалить оригинальный vm и добавить новый в местоположение оригинала. Это работает на 100%, хотя я чувствую, что это немного неуклюже. Я чересчур критичен или есть лучший способ?

Я предполагаю, что ObservableCollection запускает событие INotify, когда что-то добавляется / удаляется. То же самое в моем vm, когда свойство обновляется, оно работает, потому что я вызываю событие.

Так может ли быть проблема в том, что если вы перезаписываете что-то в ObservableCollection, событие не генерируется?

Должны быть какие-то умные засорения, которые знают.

1 Ответ

2 голосов
/ 28 октября 2009

Здесь вам нужно понять один из указателей, а не WPF.

Ваша переменная "obj" - это просто сохраненный адрес объекта в памяти. В вашем примере есть 2 ссылки на этот объект. Первая - это ваша переменная obj, вторая - ваша ObservableCollection.

Скажем так. Давайте назовем ваш оригинальный объект "A", а ваш новый объект - "B". В настоящее время ваши ссылки выглядят так:

obj => A
ObservableCollection => A
presenter.ComPort => B

Когда вы говорите obj = Presenter.ComPort, вы на самом деле говорите: «Я больше не хочу ссылаться на объект, на который ссылался obj ... теперь я хочу сослаться на объект, на который указывает Presenter.ComPort» , Теперь все выглядит так:

obj => B
ObservableCollection => A
presenter.ComPort => B

Обратите внимание, что вы ничего не сделали с ObservableCollection. Это отвечает на ваш вопрос «если вы что-то перезаписали ... событие не возникает» ... вы ничего не «перезаписали».

Вот почему вы должны были сделать Remove + Add, чтобы изменить ObservableCollection. Когда вы делаете это, СЕЙЧАС вы что-то изменили в ObservableCollection:

obj => B
ObservableCollection => B
presenter.ComPort => B

На самом деле нет способа обойти это, и то, что вы сделали, правильно. Такова природа ссылок.

Есть способы обойти это, используя «ссылочные ссылки» (передавая ссылку с ключевым словом «ref»), но мне действительно нравится явный характер того, что вы здесь делаете, и я бы посоветовал вам сохранить его.

...