/// <summary> /// nameに紐づけられているPropertyObjectを取得する。 /// なければ新たに生成して返す。 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="name"></param> /// <returns></returns> protected PropertyObject GetProperty(string name) { PropertyObject current; if (!propery_objects.TryGetValue(name, out current)) { current = propery_objects[name] = new PropertyObject(); // current.obj == null } return(current); }
/// <summary> /// name の propertyが変更されたときに、これを購読しているobserverに更新通知を送る。 /// SetValue()を使わずに自力で名前に対応するイベントハンドラを呼びたい時にも用いる。 /// 遅延呼び出しではなく、即座にハンドラが呼び出される。 /// /// notify_otherがtrueなら、DataBindしている他のNotifyObjectにも通知がいく。 /// /// DataBindで双方向bindingしているときに無限再帰になるのを防ぐためにsenderをつけている。 /// A <-> B <-> C <-> A /// のような循環Bindingしている場合は、無限再帰になってしまうが、MVVMアーキテクチャでそういうようなBindingは /// しないと考えられるので問題ない。(MVVMでは送信元のデータソースは一箇所にあると考えられるので) /// </summary> /// <param name="e"></param> /// <param name="sender"></param> protected void RaisePropertyChanged(PropertyChangedEventArgs e) { if (!PropertyChangedEventEnable) { return; } PropertyObject current = null; Control form = null; lock (lockObject) { // このpropertyが見つからないということはないはず。(事前にハンドラが登録されているはずで…) current = GetProperty(e.name); // このformのBegineInvoke()で呼び出すことが要求されている。 if (current.form != null) { // Form生成前。無理ぽ if (!current.form.IsHandleCreated) { return; } // UIスレッド以外からの実行か? if (current.form.InvokeRequired) { // このlockのなかでInvoke()するとdead lockになるから駄目。 //form.Invoke(new Action(() => RaisePropertyChanged(e))); //return; form = current.form; // このlockを抜けてからcallbackする。 } } } // このpropertyをsubscribeしているobserverに更新通知を送る重複名はないことは保証されている。 // cloneしてsenderを書き換えて送る。 var e2 = e.Clone(); e2.sender = this; // lockの外側でコールバックしないとデッドロックになる。 // Invoke()をする場合であって、BegineInvoke()なら大丈夫か…。 var h = current.handler; if (h != null) { // UIスレッドからの実行が必要なのであればForm.BeginInvoke()を用いてコールバックする。 // Invoke()と違ってこちらは非同期に実行される。 if (form == null) { h(e2); } else { // 対局中だとFormがDisposeされているのにcallbackが起きることがある。 // form.Disposingとform.Disposedだけで判定できると言えないので、try~catchで書いておく。 try { form.BeginInvoke(new Action(() => h(e2))); } catch { } } } // data bindされているならそれらのオブジェクトにも通知 // これは同じスレッドで通知して良い。 if (current.notifies != null) { foreach (var notify in current.notifies) { // 無限再帰になるのを防ぐため、送信元を付与して呼び出す。 if (notify != e.sender /* is original sender */) { notify.SetValueAndRaisePropertyChanged(e2); } } } }