In the previous post in this series, I introduce the reader to a new attached property that gives the ability to attach a collection of behaviors to an item through a style. Since that post, I have discovered a bug in the original implementation that prevented databindings inside the behavior to work correctly.
Rather than do a walk through, here is the full, revised version of the StylizedBehaviors class:
#region
using System;
using System.Windows;
using System.Windows.Interactivity;
#endregion
namespace LivingAgile.Common.Presentation.WPF.Behaviors
{
public class StylizedBehaviors
{
#region Fields (private)
private static readonly DependencyProperty BehaviorIdProperty =
DependencyProperty.RegisterAttached(
@"BehaviorId", typeof ( Guid ), typeof ( StylizedBehaviors ), new UIPropertyMetadata( Guid.Empty ) );
#endregion
#region Fields (public)
public static readonly DependencyProperty BehaviorsProperty = DependencyProperty.RegisterAttached(
@"Behaviors",
typeof ( StylizedBehaviorCollection ),
typeof ( StylizedBehaviors ),
new FrameworkPropertyMetadata( null, OnPropertyChanged ) );
#endregion
#region Static Methods (public)
public static StylizedBehaviorCollection GetBehaviors ( DependencyObject uie )
{
return ( StylizedBehaviorCollection )uie.GetValue( BehaviorsProperty );
}
public static void SetBehaviors ( DependencyObject uie, StylizedBehaviorCollection value )
{
uie.SetValue( BehaviorsProperty, value );
}
#endregion
#region Static Methods (private)
private static Guid GetBehaviorId ( DependencyObject obj )
{
return ( Guid )obj.GetValue( BehaviorIdProperty );
}
private static int GetIndexOf ( BehaviorCollection itemBehaviors, Behavior behavior )
{
int index = -1;
Guid behaviorId = GetBehaviorId( behavior );
for ( int i = 0; i < itemBehaviors.Count; i++ )
{
Behavior currentBehavior = itemBehaviors[ i ];
if ( currentBehavior == behavior )
{
index = i;
break;
}
Guid cloneId = GetBehaviorId( currentBehavior );
if ( cloneId == behaviorId )
{
index = i;
break;
}
}
return index;
}
private static void OnPropertyChanged ( DependencyObject dpo, DependencyPropertyChangedEventArgs e )
{
var uie = dpo as UIElement;
if ( uie == null )
{
return;
}
BehaviorCollection itemBehaviors = Interaction.GetBehaviors( uie );
var newBehaviors = e.NewValue as StylizedBehaviorCollection;
var oldBehaviors = e.OldValue as StylizedBehaviorCollection;
if ( newBehaviors == oldBehaviors )
{
return;
}
if ( oldBehaviors != null )
{
foreach ( var behavior in oldBehaviors )
{
int index = GetIndexOf( itemBehaviors, behavior );
if ( index >= 0 )
{
itemBehaviors.RemoveAt( index );
}
}
}
if ( newBehaviors != null )
{
foreach ( var behavior in newBehaviors )
{
Guid behaviorId = GetBehaviorId( behavior );
if ( behaviorId == Guid.Empty )
{
behaviorId = Guid.NewGuid( );
SetBehaviorId( behavior, behaviorId );
}
int index = GetIndexOf( itemBehaviors, behavior );
if ( index < 0 )
{
var clone = ( Behavior )behavior.Clone( );
Guid cloneId = GetBehaviorId( clone );
if ( behaviorId != cloneId )
{
}
itemBehaviors.Add( clone );
}
}
}
}
private static void SetBehaviorId ( DependencyObject obj, Guid value )
{
obj.SetValue( BehaviorIdProperty, value );
}
#endregion
}
}
The major change is that we now copy a cloned version of the behavior to the item that inherits the style. This seems to be necessary because the behavior gets the incorrect inheritance context since it really lives inside a freezable collection. Cloning the behavior seems to clear out the inheritance context and make things work correctly. To support that change, I had to add tracking ID's to the behaviors that are attached so they can be related during the remove operation.
Happy databinding on behaviors inside styles!