LoginSignup
0
0

ReactivePropertyのObservePropertyはネストしたプロパティも指定できるって知ってた?

Last updated at Posted at 2024-01-08

実装を追ってみる

ネスト対応のPropertyObservable生成部分がここで

ExpressionTreeからネストしたプロパティ指定をノードに変換するのがここで

ノード内でネスト指定されたプロパティを取得してるのがここ

    public object? GetPropertyPathValue()
    {
        if (Source == null)
        {
            return null;
        }

        if (Next != null)
        {
            return Next.GetPropertyPathValue();
        }

        return GetPropertyValue();
    }

Nextノードがある場合(プロパティがネストして指定された状態)と、Nextが無い場合で呼び分けられており、

GerPropertyValue()がソースとプロパティ名から実際の値を取得していますが、この中で動的にプロパティのgetterを(キャッシュして)呼び出しています。なるほどすごい。

    private object? GetPropertyValue()
    {
        EnsureDispose();
        if (Source == null) return null;
        return (_getAccessor ?? (_getAccessor = AccessorCache.LookupGet(Source.GetType(), PropertyName)))
            .DynamicInvoke(Source);
    }

ObservePropertyのネスト指定が出来て何が嬉しいのか?

C#+Xamlの環境では、ViewModelアイテムにObservableなModelインスタンスを持たせてXamlからバインディング(INotifyPropertyChangedを通じてプロパティ変更をView/Modelに相互に反映する紐づけ動作)することもあるんですが、ViewModelでModelと似たようなObservableなプロパティを重複定義させることなく、Modelオブジェクトを単に公開してしまう形で定義しておいて任意のプレゼンテーション部分(例えばPageViewModelとか)で


// PageVM上で画像レイヤーを定義していたとして
public ObservableCollection<StoryboardImageLayerCategoryViewModel> ImageLayers { get; }

// IsExportEnabledが変わったらプレビュー動画を差し替えたりしたい
ImageLayers.ObserveElementProperty(x => x.ImageLayerCategory.IsExportEnabled)
    .Subscribe(x => ...)
    .AddTo(_navigationDisposer);

// ViewModelアイテムからModelインスタンスを公開してる
public sealed partial class StoryboardImageLayerCategoryViewModel : ObservableObject
{
    public StoryboardImageLayerCategory ImageLayerCategory { get; }
    public StoryboardImageLayerCategoryViewModel(
        StoryboardImageLayerCategory imageLayerCategory
        )
    {
        ImageLayerCategory = imageLayerCategory;
    }

    // 他にもModel内のプロパティをクッションしたり、Page表現を補助するような別のコードがあったりするので、ViewModel表現自体は必要。
}

みたいに書けてとても嬉しいわけです。実際便利。

Modelは変更されたら確実に保存したいので常にプロパティ変更時を捕捉しながらローカルDBに更新掛けたりするので、ViewModelだけでなくModelもObservableな実装にする必要があります。

// CommunityToolkit.Mvvm を導入してる前提で
public sealed partial class StoryboardImageLayerCategory : DeferSaveAwareObservableObject
{
    public StoryboardImageLayerCategory(
        StoryboardImageLayerCategoryEntity layerCategory
        , StoryboardSceneRepository repository)
    {
        LayerCategoryEntity = layerCategory;
        _repository = repository;
    }

    public StoryboardImageLayerCategoryEntity LayerCategoryEntity { get; }
    public bool IsExportEnabled
    {
        get => LayerCategoryEntity.IsExportEnabled;
        set => SetProperty(LayerCategoryEntity.IsExportEnabled, value, LayerCategoryEntity, static (m, v) => m.IsExportEnabled = v);
    }

    protected override void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        _repository._imageLayerCategoryCollection.Upsert(LayerCategoryEntity);
        base.OnPropertyChanged(e);
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0