Closed dhq-boiler closed 2 years ago
@dhq-boiler すいません。 ただ、書いたコードがバグっているので直してほしいという投稿に見えます。 具体的にReactivePropertyのなんの機能に関する質問(もしくはバグや改善要望)ですか??
わかりにくい質問をしてしまいどうもすみません。
具体的に質問しますと、ObservePropertyでReactivePropertySlimクラスのValueを指定した時に、ここにnewされて生成された新規のObserveCollectionが代入された場合、正しく通知が行われれますか?
プロパティの定義
public ReactivePropertySlim<ObservableCollection<UserClass>> Children { get; set; } = new ReactivePropertySlim<ObservableCollection<UserClass>>();
監視の開始
ObserveProperty(x => x.Children)
または
ObserveProperty(x => x.Children.Value)
上記のコードが実行された後に、Children.Valueに新規にインスタンス化したObservableCollecitonオブジェクトを代入します。
Children.Value = new ObservableCollection<UserClass>(array);
私のコード上で試していますが、Children.Valueに代入されたことが監視先のReactivePropertyに伝わっていないような気がしますが、これが自分のコードにバグがあるのか、それともReactivePropertyにバグがあるのか判断つかず質問させていただきました。
@dhq-boiler
以下のようなプログラムを書いて動かすと「変更通知を受け取りました」という表示が3つ表示されます。
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System.Collections.ObjectModel;
using System.ComponentModel;
var viewModel = new SomeViewModel();
var rp = viewModel.ObserveProperty(x => x.Children.Value)
.ToReadOnlyReactivePropertySlim();
rp.Subscribe(x => Console.WriteLine("変更通知を受け取りました"));
viewModel.Children.Value = new ObservableCollection<UserClass>();
viewModel.Children.Value = new ObservableCollection<UserClass>();
class SomeViewModel : INotifyPropertyChanged
{
public ReactivePropertySlim<ObservableCollection<UserClass>> Children { get; } = new ReactivePropertySlim<ObservableCollection<UserClass>>();
public event PropertyChangedEventHandler? PropertyChanged;
}
class UserClass
{
}
1 つ目の表示は Subscribe したときのもので、あとの 2 回が Children の値が更新されたときのものです。 これを見る限りは変更通知はされているように見えます。
@dhq-boiler 少しコードを変更して PropertyChanged も起きているかどうかという点と、実際に設定された値がわたってきているかが確認できるようにしました。
using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System.Collections.ObjectModel;
using System.ComponentModel;
var viewModel = new SomeViewModel();
var rp = viewModel.ObserveProperty(x => x.Children.Value)
.ToReadOnlyReactivePropertySlim();
rp.Subscribe(x => Console.WriteLine($"Subscribe: 要素数 {x?.Count}"));
rp.PropertyChanged += (_, e) =>
{
Console.WriteLine($"PropertyChanged: 要素数 {rp.Value.Count}");
};
viewModel.Children.Value = new ObservableCollection<UserClass>(new[] { new UserClass() });
viewModel.Children.Value = new ObservableCollection<UserClass>(new[] { new UserClass(), new UserClass() });
class SomeViewModel : INotifyPropertyChanged
{
public ReactivePropertySlim<ObservableCollection<UserClass>> Children { get; } = new ReactivePropertySlim<ObservableCollection<UserClass>>();
public event PropertyChangedEventHandler? PropertyChanged;
}
class UserClass
{
}
実行結果は以下のようになります。ちゃんと変更通知が行われて、設定されている値がわたってきていることが確認できます。
Subscribe: 要素数
Subscribe: 要素数 1
PropertyChanged: 要素数 1
Subscribe: 要素数 2
PropertyChanged: 要素数 2
問題が確認できるミニマムバージョンのコードですが、これくらい問題の起きる機能に絞ったものを期待しています。今回はコンソールアプリケーションで作りましたが、WPFなどの特定のプラットフォームでしか問題が再現しない場合は、画面等があっても大丈夫です。 開発中のアプリケーションの不要な機能を削ったものではないです。
もし、ReactiveProperty の動作として問題がある部分を見つけたら教えていただければ対応できますが、現在いただいた情報を見る限りだと @dhq-boiler さんの書かれたコードの中に何かしらバグがあるのではないかという疑いの方が強いと思います。アプリのコードベースも大きすぎて私の方では確認する時間をとることは出来ません。
あと、私のところでテスト実行したのですが全部成功しました。
お疲れさまです。 すみません、昨日夜、検証していたのですが、 自分のコードにバグがありました。 該当箇所を修正したところ、テストはすべて通ったので ReactivePropertyに問題はありませんでした。 いろいろと検証してくださりありがとうございました。
こんにちは。お世話になっております。
質問があります。
現在私のリポジトリ boiler's Graphics でラスター画像をベクター画像に変換する試みに取り組んでいます。
主要な処理は OpenCvSharp にほぼまかせてしまっているのですが、キャンパスにアイテムとして表示する際はどうしても追加の実装が必要になります。ひとまず実装してみました。
これで上手くいくかと思ったのですが、上記の処理が実装されているDiagramViewModelクラスのAllItemsプロパティに※で置き換えたアイテムが反映されないのです。
AllItemsプロパティは以下のようになっています。
LayerItemsChangedAsObservableメソッドは以下の通りです。
SelectRecursive拡張メソッドは以下のようになっています。
テスト用アプリ
アプリのミニマムバージョンを用意しています。場所はQuestion20220226.slnです。 ミニマムバージョンでは2つのツール、四角形描画ツールと画像ファイル描画ツールを実装しています。また画像オブジェクトを右クリックすると、「ベクターオブジェクトに変換」というコンテキストメニューがあります。これが本件の処理です。この処理は重いので完了までに時間がかかると思います。
ユニットテスト
また、ユニットテストを用意しています。ReactivePropertyTest.csです。 2つテストメソッドがあります。
1が今一番成功させたいテストメソッドです。 現状、これがテスト実行時に失敗してしまいます。AllItems.Valueはemptyと出てしまいます。
追伸
2は※でObservableCollectionをnewしつつListオブジェクトで初期化していますが、当初はこれはReactiveCollectionでした。しかし、ReactiveCollectionにアイテムを一つ一つ追加していくと膨大な時間がかかったので、(10000+個のアイテムを追加しようとしていて1個当たり1000msかかった記憶があります)工夫しようと試みました。最初、ReactiveCollectionをnewして初期化時にコンストラクタにまとめて渡す方法を試しました。2でも動作することを確認していますが、プロダクトコード上でやってみると10000+のアイテムを渡しているはずなのに0個になってしまうのです。原因はToObservableメソッドにあると思うのですが、結局わからず、次にObservableCollectionをnewする方法に切り替えました。ReactivePropertyの作者の皆さんからすると、あまりヘビーな処理はReactivePropertyには向いていないと思っているかもしれませんが、リソースを駆使すればどうにかする方法があるのではないかと思っています。
まとめ
話は長くなりましたが、AllItemsが正しく反映されるようになることを私は期待しています。 何か心当たりはないでしょうか。
よろしくお願いいたします。