How to Add a Blend Behavior in a Style Setter

Use behavior in style

There are basically two different ways of implementing behaviours in WPF, commonly referred to as attached behaviours and Blend behaviours.

An attached behaviour is simply an attached property with a PropertyChangedCallback change handler attached to it that performs some action on or extends the DependencyObject to which it is attached when the value of the dependency property changes.

You typically define an attached behaviour as a static class with a set of static methods that get and set the value of the dependency property and perform the desired logic as a result of the callback being invoked. You could easily set the value of any such properties in a Style:

<Setter Property="local:EmptyToNaBehaviour.SomeProperty" Value="x"/>

A Blend behaviour provides a better way of encapsulating the functionality of a behaviour compared to an ordinary attached behaviour. They are also more design friendly easily as they can be easily attached to visual elements in the UI via drag-drop functionality in Blend. But I am afraid you cannot really add a Blend behaviour to a Style setter:

How to add a Blend Behavior in a Style Setter

So if you want to be able to apply your behaviour in the context of a Style, you should write an attached behaviour instead of a Blend behaviour.

Edit: Your behaviour is a Blend behaviour. Replace it by an attached behaviour:

public class EmptyToNaBehaviour
{
public static string GetText(TextBlock textBlock)
{
return (string)textBlock.GetValue(IsBroughtIntoViewWhenSelectedProperty);
}

public static void SetText(TextBlock textBlock, string value)
{
textBlock.SetValue(IsBroughtIntoViewWhenSelectedProperty, value);
}

public static readonly DependencyProperty IsBroughtIntoViewWhenSelectedProperty =
DependencyProperty.RegisterAttached(
"Text",
typeof(string),
typeof(EmptyToNaBehaviour),
new UIPropertyMetadata(null, OnTextChanged));

private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TextBlock txtblock = d as TextBlock;
if(txtblock != null)
txtblock.Loaded += Txtblock_Loaded;
}

private static void Txtblock_Loaded(object sender, RoutedEventArgs e)
{
TextBlock txtblock = sender as TextBlock;
string text = GetText(txtblock);
if (txtblock != null && !string.IsNullOrEmpty(text) && string.IsNullOrEmpty(txtblock.Text))
{
txtblock.Text = text;
}
}
}

Usage:

<TextBlock Grid.Row="0"  Grid.Column="1" Text="text"
Behaviors:EmptyToNaBehaviour.Text="N/A">
</TextBlock>

Or in a Style:

<Style x:Key="style" TargetType="TextBlock">
<Setter Property="Behaviors:EmptyToNaBehaviour.Text" Value="N/A" />
</Style>

How to add style setters as an attached behaviour from a static resource

A Style is not a DependencyObject so you won't be able to set your StyleSetters attached property on a Style.

Is it possible to apply Blend behavior using style or template?

I ran into the same problem and I posted on my blog on how to create an attached property to work around this shortcoming of the Blend SDK.

Move Interaction.Behaviors to Style, Template or something else

I have discovered that the following WORKS. and solves the question from many places, I will start answering, Try the following!

<Page
x:Class="Example1.ListBoxTest"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Example1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
mc:Ignorable="d">

<Page.Resources>
<i:BehaviorCollection x:Key="behaviors">
<core:EventTriggerBehavior EventName="Tapped">
<core:InvokeCommandAction Command="{Binding SetModeToAll}" />
</core:EventTriggerBehavior>
</i:BehaviorCollection>

<Style TargetType="TextBlock" x:Key="textblockstyle">
<Setter Property="i:Interaction.Behaviors" Value="{StaticResource behaviors}">
</Setter>
</Style>
</Page.Resources>

<Grid x:Name="LayoutRoot" Background="Transparent">
<TextBlock Text="Testing" Foreground="Red" FontSize="20" Style="{StaticResource textblockstyle}">
</TextBlock >
</Grid>

It is curious, if I set the behaviors inside the propery i:Interaction.Behaviors does not work but in this way the Command is called!!
Please tell me it works you too!

WPF How to add blend interaction trigger to style resource

So I must admit that I had a working answer when I wrote this, but it took me a long time to figure it out so I am posting it here hoping it helps someone else even though it is a very specific scenario.

I am using the MVVM model for my application so I don't want to have code behind the xaml pages. I also wanted a way to have a textbox bind to the IDataErrorInfo properties where the validation for that textbox is triggered through the lostfocus event of the textbox. This event will be bound to a relay command on the viewmodel that will validate the applicable object and add realted errors.

So i needed to have the textbox lostfocus eventcommand take the textbox name (which matches the column names from the database) as a command parameter.

Here is a screen shot of what I am trying to accomplish
WPF Screen Shot of Textbox Validation STyle

Here is how I did it:

First I defined the command on the view model:

Imports GalaSoft.MvvmLight.Command
Private _LostFocusValidateCommand As RelayCommand(Of String)

Public ReadOnly Property LostFocusValidateCommand() As RelayCommand(Of String)
Get
If _LostFocusValidateCommand Is Nothing Then
_LostFocusValidateCommand = New RelayCommand(Of String)(AddressOf LostFocusValidateExecute)
End If
Return _LostFocusValidateCommand
End Get
End Property
Private Sub LostFocusValidateExecute(sParam As String)
NewClient.PropertyValitaion(False, sParam)
End Sub

here is the property validation using IDataErrorInfo (I left out he basic implementation of IDataErrorInfo to save space, leave a comment if you want me to post it)

 Public Sub PropertyValitaion(bAllProperties As Boolean, Optional sProperty As String = "")
'initialize validation helper
Dim vhelper As New ValidationHelper

If bAllProperties Or sProperty = "chrCompany" Then
If String.IsNullOrEmpty(chrCompany) Then
AddError("chrCompany", "You must enter a Company Name")
Else
RemoveError("chrCompany")
End If
End If
If bAllProperties Or sProperty = "chrFirst" Then
If String.IsNullOrEmpty(chrFirst) Then
AddError("chrFirst", "You must enter a First Name")
Else
RemoveError("chrFirst")
End If
End If
If bAllProperties Or (sProperty = "chrPhone1" Or sProperty = "chrPhone1Ext") Then
If String.IsNullOrEmpty(Trim(chrPhone1Ext)) = False And String.IsNullOrEmpty(Trim(chrPhone1)) Then
Me.AddError("chrPhone1", "Provide a phone number or remove extension")
Else
RemoveError("chrPhone1")
End If
If String.IsNullOrEmpty(Trim(chrPhone1)) = False Then
If vhelper.CheckPhoneNumber(Me.chrPhone1) = False Then
Me.AddError("chrPhone1", "Phone 1 format invalid")
Else
RemoveError("chrPhone1")
End If
End If
End If

End Sub

The hard part was figuring out how to define the style. The style is long, sorry, the joys of "readable" xml:

    <Style x:Key="FTC_ValidateTextBox" BasedOn="{x:Null}" TargetType="{x:Type TextBox}">
<Style.Setters>
<Setter Property="FontFamily" Value="Open Sans Condensed"/>
<Setter Property="FontSize" Value="19" />
<Setter Property="Margin" Value="3,3,15,6"/>
<Setter Property="Padding" Value="10,3"/>
<Setter Property="TextWrapping" Value="Wrap" />
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="VerticalAlignment" Value="Center" />
<Setter Property="Background" Value="{StaticResource DetailTextBox}" />
<Setter Property="BorderBrush" Value="{StaticResource MediumGray}" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border Name="Bd" SnapsToDevicePixels="true" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="LostFocus">
<cmd:EventToCommand Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}},Path=DataContext.LostFocusValidateCommand}"
CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type TextBox}},Path=Name}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="{StaticResource MediumRed}" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<AdornedElementPlaceholder Name="parentTextBox" />
<TextBlock Grid.Row="1" Style="{StaticResource FTC_DetailError}"
Text="{Binding ElementName=parentTextBox, Path=AdornedElement.(Validation.Errors).CurrentItem.ErrorContent}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
<Setter Property="BorderBrush" Value="{StaticResource MediumRed}"/>
<Setter Property="Foreground" Value="{StaticResource MediumRed}"/>
<Setter Property="Margin" Value="3,3,15,31"/>
</Trigger>
</Style.Triggers>
</Style>

<Style x:Key="FTC_DetailError" TargetType="TextBlock">
<Style.Setters>
<Setter Property="FontFamily" Value="Open Sans Condensed"/>
<Setter Property="Control.FontWeight" Value="Light" />
<Setter Property="Foreground" Value="{StaticResource TitleWhite}"/>
<Setter Property="FontSize" Value="15" />
<Setter Property="Margin" Value="0"/>
<Setter Property="Padding" Value="10,3"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="Background" Value="{StaticResource MediumRed}"/>
</Style.Setters>
</Style>

all the magic happens in the property template. THe following must be included in the top declarations of your resource dictionary:

> xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
> xmlns:cmd="http://www.galasoft.ch/mvvmlight"

all the magic happens in the template property that defines the control template. You can not wrap a i:interaction in the control template itself, it must be contained within a derived object, almost anything really, border, scrollviewer, wrappanel etc... Then you set the vent trigger and the command properties. They should be easy enough to follow, I pass the textbox name as the command parameter. The client "box" you see in the screen shot is a grid with its data context set to a new client object property of the parent viewmodel. SO in order to access the command in the parent viewmodel, I had to reference the parent's datacontext and call the command property.

Again, I realize that this is a very specific scenario, but I thought it has some examples that might be able to help others. I am now able to define one style for all textboxes in the application that are data-entry and that I want to trigger basic validation procedures. It will save me having to define the custom command behaviour on all those text boxes individually, and this is all accomplished in xaml, with out code behind.

Cheers

uwp define eventriggerbehavior inside style

My first idea was that it could work this way:

     <Style TargetType="Button">
<Setter Property="i:Interaction.Behaviors">
<Setter.Value>
<i:BehaviorCollection>
<core:EventTriggerBehavior EventName="Click">
<core:InvokeCommandAction Command="{Binding TestCommand}" />
</core:EventTriggerBehavior>
</i:BehaviorCollection>
</Setter.Value>
</Setter>
</Style>

But unfortunately this works only for the first control which gets the behavior attached. The reason is that the value is constructed just once and the AssociatedObject property of the BehaviorCollection is set to the first control.

I have then come over this solution - How to add a Blend Behavior in a Style Setter .
The idea is to create an attached property that manually assigns the behavior to the control.

Based on this I suggest you do it like this:

public static class ListViewBehaviorAttacher
{
public static readonly DependencyProperty IsAttachedProperty = DependencyProperty.RegisterAttached(
"IsAttached", typeof( bool ), typeof( ListViewBehaviorAttacher ), new PropertyMetadata( default( bool ), IsAttachedChanged ) );

private static void IsAttachedChanged( DependencyObject dependencyObject, DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs )
{
var listView = ( ListView )dependencyObject;
//create the binding
BehaviorCollection collection = new BehaviorCollection();
var eventTrigger = new EventTriggerBehavior() { EventName = "ItemClick" };
var invokeCommandAction = new InvokeCommandAction();
//binding to command
BindingOperations.SetBinding(
invokeCommandAction,
InvokeCommandAction.CommandProperty,
new Binding() { Path = new PropertyPath( "ShowItemClickedCommand" ), Source = listView.DataContext } );
eventTrigger.Actions.Add( invokeCommandAction );
collection.Add( eventTrigger );
listView.SetValue( Interaction.BehaviorsProperty, collection );
}

public static void SetIsAttached( DependencyObject element, bool value )
{
element.SetValue( IsAttachedProperty, value );
}

public static bool GetIsAttached( DependencyObject element )
{
return ( bool )element.GetValue( IsAttachedProperty );
}
}

And then in the style attach it like this:

<Style TargetType="ListView">
<Setter Property="SelectionMode" Value="None"></Setter>
<Setter Property="IsItemClickEnabled" Value="True" />
<Setter Property="local:ListViewBehaviorAttacher.IsAttached" Value="True" />
</Style>


Related Topics



Leave a reply



Submit