如何使用 ReactiveUI 设置动态填充的 TabControl?

How to set up a dynamically-populated TabControl with ReactiveUI?

我正在努力研究 ReactiveUI。如果你不仔细看的话,其中的大部分内容在某种程度上是有道理的,但是当我尝试设置一个 TabControl 时,一切都在我面前爆炸。

我的 Window 上有一个 TabControl。我希望能够根据不同的用户操作在运行时动态地向其添加多个不同类型的选项卡。 This answer 解释了一种标准的 WPF 方法,它几乎可以工作,但由于它不是 ReactiveUI,每当我尝试打开一个带有 Reactive 视图的选项卡时,一切都会崩溃,因为 ViewModel 依赖属性尚未绑定。

XAML:

    <Window.Resources>
        <DataTemplate DataType="{x:Type vm:MyTabViewModel}">
            <views:MyTabEditor/>
        </DataTemplate>
    </Window.Resources>
...
        <TabControl Name="Multitab" Grid.Column="2" ItemsSource="{Binding Tabs}">
            <TabControl.ItemContainerStyle>
                <Style TargetType="{x:Type TabItem}">
                    <Setter Property="Header" Value="{Binding Name}" />
                </Style>
            </TabControl.ItemContainerStyle>
        </TabControl>

视图模型:

        public ObservableCollection<ITabPage> Tabs { get; } = new();

        public void AddNewTab()
        {
            var vm = new MyTabEditorViewModel();
            Tabs.Add(vm);
        }

XAML.cs

        private void NewTab_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            ViewModel.AddNewJob();
            Multitab.SelectedIndex = Multitab.Items.Count - 1;
            var last = Multitab.SelectedIndex;
            this.OneWayBind(ViewModel, vm => vm.Tabs[last], v => GAH WHAT GOES HERE?!?);
        }

这是我迷路的部分。如何设置绑定以将新 VM 绑定到为其创建的视图? Multitab.SelectedItem returns VM,而不是 View,我似乎找不到任何方法来获取新创建的 View 对象以绑定它。

有人知道如何正确设置吗?

请参考下面的示例代码。

Window1.xaml:

<reactiveui:ReactiveWindow x:Class="WpfApp1.Window1"
        x:TypeArguments="local:ViewModel"
        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:local="clr-namespace:WpfApp1"
        xmlns:reactiveui="http://reactiveui.net"
        mc:Ignorable="d"
        Title="Window1" Height="450" Width="800">
    <Grid>
        <TabControl Name="Multitab">
            <TabControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Name}" />
                </DataTemplate>
            </TabControl.ItemTemplate>
            <TabControl.ContentTemplate>
                <DataTemplate>
                    <reactiveui:ViewModelViewHost ViewModel="{Binding}" />
                </DataTemplate>
            </TabControl.ContentTemplate>
        </TabControl>
    </Grid>
</reactiveui:ReactiveWindow>

Window.xaml.cs:

public partial class Window1 : ReactiveWindow<ViewModel>
{
    public Window1()
    {
        InitializeComponent();
        ViewModel = new ViewModel();

        this.WhenActivated(disposableRegistration =>
        {
            this.OneWayBind(ViewModel,
                viewModel => viewModel.Tabs,
                view => view.Multitab.ItemsSource)
                .DisposeWith(disposableRegistration);
        });
    }
}

查看模型:

public class ViewModel
{
    public ObservableCollection<ITabPage> Tabs { get; } = 
        new ObservableCollection<ITabPage>() { new MyTabEditorViewModel() };
}

选项卡视图模型:

public interface ITabPage { }

public class MyTabEditorViewModel : ITabPage
{
    public string Name { get; } = "Name...";
}

TabView.xaml:

<reactiveui:ReactiveUserControl x:Class="WpfApp1.TabEditorView"
             x:TypeArguments="local:MyTabEditorViewModel"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:reactiveui="http://reactiveui.net"
             xmlns:local="clr-namespace:WpfApp1"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">
    <Grid>
        <TextBlock>Tab content...</TextBlock>
    </Grid>
</reactiveui:ReactiveUserControl>

TabView.xaml.cs:

public partial class TabEditorView : ReactiveUserControl<MyTabEditorViewModel>
{
    public TabEditorView()
    {
        InitializeComponent();
    }
}