Привязка данных отстает от уведомления о событии - обсуждение - PullRequest
5 голосов
/ 02 марта 2011

Обнаружил интересную проблему, которую я впервые обнаружил в WinForms и снова нашел в Silverlight, а также, скорее всего, WPF, когда дело касается привязки данных.

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

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

В этом сценарии пользователь вкладывает элемент управления (давайте используем текстовое поле для этого примера) и обновляеттекстовое окно.На этом этапе привязка данных не сбросила управление, и, следовательно, виртуальная машина еще не видела изменения.Затем пользователь использует свою мышь, чтобы щелкнуть следующую вкладку элемента управления.

На этом этапе все становится интересным.Я использовал PreviewSelectionChanged (Telerik RadTabControl), так как я хочу проверить вещи до того, как произойдет переход к следующей вкладке, и это также дает мне возможность отменить событие.

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

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

Мне кажется, я нашел интересную работу по этому вопросу - но я не уверен на 100%, что это сработает на 100%.время.Я отменяю текущее событие, но затем использую Dispatcher и создаю делегат, указывающий на другой метод с той же сигнатурой, что и у текущего события.Диспетчер добавит это сообщение в рассылку сообщений, которая к этому времени (надеюсь?) Будет за сообщениями об обновлении виртуальной машины ...

Code Snippet delaying the event

Мои два вопросаявляются следующими: 1) Я предполагаю, что элемент управления textbox либо не сбрасывался, когда мышь покидала элемент управления, либо запущенный процесс был слишком медленным, и, следовательно, сообщение предварительного просмотра было на насосе до привязки данных - в любом случае, я вижуэто будет серьезной проблемой.

2) Является ли обходной путь хорошим решением?

Ответы [ 5 ]

2 голосов
/ 12 апреля 2011

Хорошо, первый ответ на вопрос 1:

То, что мышь покинула область текстового поля, не означает, что текстовое поле потеряло фокус. Он теряет фокус только тогда, когда что-то еще фокусируется Например, если вы переместили мышь из текстового поля и щелкнули какой-либо другой элемент управления на своей странице (это может быть что угодно, от средства просмотра прокрутки до другого текстового поля и т. Д.), Тогда ваше текстовое поле потеряет фокус.

Теперь, исходя из этого, события не происходят в неправильном порядке. Что происходит? событие щелчка мышью на другой вкладке приводит к тому, что текстовое поле приводит к потере фокуса (и привязке данных к нему) и к переходу к следующему кадру, и на этом основании вы получаете условие гонки, при котором происходит переход к следующей вкладке. до привязки данных.

О вопросе 2:

Что вы можете сделать, это установить UpdateSourceTrigger в значение Explicit, однако вы будете вынуждены затем иметь какое-то событие text_changed и вручную обновлять привязку.

Вы можете узнать больше об этом здесь . Возможно, это не самое полное объяснение, но это хорошее начало.

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

1 голос
/ 13 апреля 2011

Здесь есть дефект дизайна, и вы пытаетесь обойти его, а не исправлять. Вам не нужно выяснять, как отменить событие Click на вкладке. Во-первых, вкладка не должна обрабатывать события Click.

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

Ваша модель представления должна предоставлять команду для перехода к следующей вкладке, и вкладка должна быть привязана к команде. Метод CanExecute команды должен возвращать true только в том случае, если состояние модели представления на текущей вкладке действительно.

Это не решит вашу другую проблему, которая заключается в том, что Silverlight не поддерживает UpdateSourceTrigger="PropertyChanged" из коробки. Но это решенная проблема ( здесь является одним примером).

Обратите внимание, что если вы реализуете команды для управления этой навигацией, подобной мастеру, в своем приложении, вы можете в будущем изменить представление, чтобы использовать что-то, кроме элемента управления вкладкой (например, использовать кнопки навигации, такие как настоящий мастер, или что-то вроде Telerik's PanelBar ) без необходимости использовать обработчики событий.

1 голос
/ 11 апреля 2011

Просто идея: почему бы не сделать все в событии PropertyChanged виртуальной машины?

protected override void OnThisViewModelPropertyChanged(object sender, PropertyChangedEventArgs e) {
            if(e.PropertyName == "WhateverProperty") {
                //Do your magic here for whatever you want to set
            }
        }

Связать ваши TabItems с коллекцией, которая будет контролировать, отключено или нет.

<sdk:TabControl>
   <sdk:TabItem IsEnabled="{Binding SomeProperty, Converter={AmIDisabledOrWhatConverter}}" />    
</sdk:TabControl>

Таким образом, все запускается всякий раз, когда свойство изменяется в виртуальной машине. Больше никаких проблем с синхронизацией, так как все на виртуальной машине.

Только мои два цента.

0 голосов
/ 13 апреля 2011
    MyOwnTextBox()
    {
        this.TextChanged += (s, e) => UpdateText();
    }

    private void UpdateText()
    {
        BindingExpression be = GetBindingExpression(TextProperty);
        if (be != null && be.ParentBinding.Mode == BindingModes.TwoWay)
        {
            be.UpdateSource();
        }
    }

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

0 голосов
/ 06 марта 2011

Измените ваши привязки, чтобы включить UpdateSourceTrigger = "PropertyChanged".

Это будет гарантировать, что ваши источники данных обновляются при каждом нажатии клавиши, а не только в LostFocus.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...