dhq_boiler
@dhq_boiler

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

[ReactiveProperty]シーケンス要素を絞り込んで取得できない

お世話になっております。

解決したいこと

C# + WPFでベクターグラフィックスドローイングツールを開発しています。

2021-07-23.png

※下にソースコードへの案内を記載しております。よろしければそちらを参照ください。

発生している問題・エラー

現在、レイヤー機能を実装しているところです。SelectedLayersというReactiveCollectionを作成し、レイヤーウィンドウで選択されたレイヤーを絞り込んで、図形を描画する時にそのレイヤーに図形を追加するといった処理をさせたいのですが、SelectedLayersのReactiveCollectionがコレクション変更通知をうまく処理できていないようで、レイヤーウィンドウでレイヤーを選択しても必ず「レイヤー1」の名前のレイヤーがSelectedLayersにセットされるという状況が続いております。

また、SelectedLayers.ToObservable().Subscribe()としても、SelectedLayersコレクションの変更時のイベントが検知できていないようです。

DiagramViewModel.cs

public class DiagramViewModel : BindableBase, IDiagramViewModel, IDisposable
    {
        :
        #region Property

        public ReactiveCollection<Layer> Layers { get; } = new ReactiveCollection<Layer>();

        public ReadOnlyReactiveCollection<Layer> SelectedLayers { get; }

        :
        #endregion //Property

public DiagramViewModel()
        {
            :

            //SelectedLayers = Layers.ObserveElementProperty(x => x.IsSelected)
            //                       .Where(x => x.Instance.IsSelected.Value == true)
            //                       .Select(x => x.Instance)
            //                       .ToReactiveCollection(); //not working

            //SelectedLayers = Layers.CollectionChangedAsObservable()
            //                       .Select(_ => Layers.CollectionChangedAsObservable().Where(x => x.Action == NotifyCollectionChangedAction.Remove || x.Action == NotifyCollectionChangedAction.Reset).ToUnit())
            //                       .Switch()
            //                       .Select(_ => Layers.Where(x => x.IsSelected.Value == true).ToArray())
            //                       .ToReadOnlyReactivePropertySlim(Array.Empty<Layer>()); //not working

            //SelectedLayers = Layers.ToObservable()
            //                       .Where(x => x.IsSelected.Value == true)
            //                       .ToReadOnlyReactiveCollection(); //not working

            SelectedLayers = Layers.ObserveElementProperty(x => x.IsSelected)
                                   .Where(x => x.Instance.IsSelected.Value == true)
                                   .Select(x => x.Instance)
                                   .ToReadOnlyReactiveCollection(); //not working

            SelectedLayers.ToObservable()
                          .Subscribe(x =>
            {
                Trace.WriteLine($"SelectedLayers changed {x.Name.Value}"); //not called
            })
            .AddTo(_CompositeDisposable);

            :
        }

Layer.cs
    public class Layer : BindableBase, IObservable<LayerObservable>
    {
        private CompositeDisposable _disposable = new CompositeDisposable();
        public static int LayerCount { get; set; } = 1;
        public static ObservableCollection<Layer> SelectedLayers { get; } = new ObservableCollection<Layer>();

        public ReactivePropertySlim<ImageSource> Appearance { get; } = new ReactivePropertySlim<ImageSource>();

        public ReactivePropertySlim<bool> IsVisible { get; } = new ReactivePropertySlim<bool>();

        public ReactivePropertySlim<bool> IsSelected { get; } = new ReactivePropertySlim<bool>();

        public ReactivePropertySlim<bool> IsExpanded { get; } = new ReactivePropertySlim<bool>();

        public ReactivePropertySlim<string> Name { get; } = new ReactivePropertySlim<string>();

        public ReactiveCommand SwitchVisibilityCommand { get; } = new ReactiveCommand();
        public ReactiveCommand SelectLayerCommand { get; } = new ReactiveCommand();

        public ReactiveCollection<LayerItem> Items { get; } = new ReactiveCollection<LayerItem>();


        public Layer()
        {
            SwitchVisibilityCommand.Subscribe(_ =>
            {
                IsVisible.Value = !IsVisible.Value;
            })
            .AddTo(_disposable);
            SelectLayerCommand.Subscribe(args =>
            {
                MouseEventArgs ea = args as MouseEventArgs;
                if (!Keyboard.IsKeyDown(Key.LeftShift) && !Keyboard.IsKeyDown(Key.RightShift))
                {
                    var diagramVM = (App.Current.MainWindow.DataContext as MainWindowViewModel).DiagramViewModel;
                    diagramVM.Layers.Where(x => x.IsSelected.Value == true)
                                    .ToList()
                                    .ForEach(x => x.IsSelected.Value = false);
                }

                IsSelected.Value = true;
            })
            .AddTo(_disposable);
            IsVisible.Subscribe(isVisible =>
            {
                if (!isVisible)
                {
                    Items.ToList().ForEach(x => x.IsVisible.Value = isVisible);
                }
            })
            .AddTo(_disposable);
            Items.ObserveElementProperty(x => x.Item.Value)
                 .Delay(TimeSpan.FromMilliseconds(500))
                 .ObserveOnDispatcher()
                 .Subscribe(x =>
                 {
                     UpdateAppearance(Items.Select(xx => xx.Item.Value));
                 })
                 .AddTo(_disposable);
            IsVisible.Value = true;
        }

        public IObservable<Unit> LayerItemsChangedAsObservable()
        {
            return Items.ObserveElementObservableProperty(x => x.Item)
                        .ToUnit()
                        .Merge(Items.CollectionChangedAsObservable().Where(x => x.Action == NotifyCollectionChangedAction.Remove || x.Action == NotifyCollectionChangedAction.Reset).ToUnit());
        }

        private void UpdateAppearance(IEnumerable<SelectableDesignerItemViewModelBase> items)
        {
            var designerCanvas = App.Current.MainWindow.GetChildOfType<DesignerCanvas>();
            double minX, maxX, minY, maxY;
            var width = GetWidth(items, out minX, out maxX);
            var height = GetHeight(items, out minY, out maxY);

            if (width < 0 || height < 0)
                return;

            var rtb = new RenderTargetBitmap((int)width, (int)height, 96, 96, PixelFormats.Pbgra32);

            DrawingVisual visual = InitializeBitmap(width, height);
            rtb.Render(visual);

            foreach (var item in items)
            {
                var views = designerCanvas.GetCorrespondingViews<FrameworkElement>(item).Where(x => x.GetType() == item.GetViewType());
                foreach (var view in views)
                {
                    if (view != null)
                    {
                        if (view.ActualWidth >= 1 && view.ActualHeight >= 1)
                        {
                            using (DrawingContext context = visual.RenderOpen())
                            {
                                double diffX = 0;
                                double diffY = 0;
                                var designerItem = item as DesignerItemViewModelBase;
                                var connectorItem = item as ConnectorBaseViewModel;
                                if (designerItem != null)
                                {
                                    diffX = designerItem.Left.Value - minX;
                                    diffY = designerItem.Top.Value - minY;
                                }
                                if (connectorItem != null)
                                {
                                    diffX = Math.Min(connectorItem.Points[0].X, connectorItem.Points[1].X);
                                    diffY = Math.Min(connectorItem.Points[0].Y, connectorItem.Points[1].Y);
                                }
                                VisualBrush brush = new VisualBrush(view);
                                context.DrawRectangle(brush, null, new Rect(new Point(diffX, diffY), new Size(view.ActualWidth, view.ActualHeight)));
                            }

                            rtb.Render(visual);
                        }
                    }
                    else
                    {
                        throw new Exception("view not found");
                    }
                }
            }

            Appearance.Value = rtb;
        }

        private static DrawingVisual InitializeBitmap(int width, int height)
        {
            DrawingVisual visual = new DrawingVisual();
            using (DrawingContext context = visual.RenderOpen())
            {
                //白背景でビットマップを初期化
                context.DrawRectangle(Brushes.White, null, new Rect(new Point(), new Size(width, height)));
            }
            return visual;
        }

        private int GetWidth(IEnumerable<SelectableDesignerItemViewModelBase> items, out double minX, out double maxX)
        {
            minX = 0d;
            maxX = 0d;
            foreach (var item in items)
            {
                var desingerItem = item as DesignerItemViewModelBase;
                var connectorItem = item as ConnectorBaseViewModel;
                if (desingerItem != null)
                {
                    minX = Math.Min(Math.Min(minX, desingerItem.Left.Value), desingerItem.Right.Value);
                    maxX = Math.Max(Math.Max(maxX, desingerItem.Left.Value), desingerItem.Right.Value);
                }
                if (connectorItem != null)
                {
                    minX = Math.Min(Math.Min(minX, connectorItem.Points[0].X), connectorItem.Points[1].X);
                    maxX = Math.Max(Math.Max(maxX, connectorItem.Points[0].X), connectorItem.Points[1].X);
                }
            }
            return (int)(maxX - minX);
        }

        private int GetHeight(IEnumerable<SelectableDesignerItemViewModelBase> items, out double minY, out double maxY)
        {
            minY = 0d;
            maxY = 0d;
            foreach (var item in items)
            {
                var desingerItem = item as DesignerItemViewModelBase;
                var connectorItem = item as ConnectorBaseViewModel;
                if (desingerItem != null)
                {
                    minY = Math.Min(Math.Min(minY, desingerItem.Top.Value), desingerItem.Bottom.Value);
                    maxY = Math.Max(Math.Max(maxY, desingerItem.Top.Value), desingerItem.Bottom.Value);
                }
                if (connectorItem != null)
                {
                    minY = Math.Min(Math.Min(minY, connectorItem.Points[0].X), connectorItem.Points[1].X);
                    maxY = Math.Max(Math.Max(maxY, connectorItem.Points[0].X), connectorItem.Points[1].X);
                }
            }
            return (int)(maxY - minY);
        }

        public void RemoveItem(SelectableDesignerItemViewModelBase item)
        {
            var layerItems = Items.Where(x => x.Item.Value == item);
            layerItems.ToList().ForEach(x =>
            {
                var removed = Items.Remove(x);
                Trace.WriteLine($"{x} removed from {Items} {removed}");
            });
        }

        public void AddItem(SelectableDesignerItemViewModelBase item)
        {
            var layerItem = new LayerItem(item, this);
            layerItem.IsVisible.Value = true;
            layerItem.Name.Value = $"アイテム{LayerItem.LayerItemCount++}";
            Items.Add(layerItem);
        }

        private List<IObserver<LayerObservable>> _observers = new List<IObserver<LayerObservable>>();

        public IDisposable Subscribe(IObserver<LayerObservable> observer)
        {
            _observers.Add(observer);
            observer.OnNext(new LayerObservable());
            return new LayerDisposable(this, observer);
        }

        public class LayerDisposable : IDisposable
        {
            private Layer layer;
            private IObserver<LayerObservable> observer;

            public LayerDisposable(Layer layer, IObserver<LayerObservable> observer)
            {
                this.layer = layer;
                this.observer = observer;
            }

            public void Dispose()
            {
                layer._observers.Remove(observer);
            }
        }
    }

    public class LayerObservable : BindableBase
    {
    }
}

下記にGIFアニメを載せます。
最初にレイヤーウィンドウでレイヤー2を追加しています。
そしてレイヤー2を選択して、図形を描画していますが、図形アイテムはレイヤー1にぶらさがってしまいます。

selectedLayers_not_working.gif

ソースコード

boiler's Graphics
https://github.com/dhq-boiler/boiler-s-Graphics

gitリポジトリ
https://github.com/dhq-boiler/boiler-s-Graphics.git

ブランチ:feature/Layer

コミット:be4cf2c

自分で試したこと

もしかしてReactiveCollectionの子となるLayerクラスにはIObservable<?>インターフェースの実装が必須なのでは、と思い、
IObservableを実装してみました。LayerObservableはダミークラスで何もプロパティなどはないですが。

何か私の見落とし、致命的な勘違いなど気づいたところがあれば、回答していただけると助かります。よろしくお願いいたします。

0

5Answer

下記コードを試しに書いてみましたが、結果は変わらずです。

DiagramViewModel.cs
SelectedLayers = Layers.ObserveElementObservableProperty(x => x.IsSelected)
                                   .ToUnit()
                                   .Merge(Layers.CollectionChangedAsObservable().Where(x => x.Action == NotifyCollectionChangedAction.Remove || x.Action == NotifyCollectionChangedAction.Reset).ToUnit())
                                   .SelectMany(x => Layers)
                                   .Where(x => x.IsSelected.Value == true)
                                   .ToReadOnlyReactiveCollection();

なお、ReadOnlyReactiveCollection<Layer>でSubscribe()しようと思ったのですが、IEnumerable<Layer>.Subscribe(IObserver observer)が呼ばれてしまって、ラムダ式でパラメータが?になってしまう現象に遭遇しました。

混迷を極めております。

0Like

レイヤー選んだ時に、対象のレイヤーの IsSelected.Value は正しく true になってますか?

あと質問時は、巨大なコードを提示するよりは事象が再現する必要最低限のプロジェクトを新規で作って簡単に回答者が再現できるものがあると回答が付きやすくなると思います。
今の状態ですと、プロジェクトを clone してローカルでビルドして実行してみて、アプリを操作して(使ったことのないアプリの操作は意外と大変)、問題が再現するか確認して、該当コードがどこか膨大なソースコードから探して、問題は本当に該当部分だけの影響で起きているのか調査して etc... になるので、質問回答までにかかる労力が大きすぎる気がします。

0Like

Comments

  1. @dhq_boiler

    Questioner

    >レイヤー選んだ時に、対象のレイヤーの IsSelected.Value は正しく true になってますか?
    選択されたレイヤーのIsSelected.ValueはTrueに設定されています。
    しかし、SelectedLayersで、IsSelected=FalseのLayerが返ります。

    >あと質問時は、巨大なコードを提示するよりは事象が再現する必要最低限のプロジェクトを新規で作って簡単に回答者が再現できるものがあると回答が付きやすくなると思います。
    >今の状態ですと、プロジェクトを clone してローカルでビルドして実行してみて、アプリを操作して(使ったことのないアプリの操作は意外と大変)、問題が再現するか確認して、該当コードがどこか膨大なソースコードから探して、問題は本当に該当部分だけの影響で起きているのか調査して etc... になるので、質問回答までにかかる労力が大きすぎる気がします。
    質問では可能な限り、最低限のプロジェクトを提示するように心がけます。
    ご指摘ありがとうございました。
DiagramViewModel.cs
SelectedLayers = Layers.ObserveElementProperty(x => x.IsSelected)
                                   .Select(x => x.Instance)
                                   .Where(x => x.IsSelected.Value)
                                   .ToReadOnlyReactiveCollection(); //not working

上記のようにして、任意のアイテムを追加した時にブレークポイントを仕掛けて、デバッグ中のローカルウィンドウの値のスクショを撮ってみました。

Layers[0] : レイヤー1 IsSelected=False
Layers[1] : レイヤー2 IsSelected=True

ですが

SelectedLayers[0] : レイヤー1 IsSelected=False

となっています。

debugging.png

なぜでしょう…?

0Like

ミニマムなプロジェクトを作ってみました。

https://github.com/dhq-boiler/Qiita
https://github.com/dhq-boiler/Qiita.git

プロジェクト: Comment20210729

Program.cs
    class Program
    {
        static void Main(string[] args)
        {
            var diagramViewModel = new DiagramViewModel();
            
            var layer1 = new Layer();
            layer1.Name.Value = "レイヤー1";
            Console.WriteLine("Add layer1");
            diagramViewModel.Layers.Add(layer1);
            
            var layer2 = new Layer();
            layer2.Name.Value = "レイヤー2";
            Console.WriteLine("Add layer2");
            diagramViewModel.Layers.Add(layer2);
            
            Console.WriteLine("set IsSelect={true, false}");
            diagramViewModel.Layers[0].IsSelected.Value = true;
            diagramViewModel.Layers[1].IsSelected.Value = false;
            Console.WriteLine($"{diagramViewModel.SelectedLayers.Count} items");
            Console.WriteLine(string.Join(", ", diagramViewModel.SelectedLayers.Select(x => $"{x.Name.Value.ToString()}[{x.IsSelected.Value}]")));

            Console.WriteLine("set IsSelect={false, true}");
            diagramViewModel.Layers[0].IsSelected.Value = false;
            diagramViewModel.Layers[1].IsSelected.Value = true;
            Console.WriteLine($"{diagramViewModel.SelectedLayers.Count} items");
            Console.WriteLine(string.Join(", ", diagramViewModel.SelectedLayers.Select(x => $"{x.Name.Value.ToString()}[{x.IsSelected.Value}]")));

            Console.WriteLine("set IsSelect={true, false}");
            diagramViewModel.Layers[0].IsSelected.Value = true;
            diagramViewModel.Layers[1].IsSelected.Value = false;
            Console.WriteLine($"{diagramViewModel.SelectedLayers.Count} items");
            Console.WriteLine(string.Join(", ", diagramViewModel.SelectedLayers.Select(x => $"{x.Name.Value.ToString()}[{x.IsSelected.Value}]")));
        }
    }

    class DiagramViewModel : BindableBase
    {

        public ReactiveCollection<Layer> Layers { get; } = new ReactiveCollection<Layer>();

        public ReadOnlyReactiveCollection<Layer> SelectedLayers { get; }

        public DiagramViewModel()
        {
            SelectedLayers = Layers.ObserveElementProperty(x => x.IsSelected)
                                   .Select(x => x.Instance)
                                   .Where(x => x.IsSelected.Value)
                                   .ToReadOnlyReactiveCollection(); //not working

            //SelectedLayers = Layers.ObserveElementObservableProperty(x => x.IsSelected)
            //                       .Select(x => x.Instance)
            //                       .Where(x => x.IsSelected.Value)
            //                       .ToReadOnlyReactiveCollection(); //not working
        }
    }

    public class Layer : BindableBase, IObservable<LayerObservable>
    {

        public ReactivePropertySlim<bool> IsSelected { get; } = new ReactivePropertySlim<bool>();

        public ReactivePropertySlim<string> Name { get; } = new ReactivePropertySlim<string>();

        public ReactiveCollection<LayerItem> Items { get; } = new ReactiveCollection<LayerItem>();

        public Layer()
        {

        }

        private List<IObserver<LayerObservable>> _observers = new List<IObserver<LayerObservable>>();

        public IDisposable Subscribe(IObserver<LayerObservable> observer)
        {
            _observers.Add(observer);
            observer.OnNext(new LayerObservable());
            return new LayerDisposable(this, observer);
        }

        public class LayerDisposable : IDisposable
        {
            private Layer layer;
            private IObserver<LayerObservable> observer;

            public LayerDisposable(Layer layer, IObserver<LayerObservable> observer)
            {
                this.layer = layer;
                this.observer = observer;
            }

            public void Dispose()
            {
                layer._observers.Remove(observer);
            }
        }
    }

    public class LayerObservable : BindableBase
    {
    }

    public class LayerItem : BindableBase
    {
        public ReactiveProperty<bool> IsSelected { get; set; }
        public ReactivePropertySlim<string> Name { get; } = new ReactivePropertySlim<string>();
        public ReactivePropertySlim<Layer> Owner { get; } = new ReactivePropertySlim<Layer>();
        public ReactivePropertySlim<SelectableDesignerItemViewModelBase> Item { get; } = new ReactivePropertySlim<SelectableDesignerItemViewModelBase>();

        public LayerItem(SelectableDesignerItemViewModelBase item, Layer owner)
        {
            Item.Value = item;
            Owner.Value = owner;
            Init();
        }

        private void Init()
        {
            IsSelected = Item.Where(x => x != null)
                             .Select(x => x.IsSelected.Value)
                             .ToReactiveProperty();
            IsSelected.Subscribe(x =>
            {
                if (Item.Value != null)
                {
                    Item.Value.IsSelected.Value = x;
                }
            });
        }
    }

    public class SelectableDesignerItemViewModelBase : BindableBase
    {
        public ReactivePropertySlim<bool> IsSelected { get; } = new ReactivePropertySlim<bool>();
    }

出力:

Add layer1
Add layer2
set IsSelect={true, false}
0 items

set IsSelect={false, true}
0 items

set IsSelect={true, false}
0 items

再現しません。

さて、困った。

0Like

自己解決しました。

下記のようにSelectedLayersの型をReadOnlyReactivePropertySlimにするように変更したところ、正しく動作するようになりました。

Program.cs
    class Program
    {
        static void Main(string[] args)
        {
            var diagramViewModel = new DiagramViewModel();
            
            var layer1 = new Layer();
            layer1.Name.Value = "レイヤー1";
            Console.WriteLine("Add layer1");
            diagramViewModel.Layers.Add(layer1);
            
            var layer2 = new Layer();
            layer2.Name.Value = "レイヤー2";
            Console.WriteLine("Add layer2");
            diagramViewModel.Layers.Add(layer2);
            
            Console.WriteLine("set IsSelect={true, false}");
            diagramViewModel.Layers[0].IsSelected.Value = true;
            diagramViewModel.Layers[1].IsSelected.Value = false;
            Console.WriteLine($"{diagramViewModel.SelectedLayers.Value.Count()} items");
            Console.WriteLine(string.Join(", ", diagramViewModel.SelectedLayers.Value.Select(x => $"{x.Name.Value.ToString()}[{x.IsSelected.Value}]")));

            Console.WriteLine("set IsSelect={false, true}");
            diagramViewModel.Layers[0].IsSelected.Value = false;
            diagramViewModel.Layers[1].IsSelected.Value = true;
            Console.WriteLine($"{diagramViewModel.SelectedLayers.Value.Count()} items");
            Console.WriteLine(string.Join(", ", diagramViewModel.SelectedLayers.Value.Select(x => $"{x.Name.Value.ToString()}[{x.IsSelected.Value}]")));

            Console.WriteLine("set IsSelect={true, false}");
            diagramViewModel.Layers[0].IsSelected.Value = true;
            diagramViewModel.Layers[1].IsSelected.Value = false;
            Console.WriteLine($"{diagramViewModel.SelectedLayers.Value.Count()} items");
            Console.WriteLine(string.Join(", ", diagramViewModel.SelectedLayers.Value.Select(x => $"{x.Name.Value.ToString()}[{x.IsSelected.Value}]")));
        }
    }

    class DiagramViewModel : BindableBase
    {

        public ReactiveCollection<Layer> Layers { get; } = new ReactiveCollection<Layer>();

        public ReadOnlyReactivePropertySlim<Layer[]> SelectedLayers { get; }

        public DiagramViewModel()
        {
            SelectedLayers = Layers.ObserveElementObservableProperty(x => x.IsSelected)
                                   .Select(_ => Layers.Where(x => x.IsSelected.Value == true).ToArray())
                                   .ToReadOnlyReactivePropertySlim(Array.Empty<Layer>()); //work fine!!!
        }
    }

    public class Layer : BindableBase, IObservable<LayerObservable>
    {

        public ReactivePropertySlim<bool> IsSelected { get; } = new ReactivePropertySlim<bool>();

        public ReactivePropertySlim<string> Name { get; } = new ReactivePropertySlim<string>();

        public ReactiveCollection<LayerItem> Items { get; } = new ReactiveCollection<LayerItem>();

        public Layer()
        {

        }

        private List<IObserver<LayerObservable>> _observers = new List<IObserver<LayerObservable>>();

        public IDisposable Subscribe(IObserver<LayerObservable> observer)
        {
            _observers.Add(observer);
            observer.OnNext(new LayerObservable());
            return new LayerDisposable(this, observer);
        }

        public class LayerDisposable : IDisposable
        {
            private Layer layer;
            private IObserver<LayerObservable> observer;

            public LayerDisposable(Layer layer, IObserver<LayerObservable> observer)
            {
                this.layer = layer;
                this.observer = observer;
            }

            public void Dispose()
            {
                layer._observers.Remove(observer);
            }
        }
    }

    public class LayerObservable : BindableBase
    {
    }

    public class LayerItem : BindableBase
    {
        public ReactiveProperty<bool> IsSelected { get; set; }
        public ReactivePropertySlim<string> Name { get; } = new ReactivePropertySlim<string>();
        public ReactivePropertySlim<Layer> Owner { get; } = new ReactivePropertySlim<Layer>();
        public ReactivePropertySlim<SelectableDesignerItemViewModelBase> Item { get; } = new ReactivePropertySlim<SelectableDesignerItemViewModelBase>();

        public LayerItem(SelectableDesignerItemViewModelBase item, Layer owner)
        {
            Item.Value = item;
            Owner.Value = owner;
            Init();
        }

        private void Init()
        {
            IsSelected = Item.Where(x => x != null)
                             .Select(x => x.IsSelected.Value)
                             .ToReactiveProperty();
            IsSelected.Subscribe(x =>
            {
                if (Item.Value != null)
                {
                    Item.Value.IsSelected.Value = x;
                }
            });
        }
    }

    public class SelectableDesignerItemViewModelBase : BindableBase
    {
        public ReactivePropertySlim<bool> IsSelected { get; } = new ReactivePropertySlim<bool>();
    }
0Like

Your answer might help someone💌