【发布时间】:2015-05-22 20:43:44
【问题描述】:
我在将列表框中的 selectedItem 设置为 null 时遇到问题。但它每隔一次都会这样做,我不知道为什么会这样。仅当我尝试使用 Task 获取一些数据异步时才会发生这种情况。我创建了一个非常简单的项目来显示我发现很难理解的确切问题。 所以这是我的 xaml
<Window x:Class="AsyncProblem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding Names}" SelectedItem="{Binding SelectedName}" />
<Button Click="Button_Click" Content="Reload" Width="50" Height="50" />
</StackPanel>
</Window>
这是我的视图模型:
namespace AsyncProblem
{
public class MainWindowViewModel : NotifyPropertyChanged
{
public MainWindowViewModel()
{
Names = new ObservableCollection<string>() { "Name", "Second Name", "Third Name"};
}
private ObservableCollection<string> names;
public ObservableCollection<string> Names
{
get { return names; }
set
{
names = value;
OnPropertyChanged();
}
}
private string selectedName;
public string SelectedName
{
get { return selectedName; }
set
{
selectedName = value;
OnPropertyChanged();
}
}
Task loadTask;
int counter = 0;
public void LoadNewList()
{
counter++;
loadTask = Task.Factory.StartNew(() =>
{
Thread.Sleep(2000);
Names = new ObservableCollection<string>()
{
string.Format("{0} new name", counter),
string.Format("{0} second new name", counter),
string.Format("{0} third new name", counter)
};
});
}
public void ReselectSelectedName()
{
loadTask.ContinueWith(previousTask => SelectedName = Names.LastOrDefault());
}
}
}
这里是调用代码
public partial class MainWindow : Window
{
private MainWindowViewModel dataContext;
public MainWindow()
{
InitializeComponent();
dataContext = new MainWindowViewModel();
DataContext = dataContext;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
dataContext.LoadNewList();
dataContext.ReselectSelectedName();
}
}
现在当我运行这个程序并按下按钮时,如果得到新列表,但它没有设置 selectedName。实际上会发生什么,选定的名称更改为iza,然后以某种方式第二次调用选定的名称集,其值为null。因此,列表框当然会丢失其选定的项目。 更糟糕的是,它只会在每秒钟点击一次按钮时发生,当我再次尝试点击按钮时,我会得到想要的行为,然后行为又会出错,等等。
谁能解释一下幕后发生了什么?
谢谢
【问题讨论】:
-
你永远不应该取消/替换你的
ObservableCollection。当您这样做时,WPF 需要重新绑定所有内容。将您的ObservableCollection设为只读。那就只用Add和Remove就可以了。 -
感谢您的回复,但我怀疑这是导致问题的原因,因为如果我保持原样并删除与任务有关的所有内容,那么它可以工作。但集合将来自数据库,所以我不想在等待时阻止应用程序。
-
你错了。 WPF 能够将调用封送回 UI 线程。我已经使用 WPF 多年了。我非常非常非常确定它确实是
Names = new ObservableCollection<string>()行。您应该查看CollectionView类的源代码,该类涉及将集合绑定到视图对象。 -
如果您不相信我,请尝试在任何线程上的任何时间点调用
OnPropertyChanged("Names"),并观察 ListBox 丢失其选定项。 BTW OnPropertyChanged 实际上是使用字符串参数调用的,但 .net 4.5 编译器会自动添加它。 -
我知道,我只是添加了 CallerMemberName,所以我不必传递该字符串。我试图清除集合并添加到它,如下面的回答中所建议的那样,但现在我有一个例外。所以我正在研究这个异常来查看,因为我仍然认为它与那个任务有关,因为在我介绍这个任务之前我没有这个问题。解决此问题后,我会尽快查看您的建议和@Khan。
标签: wpf asynchronous listbox selecteditem