C# WPF 中的 ReactiveUI:列表的绑定列表

ReactiveUI in C# WPF: Bind List of List

我刚开始使用 C# - WPF 中的 ReactiveUI 和 MVVM。

我创建了一个测试项目,其目标是表示一个对象链表。 每个大学列表都有一个课程列表。在课程中,考试由学生匿名提交。 我首先只显示大学列表。这行得通。

但是我无法显示课程列表。我看到一个列表框,但条目是空的。 (为了清楚起见,我暂时省略了考试的介绍。)

  1. 大学0
    • 课程 0
      • 考试 0:待定
      • 考试 1:完成
    • 课程 1
      • 考试 2:进行中
      • 考试 3:完成
  2. 大学1
    • 课程 3
      • 考试 4:完成
      • 考试 5:完成

我假设在 UniversityViewModel.cs 中我必须以某种方式绑定课程列表,但是如何绑定?

对于初学者,我使用 ReactiveUI 页面上的示例作为指南:A Compelling Example

AppViewModel.cs

using DynamicData;
using DynamicData.Binding;
using ReactiveUI;
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Linq;

namespace UniversityViewer
{
    public class AppViewModel : ReactiveObject
    {
        private readonly IDataProviderService _dataProviderService;
        public ReadOnlyObservableCollection<UniversityViewModel> UniversityViewModels { get; }

        private string _searchTerm;
        public string SearchTerm
        {
            get => _searchTerm;
            set => this.RaiseAndSetIfChanged(ref _searchTerm, value);
        }

        public AppViewModel()
        {
            _dataProviderService = new DataProviderService();
            
            Func<University, bool> universityFilter(string text) => university =>
            {
                return
                    string.IsNullOrEmpty(text) ||
                    university.Name.ToLower().Contains(text.ToLower());
            };

            var filterPredicate = this.WhenAnyValue(x => x.SearchTerm)
                                      .Throttle(TimeSpan.FromMilliseconds(250), RxApp.TaskpoolScheduler)
                                      .DistinctUntilChanged()
                                      .Select(universityFilter);

            var dataLoader = _dataProviderService.Universities
                .Connect()
                .Filter(filterPredicate)
                .Transform(university => new UniversityViewModel(university))
                .Sort(SortExpressionComparer<UniversityViewModel>.Ascending(u => u.Name))
                .ObserveOn(RxApp.MainThreadScheduler)
                .Bind(out var bindingData)
                .Subscribe();

            UniversityViewModels = bindingData;
        }
    }
}

University.cs

using System.Collections.Generic;

namespace UniversityViewer
{
    public class University
    {
        public University(string name)
        {
            Name = name;
            Courses = new List<Course>()
            {
                new Course("1234"),
                new Course("2345"),
                new Course("3456")
            };
        }

        public string Name { get; set; }
        public List<Course> Courses { get; set; }
    }
}

UniversityView.xaml

<reactiveui:ReactiveUserControl
  x:Class="UniversityViewer.UniversityView"
  xmlns:universityViewer="clr-namespace:UniversityViewer"
  x:TypeArguments="universityViewer:UniversityViewModel"
  xmlns:reactiveui="http://reactiveui.net"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock TextWrapping="WrapWithOverflow" 
                 Margin="6" VerticalAlignment="Center">
          <Run FontWeight="SemiBold" x:Name="nameRun"/>
        </TextBlock>

        <ListBox x:Name="ListBoxCourses"
                 Grid.Row="1" Margin="5" HorizontalContentAlignment="Stretch"
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled" />
    </Grid>
</reactiveui:ReactiveUserControl>

UniversityView.xaml.cs

using ReactiveUI;
using System.Reactive.Disposables;

namespace UniversityViewer
{
    public partial class UniversityView : ReactiveUserControl<UniversityViewModel>
    {
        public UniversityView()
        {
            InitializeComponent();
            this.WhenActivated(disposableRegistration =>
            {
                this.OneWayBind(ViewModel,
                    viewModel => viewModel.Name,
                    view => view.nameRun.Text)
                    .DisposeWith(disposableRegistration);

                this.OneWayBind(ViewModel,
                    viewModel => viewModel.Courses,
                    view => view.ListBoxCourses.ItemsSource)
                    .DisposeWith(disposableRegistration);
            });
        }
    }
}

UniversityViewModel.cs

using ReactiveUI;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace UniversityViewer
{
    public class UniversityViewModel : ReactiveObject
    {
        private University _university;
        public ReadOnlyObservableCollection<CourseViewModel> CourseViewModels { get; }

        public UniversityViewModel(University university)
        {
            _university = university;

            //var dataLoader = _university.Courses
            //    .Connect()
            //    ....

            //CourseViewModels = bindingData;
        }

        public string Name => _university.Name;
        public List<Course> Courses => _university.Courses;
    }
}

Course.cs

namespace UniversityViewer
{
    public class Course
    {
        public Course(string name)
        {
            Name = name;
            //Exams = new List<Exam>();
        }

        public string Name { get; set; }
        //public List<Exam> Exams { get; set; }
    }
}

CourseView.xaml

<reactiveui:ReactiveUserControl
  x:Class="UniversityViewer.CourseView"
  xmlns:universityViewer="clr-namespace:UniversityViewer"
  x:TypeArguments="universityViewer:CourseViewModel"
  xmlns:reactiveui="http://reactiveui.net"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock TextWrapping="WrapWithOverflow" 
                 Margin="6" VerticalAlignment="Center">
          <Run FontWeight="SemiBold" x:Name="nameRun"/>
        </TextBlock>

        <!--<ListBox x:Name="ListBoxExams"
                 Grid.Row="1" Margin="5" HorizontalContentAlignment="Stretch"
                 ScrollViewer.HorizontalScrollBarVisibility="Disabled" />-->
    </Grid>
</reactiveui:ReactiveUserControl>

CourseView.xaml.cs

using ReactiveUI;
using System.Reactive.Disposables;

namespace UniversityViewer
{
    public partial class CourseView : ReactiveUserControl<CourseViewModel>
    {
        public CourseView()
        {
            InitializeComponent();
            this.WhenActivated(disposableRegistration =>
            {
                this.OneWayBind(ViewModel,
                    viewModel => viewModel.Name,
                    view => view.nameRun.Text)
                    .DisposeWith(disposableRegistration);
            });
        }
    }
}

CourseViewModel.cs

using ReactiveUI;

namespace UniversityViewer
{
    public class CourseViewModel : ReactiveObject
    {
        private Course _course;

        public CourseViewModel(Course course)
        {
            _course = course;
        }

        public string Name => _course.Name;
    }
}

UniversityView 应该绑定到 CourseViewModels 而不是 Courses 才能解析 CourseView

this.OneWayBind(ViewModel,
    viewModel => viewModel.CourseViewModels,
    view => view.ListBoxCourses.ItemsSource)
    .DisposeWith(disposableRegistration);

然后您需要填充 CourseViewModels 集合。

Courses 属性 应该从 UniversityViewModel 中删除。 Course.

没有要解决的 ReactiveUserControl