[WPF] BehaviorのOnDetaching
ビヘイビアで、以下のようにOnAttached/OnDetachingメソッドで初期化/終了処理をしているコードをよく見かける。
1 2 3 4 5 6 7 8 9 10 | protected override void OnAttached() { AssociatedObject.SelectionChanged += ListBox_SelectionChanged; } protected override void OnDetaching() { AssociatedObject.SelectionChanged -= ListBox_SelectionChanged; } |
一見、問題無さそうに見えるが、ビヘイビアのOnDetachingはコールされないことがあるため、メモリやリソースのリークが発生する可能性がある。
確実に初期化/終了処理を行いたいのであれば、AssociatedObjectのLoad/Unloadイベントを使った方がよい。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 | public class BehaviorBase<T> : Behavior<T> where T : FrameworkElement { #region フィールド /// <summary> /// セットアップ状態 /// </summary> private bool isSetup = false ; /// <summary> /// Hook状態 /// </summary> private bool isHookedUp = false ; /// <summary> /// 対象オブジェクト /// </summary> private WeakReference weakTarget; #endregion // フィールド #region メソッド /// <summary> /// Changedハンドラ /// </summary> protected override void OnChanged() { base .OnChanged(); //==== 関連オブジェクト有無判定 ====// var target = AssociatedObject; if (target != null ) { //-==- 有り -==-// //==== Hook開始 ====// HookupBehavior(target); } else { //-==- 無し -==-// //==== Hook解除 ====// UnHookupBehavior(); } } /// <summary> /// ビヘイビアをHookする。 /// </summary> /// <param name="target"></param> private void HookupBehavior(T target) { //==== Hook状態判定 ====// if (isHookedUp) { //-==- Hook中 -==-// return ; } //==== Hook開始 ====// weakTarget = new WeakReference(target); isHookedUp = true ; target.Unloaded += OnTargetUnloaded; target.Loaded += OnTargetLoaded; //==== ビヘイビアのセットアップ ====// SetupBehavior(); } /// <summary> /// ビヘイビアをUnhookする。 /// </summary> private void UnHookupBehavior() { //==== Hook状態判定 ====// if (!isHookedUp) { //-==- 未Hook -==-// return ; } //==== Hook解除 ====// isHookedUp = false ; var target = AssociatedObject ?? (T)weakTarget.Target; if (target != null ) { target.Unloaded -= OnTargetUnloaded; target.Loaded -= OnTargetLoaded; } //==== ビヘイビアのクリーンアップ ====// CleanupBehavior(); } /// <summary> /// [関連オブジェクト] Loadedハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnTargetLoaded( object sender, RoutedEventArgs e) { //==== ビヘイビアのセットアップ ====// SetupBehavior(); } /// <summary> /// [関連オブジェクト] Unloadedハンドラ /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void OnTargetUnloaded( object sender, RoutedEventArgs e) { //==== ビヘイビアのクリーンアップ ====// CleanupBehavior(); } /// <summary> /// セットアップ時の処理を行う。 /// </summary> protected virtual void OnSetup() { } /// <summary> /// クリーンアップ時の処理を行う。 /// </summary> protected virtual void OnCleanup() { } /// <summary> /// ビヘイビアのセットアップを行う。 /// </summary> private void SetupBehavior() { if (isSetup) { return ; } isSetup = true ; OnSetup(); } /// <summary> /// ビヘイビアのクリーンアップを行う。 /// </summary> private void CleanupBehavior() { if (!isSetup) { return ; } isSetup = false ; OnCleanup(); } #endregion // メソッド } |
使い方は簡単。
派生クラスを作って、そのなかでOnSetup/OnCleanupメソッドをoverride。
このメソッド内に初期化/終了処理を実装する。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class HogeBehaviour : BehaviorBase<ListBox> { /// <summary> /// セットアップ時の処理を行う。 /// </summary> protected override void OnSetup() { base .OnSetup(); AssociatedObject.SelectionChanged += ListBox_SelectionChanged; } /// <summary> /// クリーンアップ時の処理を行う。 /// </summary> protected override void OnCleanup() { AssociatedObject.SelectionChanged -= ListBox_SelectionChanged; base .OnCleanup(); } } |