1: /// <summary>
2: /// Behavior base class that handles a safe unsubscribe and clean up because the default
3: /// behavior class does not always call <see cref="Behavior.OnDetaching"/>.
4: /// <para />
5: /// This class also adds some specific features such as <see cref="ValidateRequiredProperties"/>
6: /// which is automatically called when the behavior is attached.
7: /// </summary>
8: /// <typeparam name="T">The dependency object this behavior should attach to.</typeparam>
9: public abstract class BehaviorBase<T> : Behavior<T> where T : FrameworkElement
10: {
12: private bool _isClean = true;
22: /// <summary>
23: /// Called after the behavior is attached to an AssociatedObject.
24: /// </summary>
25: /// <remarks>Override this to hook up functionality to the AssociatedObject.</remarks>
26: protected sealed override void OnAttached()
27: {
28: base.OnAttached();
29:
30: AssociatedObject.Unloaded += OnAssociatedObjectUnloaded;
31: _isClean = false;
32:
33: ValidateRequiredProperties();
34:
35: Initialize();
36: }
37:
38: /// <summary>
39: /// Called when the behavior is being detached from its AssociatedObject, but before it has actually occurred.
40: /// </summary>
41: /// <remarks>Override this to unhook functionality from the AssociatedObject.</remarks>
42: protected sealed override void OnDetaching()
43: {
44: CleanUp();
45:
46: base.OnDetaching();
47: }
48:
49: /// <summary>
50: /// Validates the required properties. This method is called when the object is attached, but before
51: /// the <see cref="Initialize"/> is invoked.
52: /// </summary>
53: protected virtual void ValidateRequiredProperties()
54: {
55: }
56:
57: /// <summary>
58: /// Initializes the behavior. This method is called instead of the <see cref="OnAttached"/> which is sealed
59: /// to protect the additional behavior.
60: /// </summary>
61: protected virtual void Initialize()
62: {
63:
64: }
65:
66: /// <summary>
67: /// Uninitializes the behavior. This method is called when <see cref="OnDetaching"/> is called, or when the
68: /// <see cref="AttachedControl"/> is unloaded.
69: /// <para />
70: /// If dependency properties are used, it is very important to use <see cref="ClearValue"/> to clear the value
71: /// of the dependency properties in this method.
72: /// </summary>
73: protected virtual void Uninitialize()
74: {
75:
76: }
77:
78: /// <summary>
79: /// Called when the <see cref="AssociatedObject"/> is unloaded.
80: /// </summary>
81: /// <param name="sender">The sender.</param>
82: /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
83: private void OnAssociatedObjectUnloaded(object sender, EventArgs e)
84: {
85: CleanUp();
86: }
87:
88: /// <summary>
89: /// Actually cleans up the behavior because <see cref="OnDetaching"/> is not always called.
90: /// </summary>
91: /// <remarks>
92: /// This is based on the blog post: http://dotnetbyexample.blogspot.com/2011/04/safe-event-detachment-pattern-for.html.
93: /// </remarks>
94: private void CleanUp()
95: {
96: if (_isClean)
97: {
98: return;
99: }
100:
101: _isClean = true;
102:
103: if (AssociatedObject != null)
104: {
105: AssociatedObject.Unloaded -= OnAssociatedObjectUnloaded;
106: }
107:
108: Uninitialize();
109: }
111: }