如何使用 ReactiveUI 在 Canvas 中显示矩形的 ObservableCollection?

How do I display an ObservableCollection of Rectangles in a Canvas using ReactiveUI?

我有一个要在 canvas 上显示的矩形列表。我使用了 Krzysztof Skowronek 对 的回答,但他没有使用 ReactiveUI Binding;我是。但是我无法让我的任何矩形显示在我的 canvas 上。一切都编译没有错误,也没有抛出异常。这段代码并不多,因为我创建了一个新项目只是为了在将它添加到我的实际项目之前对其进行测试。这是我得到的。

MainWindowViewModel.cs

namespace CanvasTest
{
    public class MainWindowViewModel : ReactiveObject
    {
        //test canvas implementation
        private ObservableCollection<IShape> _shapes;
        public ObservableCollection<IShape> Shapes
        {
            get => _shapes;
            set => this.RaiseAndSetIfChanged(ref _shapes, value);
        }

        public ReactiveCommand<Unit, Unit> AddRectangle { get; }

        public MainWindowViewModel()
        {
            // Test Canvas Implementation
            Shapes = new ObservableCollection<IShape>();

            AddRectangle = ReactiveCommand.Create<Unit>(x => { 
                Shapes.Add(new Rectangle { Top = 25, Left = 10, Height = 50, Width = 50 });
            });
        }
    }

    public interface IShape
    {
        int Top { get; set; }
        int Left { get; set; }
    }

    public abstract class Shape : ReactiveObject, IShape
    {
        private int _top;
        public int Top
        {
            get => _top;
            set => this.RaiseAndSetIfChanged(ref _top, value);
        }

        private int _left;
        public int Left
        {
            get => _left;
            set => this.RaiseAndSetIfChanged(ref _left, value);
        }
    }

    public class Rectangle : Shape
    {
        private int _width;
        public int Width
        {
            get => _width;
            set => this.RaiseAndSetIfChanged(ref _width, value);
        }

        private int _height;
        public int Height
        {
            get => _height;
            set => this.RaiseAndSetIfChanged(ref _height, value);
        }
    }
}

我使用断点检查命令是否正常工作。

MainWindow.xaml

<rxui:ReactiveWindow x:Class="CanvasTest.MainWindow"
        x:TypeArguments="ct:MainWindowViewModel"
        xmlns:rxui ="http://reactiveui.net"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:ct="clr-namespace:CanvasTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <ItemsControl x:Name="audioDisplayItemsControl" 
                      BorderBrush="Black"
                      BorderThickness="2"
                      Height="100">
            <ItemsControl.Resources>
                <DataTemplate DataType="{x:Type ct:Rectangle}">
                    <Rectangle Canvas.Top="{Binding Top, Mode=OneWay}" 
                               Canvas.Left="{Binding Left, Mode=OneWay}"
                               Width="{Binding Width}"
                               Height="{Binding Height}"
                               Fill="Green"/>
                </DataTemplate>
            </ItemsControl.Resources>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Canvas.Top" Value="{Binding Top, Mode=OneWay}"/>
                    <Setter Property="Canvas.Left" Value="{Binding Left, Mode=OneWay}"/>
                </Style>
            </ItemsControl.ItemContainerStyle>
        </ItemsControl>
        
        <Button x:Name="addButton" 
                Grid.Row="1" 
                Content="Add" 
                Width="50"
                Margin="0,0,0,10"/>
    </Grid>
</rxui:ReactiveWindow>

MainWindow.xaml.cs

namespace CanvasTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : ReactiveWindow<MainWindowViewModel>
    {
        public MainWindow()
        {
            InitializeComponent();
            ViewModel = new MainWindowViewModel();

            this.WhenActivated(d =>
            {
                this.OneWayBind(ViewModel,
                    vm => vm.Shapes,
                    v => v.audioDisplayItemsControl.ItemsSource)
                 .DisposeWith(d);

                this.BindCommand(ViewModel,
                    vm => vm.AddRectangle,
                    v => v.addButton)
                .DisposeWith(d);
            });
        }
    }
}

如果还有什么需要知道的我再补充,实在想不出来了。

我正在使用 WPF 并安装了以下 NuGet 包。 ReactiveUI,ReactiveUI.WPF,ReactiveUI.Events.WPF;所有 v11.4.17.

根据我上面的评论,我向 MainWindow.xaml header 添加了一个数据上下文,因此我可以绑定到我的 ViewModel 中的 collection:

DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}"

并 XAML 绑定到我的 Shapes ObservableCollection

<ItemsControl ItemsSource="{Binding Shapes}" ... >

在 reactui Slack 频道上与 Ani Betts 交谈后,我发现这是最好的方法。您可以通过为 rxui 绑定提供数据模板来使其工作;它会产生大量开销,并且由于 non-recycling 中的 canvas,有很多 canvas 项,它可能很昂贵 memory-wise.