gpt4 book ai didi

c# - ReactiveUI 与 ICollectionView

转载 作者:太空狗 更新时间:2023-10-29 23:14:33 25 4
gpt4 key购买 nike

我有一个 .Net 4.5 应用程序正在迁移到基于 WPF 的 RxUI(在撰写本文时保持最新,6.0.3)。我有一个文本字段,它应该用作具有相当常见的 throttle 等的过滤器字段,这些东西首先是响应式的部分原因。

这是我类(class)的相关部分。

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 的测试运行器对此进行了单元测试。我创建了一些测试数据并运行了这个测试:

[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 属性后它永远不会被调用。

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

SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());

我的问题基本上是在我更改 FilterText 后我的 View 上的 Refresh() 方法从未被调用,我不明白为什么不。

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

我是否应该放弃这个想法,而是拥有一个在触发文本更改时手动过滤的 ReactiveList 属性?

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

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

文本框:

<TextBox Name="FilterTextBox"
Grid.Column="1"
VerticalAlignment="Center"
Text="{Binding FilterText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>

数据网格:

<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 Betts 建议不要像我一样在测试构造函数中设置 SynchronizationContext,这可能是出于非常正当的理由。但是,我这样做是因为另一个 View 模型 (FileViewModel) 的工作方式——它需要等待 MessageBus 消息才能知道数据包处理已完成。这是我正在积极努力避免的事情——我知道 MessageBus 是一个非常方便的坏主意。 :) 但这是 SyncContext 东西的原因。创建测试 View 模型的方法如下所示:

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 重构。 :)

最佳答案

Btw, the constructor for the test class contains the following statement to make threading magic work:

不要这样做

My problem is basically that the Refresh() method on my view never gets called after I change the FilterText, and I can't see why not.

我相信你的问题是注释掉的部分:

.Throttle(TimeSpan.FromMilliseconds(300)/, RxApp.TaskpoolScheduler/)

这部分:

.ObserveOnDispatcher()

当您使用 TestScheduler 时,您必须使用 RxApp.[MainThread/Taskpool]Scheduler 作为所有调度器参数。在上面,您正在使用一个真正的 TaskpoolScheduler 和一个真正的 Dispatcher。因为它们不在 TestScheduler 之下,所以它们不能被 TestScheduler 控制。

相反,写:

    this.WhenAnyValue(x => x.FilterText)
.Throttle(TimeSpan.FromMilliseconds(300), RxApp.TaskpoolScheduler)
.DistinctUntilChanged()
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(_ => _packetView.Refresh());

一切都应该正常。

关于c# - ReactiveUI 与 ICollectionView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25178258/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com