В данной статье постараюсь затронуть работу с Behaviors в
WPF.
Behaviors
–
это поведение представления в WPF.
Поведение (Behavior)
инкапсулирует часть функционального поведения в многократно используемом
компоненте, который в дальнейшем можно
прикрепить к элементу в представлении. Это сделано для того, чтобы все
преимущества паттерна MVVM оставить в первозданном
виде, так как реализацию мы можем вынести в отдельный код и возложить всю
работу на XAML,
не нарушая принципов MVVM.
На сегодняшний день выделяют два типа Behaviors:
2) Blend
Behavior
предназначен для добавления
поведения для UIElement.
Рассмотрим более подробно эти представления.
Attached Behavior
В интернете все примеры по Attached Behavior, который многие
программисты называют просто Attached
Property, сводятся к написанию проверки на ввод в
текстовое поле для того, чтобы разрешать или запрещать вводить в поле ввода (TextBox) только цифры. Примеры
: Example1
и Example2.
Проявим оригинальность и сделаем, например, Attached
Property
Close,
с
помощью которого при нажатии на клавишу escape будем
закрывать форму. Вот как выглядит код реализации:
public static class WindowCloseBehaviour
{
public static void SetClose(DependencyObject target, bool value)
{
target.SetValue(CloseProperty,
value);
}
public static readonly DependencyProperty CloseProperty =
DependencyProperty.RegisterAttached(
"Close",
typeof(bool),
typeof(WindowCloseBehaviour),
new UIPropertyMetadata(false, OnClose));
private static void OnClose(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is bool && ((bool)e.NewValue))
{
Window window = GetWindow(sender);
if (window != null)
{
window.KeyUp -=
WindowKeyUp;
window.KeyUp +=
WindowKeyUp;
};
}
}
private static void WindowKeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape)
{
Window window = sender as Window;
if (window != null)
window.Close();
}
}
private static Window GetWindow(DependencyObject sender)
{
Window window = null;
if (sender is Window)
window = (Window)sender;
if (window == null)
window = Window.GetWindow(sender);
return window;
}
}
Использование
в XAML
будет
выглядеть вот так.
<Window x:Class="WpfApplicationMVVM.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Behaviors="clr-namespace:Behaviors"
Title="Library" Height="350" Width="525"
Behaviors:WindowCloseBehaviour.Close="True">
<Grid>
</Grid>
</Window>
После запуска приложения и нажатия на клавиатуре
клавиши Escape
форма
закроется.
Blend Behavior
Blend Behavior наследуется от класса Behavior<T> и доступно в пространстве System.Windows.Interactivity с Blend SDK . Но обычно этот SDK входит в поставку Visual Studio 2010/2012 и
его можно найти при добавлении ссылки на библиотеку через Refference
-> Add Refference…->Extensions. Для примера напишем простенький пример с использованием класса
Behavior<T>, чтобы отловить потерю
фокуса в контроле и вывести введенный текст в MessageBox. Суть примера - показать, что суть этого поведения - это вынесение логики с представления. Добавляем
пространство имен:
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
<TextBox>
<i:Interaction.Behaviors>
<Behaviors:LostFocusBehavior/>
</i:Interaction.Behaviors>
</TextBox>
EventToCommand
Иногда нужен декоратор, чтобы представить event как
command.
Для этого существует решение преобразования с красноречивым названием EventToCommand. Этот решение доступно при использовании MVVM Light Toolkit , но если вы
используете классическую модель MVVM,
это решение может вам пригодиться.
/// <summary>
/// This <see
cref="System.Windows.Interactivity.TriggerAction" /> can be
/// used to bind any event on
any FrameworkElement to an <see cref="ICommand" />.
/// Typically, this element is
used in XAML to connect the attached element
/// to a command located in a
ViewModel. This trigger can only be attached
/// to a FrameworkElement or a
class deriving from FrameworkElement.
/// </summary>
public class EventToCommand : TriggerAction<FrameworkElement>, ICommandSource
{
/// <summary>
/// EventArgs
/// </summary>
public static readonly DependencyProperty EventArgsProperty = DependencyProperty.Register(
"EventArgs",
typeof(object),
typeof(EventToCommand));
/// <summary>
/// Identifies the <see cref="CommandParameter"
/>
dependency property
/// </summary>
public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register(
"CommandParameter",
typeof(object),
typeof(EventToCommand),
new PropertyMetadata(
null,
(s, e) =>
{
var sender = s as EventToCommand;
if (sender == null)
{
return;
}
if (sender.AssociatedObject == null)
{
return;
}
sender.EnableDisableElement();
}));
/// <summary>
/// Identifies the <see
cref="Command" /> dependency property
/// </summary>
public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
"Command",
typeof(ICommand),
typeof(EventToCommand),
new PropertyMetadata(
null,
(s, e) => OnCommandChanged(s
as EventToCommand, e)));
/// <summary>
/// Identifies the <see
cref="MustToggleIsEnabled" /> dependency property
/// </summary>
public static readonly DependencyProperty MustToggleIsEnabledProperty
= DependencyProperty.Register(
"MustToggleIsEnabled",
typeof(bool),
typeof(EventToCommand),
new PropertyMetadata(
false,
(s, e) =>
{
var sender = s as EventToCommand;
if (sender == null)
{
return;
}
if (sender.AssociatedObject == null)
{
return;
}
sender.EnableDisableElement();
}));
/// <summary>
/// Gets or sets the ICommand that
this trigger is bound to. This
/// is a DependencyProperty.
/// </summary>
public ICommand Command
{
get
{
return (ICommand)GetValue(CommandProperty);
}
set
{
SetValue(CommandProperty, value);
}
}
/// <summary>
/// Allows access to event args in
binding
/// <example>
/// CommandParameter="{Binding
EventArgs, RelativeSource={RelativeSource Self}}"
/// </example>
/// </summary>
public object EventArgs
{
get
{
return GetValue(EventArgsProperty);
}
set
{
SetValue(EventArgsProperty, value);
}
}
/// <summary>
/// Gets or sets an object that will
be passed to the <see cref="Command" />
/// attached to this trigger. This is
a DependencyProperty.
/// </summary>
public object CommandParameter
{
get
{
return this.GetValue(CommandParameterProperty);
}
set
{
SetValue(CommandParameterProperty, value);
}
}
public IInputElement CommandTarget
{
get { return this.AssociatedObject; }
}
/// <summary>
/// Gets or sets a value indicating
whether the attached element must be
/// disabled when the <see
cref="Command" /> property's CanExecuteChanged
/// event fires. If this property is
true, and the command's CanExecute
/// method returns false, the element
will be disabled. If this property
/// is false, the element will not be
disabled when the command's
/// CanExecute method changes. This
is a DependencyProperty.
/// </summary>
public bool MustToggleIsEnabled
{
get
{
return (bool)this.GetValue(MustToggleIsEnabledProperty);
}
set
{
SetValue(MustToggleIsEnabledProperty, value);
}
}
/// <summary>
/// Called when this trigger is
attached to a FrameworkElement.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
EnableDisableElement();
}
/// <summary>
/// Provides a simple way to invoke
this trigger programatically
/// without any EventArgs.
/// </summary>
public void Invoke()
{
Invoke(null);
}
/// <summary>
/// Executes the trigger.
/// <para>To access the EventArgs of
the fired event, use a RelayCommand<EventArgs>
/// and leave the CommandParameter
and CommandParameterValue empty!</para>
/// </summary>
/// <param
name="parameter">The EventArgs of the fired event.</param>
protected override void Invoke(object parameter)
{
EventArgs = parameter;
if (AssociatedElementIsDisabled())
{
return;
}
if (CanExecute())
Execute();
}
private static void OnCommandChanged(
EventToCommand element,
DependencyPropertyChangedEventArgs e)
{
if (element == null)
{
return;
}
if (e.OldValue != null)
{
((ICommand)e.OldValue).CanExecuteChanged -=
element.OnCommandCanExecuteChanged;
}
var command = (ICommand)e.NewValue;
if (command != null)
{
command.CanExecuteChanged +=
element.OnCommandCanExecuteChanged;
}
element.EnableDisableElement();
}
private bool AssociatedElementIsDisabled()
{
return AssociatedObject != null &&
!AssociatedObject.IsEnabled;
}
private void EnableDisableElement()
{
if (AssociatedObject == null)
{
return;
}
if (MustToggleIsEnabled &&
Command != null)
{
AssociatedObject.IsEnabled =
CanExecute();
}
}
private void OnCommandCanExecuteChanged(object sender, EventArgs e)
{
EnableDisableElement();
}
public void Execute()
{
if (Command == null)
return;
var routedCommand = Command as RoutedCommand;
if (routedCommand != null)
routedCommand.Execute(CommandParameter,
CommandTarget);
else
Command.Execute(CommandParameter);
}
public bool CanExecute()
{
if (Command == null)
return false;
var routedCommand = Command as RoutedCommand;
if (routedCommand != null)
return
routedCommand.CanExecute(CommandParameter, CommandTarget);
return Command.CanExecute(CommandParameter);
}
}
Пример
использования:
<Button Content="Add new book" Margin="3" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click" >
<Behaviors:EventToCommand
Command="{Binding AddBookCommand}"
/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
Итоги:
Суть поведений в WPF в том, чтобы поддерживать строгость паттерна MVVM и сократить код в представлении. Поведения особенно полезны при повторном использовании кода. Детальнее со всем вы можете ознакомиться в MSDN
Суть поведений в WPF в том, чтобы поддерживать строгость паттерна MVVM и сократить код в представлении. Поведения особенно полезны при повторном использовании кода. Детальнее со всем вы можете ознакомиться в MSDN
No comments:
Post a Comment