gpt4 book ai didi

c# - 为什么 CollectionViewSource.GetDefaultView(...) 从任务线程内部返回错误的 CurrentItem?

转载 作者:行者123 更新时间:2023-11-30 19:48:28 25 4
gpt4 key购买 nike

我有一个我认为相当标准的设置,一个由 ObservableCollection 支持的 ListBox

我有一些工作要处理 ObservableCollection 中的 Thing,这可能需要很长时间(超过几百毫秒),所以我会喜欢将其卸载到 Task 上(我也可以在这里使用 BackgroundWorker),以免卡住 UI。

奇怪的是,当我在开始 Task 之前执行 CollectionViewSource.GetDefaultView(vm.Things).CurrentItem 时,一切都按预期进行,但是如果发生这种情况 Task 期间,CurrentItem 似乎总是指向ObservableCollection 中的第一个元素。

我已经制定了一个完整的工作示例。

XAML:

<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<DockPanel>
<ToolBar DockPanel.Dock="Top">
<Button Content="Click Me Sync" Click="ButtonSync_Click" />
<Button Content="Click Me Async Good" Click="ButtonAsyncGood_Click" />
<Button Content="Click Me Async Bad" Click="ButtonAsyncBad_Click" />
</ToolBar>
<TextBlock DockPanel.Dock="Bottom" Text="{Binding Path=SelectedThing.Name}" />
<ListBox Name="listBox1" IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=Things}" SelectedItem="{Binding Path=SelectedThing}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</Window>

C#:

public partial class MainWindow : Window
{
private readonly ViewModel vm;

public MainWindow()
{
InitializeComponent();
vm = new ViewModel();
DataContext = vm;
}

private ICollectionView GetCollectionView()
{
return CollectionViewSource.GetDefaultView(vm.Things);
}

private Thing GetSelected()
{
var view = GetCollectionView();
return view == null ? null : (Thing)view.CurrentItem;
}

private void NewTask(Action start, Action finish)
{
Task.Factory
.StartNew(start)
.ContinueWith(t => finish());
//.ContinueWith(t => finish(), TaskScheduler.Current);
//.ContinueWith(t => finish(), TaskScheduler.Default);
//.ContinueWith(t => finish(), TaskScheduler.FromCurrentSynchronizationContext());
}

private void ButtonSync_Click(object sender, RoutedEventArgs e)
{
var thing = GetSelected();
DoWork(thing);
MessageBox.Show("all done");
}

private void ButtonAsyncGood_Click(object sender, RoutedEventArgs e)
{
var thing = GetSelected(); // outside new task
NewTask(() =>
{
DoWork(thing);
}, () =>
{
MessageBox.Show("all done");
});
}

private void ButtonAsyncBad_Click(object sender, RoutedEventArgs e)
{
NewTask(() =>
{
var thing = GetSelected(); // inside new task
DoWork(thing); // thing will ALWAYS be the first element -- why?
}, () =>
{
MessageBox.Show("all done");
});
}

private void DoWork(Thing thing)
{
Thread.Sleep(1000);
var msg = thing == null ? "nothing selected" : thing.Name;
MessageBox.Show(msg);
}
}

public class ViewModel
{
public ObservableCollection<Thing> Things { get; set; }
public Thing SelectedThing { get; set; }

public ViewModel()
{
Things = new ObservableCollection<Thing>();
Things.Add(new Thing() { Name = "one" });
Things.Add(new Thing() { Name = "two" });
Things.Add(new Thing() { Name = "three" });
Things.Add(new Thing() { Name = "four" });
}
}

public class Thing
{
public string Name { get; set; }
}

最佳答案

我相信 CollectionViewSource.GetDefaultView 实际上是线程静态的 - 换句话说,每个线程都会看到不同的 View 。这是一个简短的测试来证明:

using System;
using System.Windows.Data;
using System.Threading.Tasks;

internal class Test
{
static void Main()
{
var source = "test";
var view1 = CollectionViewSource.GetDefaultView(source);
var view2 = CollectionViewSource.GetDefaultView(source);
var view3 = Task.Factory.StartNew
(() => CollectionViewSource.GetDefaultView(source))
.Result;

Console.WriteLine(ReferenceEquals(view1, view2)); // True
Console.WriteLine(ReferenceEquals(view1, view3)); // False
}
}

如果您希望您的任务处理特定项目,我建议您在开始任务之前获取该项目。

关于c# - 为什么 CollectionViewSource.GetDefaultView(...) 从任务线程内部返回错误的 CurrentItem?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5489004/

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