【问题标题】:How do I fix SelectedItem null in listBox when using Task issue使用任务问题时如何修复列表框中的 SelectedItem null
【发布时间】: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 设为只读。那就只用AddRemove就可以了。
  • 感谢您的回复,但我怀疑这是导致问题的原因,因为如果我保持原样并删除与任务有关的所有内容,那么它可以工作。但集合将来自数据库,所以我不想在等待时阻止应用程序。
  • 你错了。 WPF 能够将调用封送回 UI 线程。我已经使用 WPF 多年了。我非常非常非常确定它确实是Names = new ObservableCollection&lt;string&gt;() 行。您应该查看CollectionView 类的源代码,该类涉及将集合绑定到视图对象。
  • 如果您不相信我,请尝试在任何线程上的任何时间点调用OnPropertyChanged("Names"),并观察 ListBox 丢失其选定项。 BTW OnPropertyChanged 实际上是使用字符串参数调用的,但 .net 4.5 编译器会自动添加它。
  • 我知道,我只是添加了 CallerMemberName,所以我不必传递该字符串。我试图清除集合并添加到它,如下面的回答中所建议的那样,但现在我有一个例外。所以我正在研究这个异常来查看,因为我仍然认为它与那个任务有关,因为在我介绍这个任务之前我没有这个问题。解决此问题后,我会尽快查看您的建议和@Khan。

标签: wpf asynchronous listbox selecteditem


【解决方案1】:

在您的LoadNewList 方法中,您正在为Names 创建一个新集合:

Names = new ObservableCollection<string>() { "New Daniel", "New James", "New Colin", "New Bruno", "Iza" };

这样做会破坏您现有的绑定。

尝试添加/删除/清空集合:

Names.Clear();
Names.Add("New Daniel");
...

【讨论】:

  • 嗨,谢谢你的时间,这不能解决我的问题,但我不得不创建第二个任务来模拟延迟,而我在上面的代码中的任务需要这个 TaskScheduler .FromCurrentSynchronizationContext());
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多