Here I have a simple Blend Behavior. It has a single DependencyProperty
which I want to do something with during initialization in OnAttached
. But I can't because the behavior appears to be attached before it is even initialized!
Usage:
<Button>
<e:Interaction.Behaviors>
<local:TestBehavior Test1="{Binding ValueOnViewModel}"/>
</e:Interaction.Behaviors>
<Button>
Definition:
class TestBehavior : Behavior<FrameworkObject>
{
public static readonly DependencyProperty Test1Property
= DependencyProperty.Register("Test1", typeof(int), typeof(TestBehavior),
new PropertyMetadata(OnTest1Property_Changed));
private static void OnTest1Property_Changed(DependencyObject sender,
DependencyPropertyChangedEventArgs args)
{
// This gets called _after_ OnAttached!
}
public int Test1
{
get { return (int)GetValue(Test1Property); }
set { SetValue(Test1Property, value); }
}
protected override void OnAttached()
{
base.OnAttached();
// No matter what Test1Property is bound to in XAML 'a' will be default
// value because as this point the Behavior is attached, but not
// initialized!
int a = Test1;
}
}
This strikes me as really bizarre. In this simple case I can get around the issue by performing my initialization in OnTest1Property_Changed instead of in OnAttached, (albeit with the mild inconvenience of being in a static context rather than an instance context).
However, what if I have a rather less trivial Behavior which has multiple properties? In some usages of the behavior all the DP's might be explicitly set, whereas in other cases only some of the DP's might be set while using the default values of the DP's that are not explicitly set. I can handle the change event for all of the DP's the behavior defines, but I have no way of knowing which DP's the client has set explicitly in XAML, so I have no way of knowing with change notifications have to have occurred before initialization is complete.
So how in that non-trivial case can I possibly know when initialization of the behavior is complete? This seems like such a glaring weakness that I can only assume I've missed something obvious.
[Update]
Using Sorskoot's suggestion I knocked up this simple base class which I can use as the base for my Behaviors instead.
public class BehaviorBase<T> : Behavior<T> where T : FrameworkElement
{
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.Loaded += AssociatedObject_Loaded;
}
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.Loaded -= AssociatedObject_Loaded;
}
protected virtual void OnLoaded()
{
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
OnLoaded();
}
}
I would suggest attaching to the Loaded
event during in the OnAttached
method and do you initialization in there.
protected override void OnAttached()
{
base.OnAttached();
base.AssociatedObject.Loaded += onloaded;
}
private void onloaded(object sender, RoutedEventArgs e)
{
int a = Test1;
// Test1 should contain a value here.
}