首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ReactiveUI对ICollectionView

ReactiveUI对ICollectionView
EN

Stack Overflow用户
提问于 2014-08-07 08:58:33
回答 1查看 1K关注 0票数 5

我有一个.Net 4.5应用程序,正在移动到基于WPF的RxUI (最新的,6.0.3到本文撰写)。我有一个文本字段,应该作为一个过滤器字段,与相当常见的油门等东西,这是原因之一,成为反应在第一。

这是我们班的相关部分。

代码语言:javascript
复制
public class PacketListViewModel : ReactiveObject
{
    private readonly ReactiveList<PacketViewModel> _packets;
    private PacketViewModel _selectedPacket;
    private readonly ICollectionView _packetView;
    private string _filterText;

    /// <summary>
    /// Gets the collection of packets represented by this object
    /// </summary>
    public ICollectionView Packets 
    {
        get
        {
            if (_packets.Count == 0)
                RebuildPacketCollection();
            return _packetView;
        }
    }

    public string FilterText
    {
        get { return _filterText; }
        set { this.RaiseAndSetIfChanged(ref _filterText, value); }
    }

    public PacketViewModel SelectedPacket
    {
        get { return _selectedPacket; }
        set { this.RaiseAndSetIfChanged(ref _selectedPacket, value); }
    }

    public PacketListViewModel(IEnumerable<FileViewModel> files)
    {
        _packets = new ReactiveList<PacketViewModel>();
        _packetView = CollectionViewSource.GetDefaultView(_packets);
        _packetView.Filter = PacketFilter;

        _filterText = String.Empty;

        this.WhenAnyValue(x => x.FilterText)
            .Throttle(TimeSpan.FromMilliseconds(300)/*, RxApp.TaskpoolScheduler*/)
            .DistinctUntilChanged()
            .ObserveOnDispatcher()
            .Subscribe(_ => _packetView.Refresh());
    }

    private bool PacketFilter(object item)
    {
        // Filter logic
    }

    private void RebuildPacketCollection()
    {
        // Rebuild packet list from data source
        _packetView.Refresh();
    }
}

我使用Xunit.net和Resharper的测试运行程序对此进行单元测试。我创建了一些测试数据并运行了这个测试:

代码语言:javascript
复制
[Fact]
public void FilterText_WhenThrottleTimeoutHasPassed_FiltersProperly()
{
    new TestScheduler().With(s =>
    {
        // Arrange
        var fvm = GetLoadedFileViewModel();
        var sut = new PacketListViewModel(fvm);
        var lazy = sut.Packets;

        // Act
        sut.FilterText = "Call";
        s.AdvanceToMs(301);

        // Assert
        var res = sut.Packets.OfType<PacketViewModel>().ToList();
        sut.Packets.OfType<PacketViewModel>()
           .Count().Should().Be(1, "only a single packet should match the filter");
    });
}

在类的构造函数中,我在FilterText配置的订阅操作上放置了一条调试语句,在启动时每个数据包项都会调用它一次,但在更改FilterText属性之后就不会调用它。

顺便说一句,测试类的构造函数包含以下语句以使线程魔术工作:

代码语言:javascript
复制
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());

我的问题基本上是,我的视图中的Refresh()方法在我更改FilterText之后永远不会被调用,我看不出为什么不调用它。

这是我的代码中的简单问题吗?或者这是CollectionViewSource在单元测试上下文中运行而不是在WPF上下文中运行的问题吗?

我是否应该放弃这一想法,而是在触发文本更改时手动筛选ReactiveList属性?

注意:这在应用程序中有效-- FilterText在那里触发更新。这在单元测试中没有发生,这让我怀疑我是否做错了。

编辑:根据请求,以下是XAML的相关部分--目前这只是一个带有文本框和数据集的简单窗口。

TextBox:

代码语言:javascript
复制
<TextBox Name="FilterTextBox"
         Grid.Column="1"
         VerticalAlignment="Center"
         Text="{Binding FilterText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
         />

达格力:

代码语言:javascript
复制
<DataGrid ItemsSource="{Binding Path=Packets}"
          Name="PacketDataGrid"
          SelectedItem="{Binding SelectedPacket}"
          AutoGenerateColumns="False"
          EnableRowVirtualization="True"
          SelectionMode="Single"
          SelectionUnit="FullRow"
          CanUserAddRows="False"
          CanUserResizeRows="False"
          >
    <DataGrid.Columns>
...

如果还有什么是相关的/需要的,请告诉我!

编辑2:Paul建议不要像我一样在测试构造函数中执行SynchronizationContext设置,可能是出于非常合理的原因。但是,我这样做是因为另一个视图模型(FileViewModel)的工作方式--它需要等待MessageBus消息才能知道数据包处理已经完成。这是我正在积极努力避免的事情--我知道MessageBus是一个非常方便的坏主意。)但这就是SyncContext的原因。创建测试视图模型的方法如下所示:

代码语言:javascript
复制
private FileViewModel GetLoadedFileViewModel()
{
    var mre = new ManualResetEventSlim();
    var fvm = new FileViewModel(new MockDataLoader());
    MessageBus.Current
              .Listen<FileUpdatedPacketListMessage>(fvm.MessageToken.ToString())
              .Subscribe(msg => mre.Set());
    fvm.LoadFile("irrelevant.log");

    mre.Wait(500);

    return fvm;
}

我知道这是个糟糕的设计,所以请不要大喊大叫。)但是我在这里使用了大量的遗留代码,并将其迁移到基于RxUI的MVVM中--我还不能做到所有这些,最终得到一个完美的设计,这就是为什么我要对所有这些东西进行单元测试,以便以后可以进行Rambo重构。:)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-08-07 15:05:14

顺便说一句,测试类的构造函数包含以下语句以使线程魔术工作:

别这么做

我的问题基本上是,我的视图中的Refresh()方法在我更改FilterText之后永远不会被调用,我看不出为什么不调用它。

我相信你的问题在于评论的部分:

.Throttle(TimeSpan.FromMilliseconds(300)/,RxAppT.askinfo.gov.hk.Throttle/)

这部分:

.ObserveOnDispatcher()

使用TestScheduler时,必须对所有调度器参数使用RxApp.MainThread/Taskinfo.gov.hk scheduler。在上面,您使用的是一个真实的TaskpoolScheduler和一个真正的调度程序。由于它们不在TestScheduler之下,所以不能由TestScheduler控制。

相反,写:

代码语言:javascript
复制
    this.WhenAnyValue(x => x.FilterText)
        .Throttle(TimeSpan.FromMilliseconds(300), RxApp.TaskpoolScheduler)
        .DistinctUntilChanged()
        .ObserveOn(RxApp.MainThreadScheduler)
        .Subscribe(_ => _packetView.Refresh());

一切都应该正常。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25178258

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档