分類:[C#]
2016/12/23(Fri) 14:24:25 編集(投稿者)
2016/12/23(Fri) 14:22:36 編集(投稿者)
こちらの記事を参考にさせてもらいました。
http://qiita.com/gaya_K/items/d1f80a295281375adc4c
記事内容にあるようにViewModelにて、
x => Hoge.Fuga.Piyo による変更通知をWeakEventListenerによって受け取りたいと思い以下のコードを書きました。
しかしリスナーの解除がうまくいかず行き詰まってしまいました。
どこが間違っているのかご指摘いただけたらと思います。
開発環境 Visual Studio Community 2015
Livetを使用してます
using System;
using System.ComponentModel;
using System.Reflection;
using System.Linq.Expressions;
using Livet;
using Livet.EventListeners.WeakEvents;
namespace sample {
public class PropertyTreeChangedWeakEventListener<TSrc> : IDisposable
where TSrc : INotifyPropertyChanged {
TSrc _eventSrc;
LivetCompositeDisposable _dsp = new LivetCompositeDisposable();
public PropertyTreeChangedWeakEventListener(TSrc obj) {
_eventSrc = obj;
}
public PropertyTreeChangedWeakEventListener<TSrc> RegisterHandler<TProp>(Expression<Func<TSrc,TProp>> propExp,PropertyChangedEventHandler handler) {
_dsp.Add(createChildWeakEventListener(this, CreatePropertyTree(propExp.Body), handler));
return this;
}
private IDisposable createChildWeakEventListener<TChild>(PropertyTreeChangedWeakEventListener<TChild> listener, _propertyTree tree, PropertyChangedEventHandler handler)
where TChild : INotifyPropertyChanged {
Action createCldLstnr = null;
Action dispCldLstnr = null;
if (tree != null) {
var methodName = MethodBase.GetCurrentMethod().Name;
createCldLstnr += () => {
var pi = tree.PropertyInfo;
var getterMi = pi.GetGetMethod();
var getterType = typeof(Func<,>).MakeGenericType(pi.ReflectedType, pi.PropertyType);
var childValue = Delegate
.CreateDelegate(getterType, getterMi)
.DynamicInvoke(listener._eventSrc);
// プロパティに対してリスナーを作成。
var childType = childValue.GetType();
object childListener = null;
if (typeof(INotifyPropertyChanged).IsAssignableFrom(childType)) {
childListener = typeof(PropertyTreeChangedWeakEventListener<>)
.MakeGenericType(childType)
.GetConstructor(new Type[] { childType })
.Invoke(new object[] { childValue });
dispCldLstnr += () => (childListener as IDisposable)?.Dispose();
listener
.GetType()
.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance)
.MakeGenericMethod(childType)
.Invoke(listener, new object[] { childListener, tree.Child, handler });
}
};
}
createCldLstnr?.Invoke();
return new LivetWeakEventListener<PropertyChangedEventHandler, PropertyChangedEventArgs>(
h => new PropertyChangedEventHandler(h),
h => {
listener._eventSrc.PropertyChanged += h;
},
h => {
listener._eventSrc.PropertyChanged -= h;
dispCldLstnr?.Invoke();
},
(s, e) => {
if (e.PropertyName == tree.PropertyInfo.Name) {
handler(s, e);
dispCldLstnr?.Invoke();
createCldLstnr?.Invoke();
}
});
}
private _propertyTree CreatePropertyTree(Expression exp, _propertyTree child = null) {
var mExp = exp as MemberExpression;
if (mExp == null) return null;
var pi = mExp.Member as PropertyInfo;
if (pi == null) throw new ArgumentException("式木からプロパティを取得できません。");
var tree = new _propertyTree(pi, child);
var parent = CreatePropertyTree(mExp.Expression, tree);
if (parent != null) return parent;
return tree;
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (disposing) {
_dsp.Dispose();
}
}
private class _propertyTree {
public _propertyTree(PropertyInfo pi, _propertyTree child) {
PropertyInfo = pi;
Child = child;
}
public PropertyInfo PropertyInfo { get; private set; }
public _propertyTree Child { get; set; }
}
}
//以下 テストコード
class Program {
static void Main(string[] args) {
var model = new TestModel();
var listener = new PropertyTreeChangedWeakEventListener<TestModel>(model);
listener.RegisterHandler(t => t.Hoge.Fuga,(s,e)=> { Console.WriteLine("prop changed "+e.PropertyName); });
var pickedHoge = model.Hoge;
model.Hoge = new TestModel2();
pickedHoge.Fuga = "xxx"; //解除できていない
listener = null;
GC.Collect();
Console.WriteLine("- - - - GC");
pickedHoge.Fuga = "xxx";
model.Hoge.Fuga = "xxx";
Console.ReadKey();
}
}
class TestModel : Livet.NotificationObject {
public TestModel() {
_hoge = new TestModel2();
}
TestModel2 _hoge;
public TestModel2 Hoge {
get { return _hoge; }
set {
_hoge = value;
this.RaisePropertyChanged();
}
}
}
class TestModel2 : Livet.NotificationObject {
string _fuga = "";
public string Fuga {
get { return _fuga; }
set {
_fuga = value;
RaisePropertyChanged();
}
}
}
}