Quantcast
Channel: かずきのBlog@hatena
Viewing all articles
Browse latest Browse all 1387

INotifyPropertyChangedのイベントを処理したい

$
0
0

みたいな記事をりんちゃさんに拾われてた。

今だとどうするかな~というと制約がなければReactive Extensionsかなぁ。例えば、こんなINotifyPropertyChangedの実装をしたクラスがあるとして

publicclass Person : INotifyPropertyChanged
{

    publicevent PropertyChangedEventHandler PropertyChanged;

    protectedvirtualvoid OnPropertyChanged(string propertyName)
    {
        var h = this.PropertyChanged;
        if (h != null)
        {
            h(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    protectedvirtualbool SetProperty<T>(ref T field, T value, [CallerMemberName]string propertyName = null)
    {
        if (object.Equals(field, value))
        {
            returnfalse;
        }

        field = value;
        this.OnPropertyChanged(propertyName);
        returntrue;
    }

    privatestring name;

    publicstring Name
    {
        get { returnthis.name; }
        set { this.SetProperty(refthis.name, value); }
    }

    privateint age;

    publicint Age
    {
        get { returnthis.age; }
        set { this.SetProperty(refthis.age, value); }
    }

}

Rx-MainをNuGetで追加してPropertyChangedイベントをIOに変換するものを作る。

staticclass NotifyPropertyChangedExtensions
{
    publicstatic IObservable<PropertyChangedEventArgs> PropertyChangedAsObservable(this INotifyPropertyChanged self)
    {
        return Observable.FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>(
            h => (s, e) => h(e),
            h => self.PropertyChanged += h,
            h => self.PropertyChanged -= h);
    }
}

こうすると、Whereでプロパティ名を絞ってからSubscribeしてるので、そこで好きなようにやるかんじ。

var p = new Person();
var propertyChanged = p.PropertyChangedAsObservable();

var token = new CompositeDisposable();
propertyChanged.Where(e => e.PropertyName == "Name")
    .Select(_ => p.Name)
    .Subscribe(name => Console.WriteLine(name)).AddTo(token);
propertyChanged.Where(e => e.PropertyName == "Age")
    .Select(_ => p.Age)
    .Subscribe(age => Console.WriteLine(age)).AddTo(token);

// プロパティを変更したら値が出力される
p.Name = "tanaka";
p.Age = 100;

// 購読解除
token.Dispose();

// イベントの処理やめたので何もおきない
p.Name = "kimura";
p.Age = 120;

もうちょっとプロパティの値の変更に特化して使いたいとかいうなら、ここまでラップしたメソッドを定義してもいいかも?

staticclass NotifyPropertyChangedExtensions
{
    publicstatic IObservable<PropertyChangedEventArgs> PropertyChangedAsObservable(this INotifyPropertyChanged self)
    {
        return Observable.FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>(
            h => (s, e) => h(e),
            h => self.PropertyChanged += h,
            h => self.PropertyChanged -= h);
    }

    publicstatic IObservable<T> PropertyChangedAsObservable<T, TProp>(this T self, Expression<Func<T, TProp>> propertySelector)
        where T : INotifyPropertyChanged
    {
        // プロパティ名を取り出して
        var name = ((MemberExpression)propertySelector.Body).Member.Name;
        return self.PropertyChangedAsObservable()
            // プロパティ名でフィルタリングして
            .Where(e => e.PropertyName == name)
            // 自分自身を返す感じ
            .Select(_ => self);
    }
}

こうしとくと、こういう感じになる。

var p = new Person();

var token = new CompositeDisposable();
p.PropertyChangedAsObservable(o => o.Name)
    .Subscribe(person => Console.WriteLine(person.Name)).AddTo(token);
p.PropertyChangedAsObservable(o => o.Age)
    .Subscribe(person => Console.WriteLine(person.Age)).AddTo(token);

p.Name = "tanaka";
p.Age = 100;

token.Dispose();

p.Name = "kimura";
p.Age = 120;

とりあえず

ReactivePropertyを入れておけば、拡張メソッドが定義されてたりします。PropertyChangdAsObservableメソッドの他にObservePropertyメソッドというのもあって以下のような感じで使えます。

var p = new Person();

var token = new CompositeDisposable();
p.ObserveProperty(o => o.Name).Subscribe(name => Console.WriteLine(name)).AddTo(token);
p.ObserveProperty(o => o.Age).Subscribe(age => Console.WriteLine(age)).AddTo(token);

p.Name = "tanaka";
p.Age = 100;

token.Dispose();

p.Name = "kimura";
p.Age = 120;

素敵なかんじ。ということで、ObserveProperty使うのがよさげ。


Viewing all articles
Browse latest Browse all 1387

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>