C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[1]: 階層化したプロパティに追従するウィークイベントリスナー


(過去ログ 140 を表示中)

[トピック内 2 記事 (1 - 2 表示)]  << 0 >>

■82245 / inTopicNo.1)  階層化したプロパティに追従するウィークイベントリスナー
  
□投稿者/ qwerty (1回)-(2016/12/23(Fri) 14:16:13)

分類:[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();
			}
		}
	}
}

引用返信 編集キー/
■82253 / inTopicNo.2)  Re[1]: 階層化したプロパティに追従するウィークイベントリスナー
□投稿者/ qwerty (2回)-(2016/12/24(Sat) 15:14:53)
自己解決

誤)
listener._eventSrc.PropertyChanged

訂)
this._eventSrc.PropertyChanged

でした。
ごめんなさい。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -