WPF: использование Attached Property и Behavior

от автора

Пожалуй, любой разработчик WPF знает о механизме Attached Property, но многие даже не слышали о Behavior. Хотя эти механизмы и имеют схожие функциональные возможности, они, все же, имеют совершенно разную смысловую нагрузку, и очень важно правильно различать их и использовать.

Давайте вспомним, что из себя представляют эти механизмы:

Attached Property. Это Dependency Property, которое объявлено не в классе объекта, для которого оно будет использоваться, но ведет себя, как будто является его частью.

Объявляется в отдельном классе, имеет getter и setter в виде статических методов. Можно добавить обработчик на PropertyChanged событие.

public static class UiConfigurator {     public static readonly DependencyProperty CustomValueProperty = DependencyProperty.RegisterAttached(         "CustomValue", typeof(bool), typeof(UiConfigurator), new PropertyMetadata(false));      public static void SetCustomValue(DependencyObject element, bool value)     {         element.SetValue(CustomValueProperty, value);     }      public static bool GetCustomValue(DependencyObject element)     {         return (bool)element.GetValue(CustomValueProperty);     } } 

Используется так же как и обычное Dependency Property, только необходимо указать класс, в котором оно определено.

<Button testApp:UiConfigurator.CustomValue="True"/> 

Благодаря обработчику PropertyChanged очень часто с помощью этого механизма пытаются добавлять к UI элементу некоторую функциональность. Например, мы хотим запоминать расположение и размер окна между запусками приложения. Делаем Attached Property SaveBounds и добавляем обработчик PropertyChanged. Если установленно в true, то выполняем код по восстановлению/сохранению позиции окна. Правильно ли это? Нет. Давайте посмотрим как Microsoft использует этот механизм у себя. Отличными примерами являются Grid.Column и DockPanel.Dock. Все эти свойства никак не влияют на функциональность объекта, а просто добавляют некоторую информацию, чтобы другие участники дерева отображения смогли более корректно с ним взаимодействовать. Сам же объект об этом ничего не знает и знать не должен. Отсюда следует, что само по себе использование события PropertyChanged для Attached Property уже повод задуматься: а все ли я правильно делаю? Он необходим только в очень редких случаях. Например, так мы может запоминать историю изменения этого свойства, чтобы при необходимости провести более глубокий анализ и взаимодействовать более интеллектуально. Но это из разряда задач “делал один раз в жизни”.

Так как Attached Property не влияет на состояние объекта, то и несколько таких свойств конфликтовать не должны, а значит мы можем навешивать их на него сколько угодно. Например, те же Grid.Column и DockPanel.Dock (хоть это и несколько нелогично) можно легко сочетать. Вот вам и вторая подсказка правильно ли вы используете этот механизм: если вы можете представить код, который может быть написан в целевом объекте или где-либо еще и который будет конфликтовать с вашим свойством – вы что-то сделали не так.

Behavior. В WPF есть абстрактный шаблонный класс Behavior. Сделав наследника, мы можем присоединить его к объекту в дереве отображения. В этом классе есть методы OnAttached и OnDetaching, которые вызовутся в соответствующих случаях.
Ниже пример CloseBehavior, который можно присоединить любой кнопке и сделать ее таким образом кнопкой закрытия приложения.

public class CloseBehavior : Behavior<Button> {     protected override void OnAttached()     {         AssociatedObject.Click += OnClick;     }      protected override void OnDetaching()     {         AssociatedObject.Click -= OnClick;     }      private void OnClick(object sender, RoutedEventArgs e)     {         Application.Current.Shutdown();     } } 

Добавляется следующим кодом:

<Button Content="Close">     <i:Interaction.Behaviors>         <testApp:CloseBehavior/>     </i:Interaction.Behaviors>  </Button> 

Вот Behavior уже в отличии от Attached Property служит для добавления функциональных возможностей UI элементам. В него можно добавлять любые Dependency Property, и, так как он является частью дерева отображения, Binding тут отлично работает. Так как для WPF принято использовать MVVM, а он, в свою очередь, предполагает минимум кода в теле View, то Behavior тут как нельзя кстати. Эти немногочисленные функциональные блоки, которые нельзя выполнить при помощи стандартного Binding, можно выносить и использовать повсеместно комбинирую друг с другом.

Не все объекты Behavior должны быть совместимы друг с другом. Например, мы не сможем совместить CloseButtonBehavior и HelpButtonBehavior. И это, разумеется, нормально.

ссылка на оригинал статьи http://habrahabr.ru/post/254887/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *