Run*_*sen 5 c# wpf xunit.net system.reactive reactiveui
我有一个.Net 4.5应用程序正在转向基于WPF的RxUI(在撰写本文时保持最新,6.0.3).我有一个文本字段,应该作为一个过滤器字段,具有相当常见的油门等东西,这是首先发生反应的部分原因.
这是我班级的相关部分.
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();
}
}
Run Code Online (Sandbox Code Playgroud)
我使用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");
});
}
Run Code Online (Sandbox Code Playgroud)
我在类的构造函数中为我的FilterText配置的Subscribe操作添加了一个debug语句,并且在启动时为每个数据包项调用一次,但在更改FilterText属性后它永远不会被调用.
顺便说一句,测试类的构造函数包含以下语句使线程魔术工作:
SynchronizationContext.SetSynchronizationContext(new SynchronizationContext());
Run Code Online (Sandbox Code Playgroud)
我的问题基本上是我更改FilterText后,我的视图上的Refresh()方法永远不会被调用,我不明白为什么不.
这是我的代码的一个简单问题吗?或者这是一个在单元测试上下文而不是在WPF上下文中运行的CollectionViewSource的问题?
我是否应该放弃这个想法,而是拥有一个ReactiveList属性,我会在触发文本更改时手动过滤?
注意:这适用于应用程序 - FilterText在那里触发更新.它不会发生在单元测试中,这让我想知道我是否做错了.
编辑:根据要求,这里是XAML的相关位 - 现在只是一个带有文本框和数据网格的简单窗口.
TextBox:
<TextBox Name="FilterTextBox"
Grid.Column="1"
VerticalAlignment="Center"
Text="{Binding FilterText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
/>
Run Code Online (Sandbox Code Playgroud)
数据网格:
<DataGrid ItemsSource="{Binding Path=Packets}"
Name="PacketDataGrid"
SelectedItem="{Binding SelectedPacket}"
AutoGenerateColumns="False"
EnableRowVirtualization="True"
SelectionMode="Single"
SelectionUnit="FullRow"
CanUserAddRows="False"
CanUserResizeRows="False"
>
<DataGrid.Columns>
...
Run Code Online (Sandbox Code Playgroud)
如果有任何其他相关/需要,请告诉我!
编辑2:Paul Betts建议不要像我一样在测试构造函数中进行SynchronizationContext设置,可能是出于非常正确的原因.但是,我这样做是因为另一个视图模型(FileViewModel)的工作方式 - 它需要等待MessageBus消息才能知道数据包处理已完成.这是我正在积极努力避免的事情 - 我知道MessageBus是一个非常方便的坏主意.:)但这是SyncContext的原因.创建测试视图模型的方法如下所示:
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;
}
Run Code Online (Sandbox Code Playgroud)
我意识到这是糟糕的设计,所以请不要大喊.;)但是我在这里采用了很多遗留代码并将其转移到基于RxUI的MVVM中 - 我无法完成所有这一切并最终得到一个完美的设计,这就是为什么我要为所有这些进行单元测试这些东西让我以后可以做Rambo重构.:)
顺便说一句,测试类的构造函数包含以下语句使线程魔术工作:
不要这样做
我的问题基本上是我更改FilterText后,我的视图上的Refresh()方法永远不会被调用,我不明白为什么不.
我相信你的问题是注释掉的部分:
.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());
Run Code Online (Sandbox Code Playgroud)
一切都应该有效.
归档时间: |
|
查看次数: |
871 次 |
最近记录: |