[Из песочницы] 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, только необходимо указать класс, в котором оно определено. Благодаря обработчику 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
protected override void OnDetaching () { AssociatedObject.Click -= OnClick; }
private void OnClick (object sender, RoutedEventArgs e) { Application.Current.Shutdown (); } } Добавляется следующим кодом: Вот Behavior уже в отличии от Attached Property служит для добавления функциональных возможностей UI элементам. В него можно добавлять любые Dependency Property, и, так как он является частью дерева отображения, Binding тут отлично работает. Так как для WPF принято использовать MVVM, а он, в свою очередь, предполагает минимум кода в теле View, то Behavior тут как нельзя кстати. Эти немногочисленные функциональные блоки, которые нельзя выполнить при помощи стандартного Binding, можно выносить и использовать повсеместно комбинирую друг с другом.Не все объекты Behavior должны быть совместимы друг с другом. Например, мы не сможем совместить CloseButtonBehavior и HelpButtonBehavior. И это, разумеется, нормально.