Обновление привязок

65

В примере из предыдущей статьи (где TextBox.Text привязывается к TextBlock.FontSize) имеется еще один нюанс. При попытке изменить отображаемый размер шрифта, вводя значение в текстовое поле, ничего не происходит. Изменение не применяется до тех пор, пока не будет совершен переход на другой элемент. Это поведение отличается от поведения, которое демонстрировалось в примере с ползунком. Там новый размер шрифта применялся после перетаскивания ползунка в другую позицию, т.е. в переходе на другой элемент вообще не было необходимости.

Чтобы понять это различие, следует повнимательнее присмотреться к выражениям привязки, которые используются этими двумя элементами управления. Когда применяется привязка OneWay или TwoWay, измененное значение распространяется от источника к цели немедленно. В случае с ползунком есть однонаправленное выражение привязки в TextBlock. Таким образом, изменения в свойстве Slider.Value немедленно отражаются в свойстве TextBlock.FontSize. То же поведение имеет место в примере с текстовым полем: изменения в источнике (которым является TextBlock.FontSize) немедленно влияют на цель (TextBox.Text).

Однако изменения, протекающие в обратном направлении — от цели к источнику — не обязательно происходят немедленно. Вместо этого их поведение управляется свойством Binding.UpdateSourceTrigger, которое принимает одно из значений, описанных ниже. Когда текст берется из текстового поля и используется для обновления свойства TextBlock.FontSize, это пример обновления цель-источник, которое использует поведение UpdateSourceTrigger.LostFocus.

PropertyChanged

Источник обновляется немедленно, когда изменяется целевое свойство

LostFocus

Источник обновляется немедленно, когда изменяется целевое свойство и цель теряет фокус

Explicit

Источник не обновляется, пока не будет вызван метод BindingExpression.UpdateSource()

Default

Поведение обновления определяется метаданными целевого свойства (формально — его свойства FrameworkPropertyMetadata.DefaultUpdateSourceTrigger). Для большинства свойств поведением по умолчанию будет PropertyChanged, хотя свойство TextBox.Text обладает поведением по умолчанию LostFocus

Помните, что эти значения не оказывают эффекта на обновление цели. Они просто управляют тем, как обновляется источник в привязках TwoWay и OneWayToSource.

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

<TextBox Name="txt" Margin="10" MinHeight="26" VerticalContentAlignment="Center"
       Text="{Binding ElementName=sld, Path=Value, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
Автоматическое обновление привязки

Поведением по умолчанию свойства TextBox.Text является LostFocus просто потому, что текст в текстовом поле будет изменяться непрерывно в процессе пользовательского ввода, вызывая множественные обновления. В зависимости от того, как исходный элемент управления обновляет себя, режим обновления PropertyChanged может сделать приложение более медлительным. Вдобавок это может заставить исходный объект обновлять себя до завершения редактирования, что создаст проблемы при проверке достоверности.

Для полного контроля над моментом обновления исходного объекта можно выбрать режим UpdateSourceTrigger.Explicit. Если воспользоваться этим подходом в примере с текстовым полем, то когда текстовое поле утратит фокус, ничего не произойдет.

Вместо этого код должен будет вручную инициировать обновление. Например, можно было бы добавить кнопку Add (Добавить), которая вызовет метод BindingExpression.UpdateSource(), инициируя немедленное обновление размера шрифта. Разумеется, прежде чем можно будет вызвать метод BindingExpression.UpdateSource(), нужен способ получения объекта BindingExpression. Объект BindingExpression — это тонкая упаковка, которая содержит в себе две вещи: уже известный объект Binding (предоставленный через свойство BindingExpression.ParentBinding) и объект, привязанный от источника (BindingExpression.DataItem).

Вдобавок объект BindingExpression предоставляет два метода для запуска немедленного обновления одной части привязки: UpdateSource() и UpdateTarget().

Для получения объекта BindingExpression используется метод GetBindingExpression(), унаследованный каждым элементом от базового класса FrameworkElement, которому передается целевое свойство, имеющее привязку.

Пройди тесты
Лучший чат для C# программистов