繁体   English   中英

WPF如何在后台线程完成创建集合时更新GUI?

[英]WPF How to update GUI when background thread finishes creating collections?

因此,我想将数据集合加载到后台线程中,然后将树视图绑定到新集合(而不是让后台线程在每次它想要向列表中添加项目时将事情排队在调度程序上[听起来效率低下]) 。

这可能吗? 我创建了新的数据结构,并在后台线程上将其输出为pd.result。 当UI线程检查对话框是否已关闭时,应将其设置为

ModuleHierarchyVM.TopLevelModules = pd.Result as ObservableCollection<ModuleViewModel>;

此后,将调用事件OnLoadVCD。 我有一个事件处理程序,然后尝试将树视图的itemsource设置为新集合。

this.AvailableModulesTreeView.ItemsSource = gvvm.ModuleHierarchyVM.TopLevelModules;

这将因错误而崩溃:“调用线程无法访问该对象,因为其他线程拥有它。”

甚至不知道如何调试它,调用堆栈没有提供任何实际的细节。

但是,如果仅将Itemsource设置为空白的新的空集合,如下所示:

this.AvailableModulesTreeView.ItemsSource = (IEnumerable<object>)new List<object>();

它不会崩溃(但随后也不会显示我的数据)。 有什么想法可能导致崩溃吗?

我认为可能是我从错误的线程更新了UI,所以我尝试了用begininvoke调用调度程序,并尝试通过dispatcher.checkaccess()检查我确实是UI线程。 所以这似乎不是问题。 但是,我真的不知道发生了什么。

我可以实现此目标的另一种方法是,通过在将每个项目添加到可观察集合时,对每个项目进行调度,从而使解析例程仅更新绑定到树视图的原始数据结构。 但是,即使那是唯一的解决方案,我也真的不喜欢不知道为什么某些事情不起作用。 在我看来,仅在不同的线程上创建一个全新的数据结构,然后将新的数据结构重新绑定到树视图,而丢弃旧的视图,似乎是合理的。 对我来说,似乎还不如几十行的ObservableCollectionInstance.Add在通过后台线程中的文件进行解析时在调度程序上添加调用。

完整代码:

UI线程调用的方法

public bool LoadPortInterface(string VCDFileName)
{
    ProgressDialog pd = new ProgressDialog("Loading File: ", VCDFileName);
    pd.Owner = Application.Current.MainWindow;
    pd.WindowStartupLocation = WindowStartupLocation.CenterOwner;
    ModuleHierarchyVM.TopLevelModules.Clear();


    VCDData TempVCDOutput = null;
    Func<object> handler = delegate
    {
        return VCDParser.ParseVCDFileForAllPorts(VCDFileName, this, pd.Worker, out TempVCDOutput);
    };
    pd.RunWorkerThread(handler);
    pd.ShowDialog();
    if (pd.DialogResult == true)
    {
        ModuleHierarchyVM.TopLevelModules = pd.Result as ObservableCollection<ModuleViewModel>;
        VCDOutput = TempVCDOutput;
    }
    OnLoadVcd();
}

对graphviewer中的OnLoadVCD事件处理程序的响应:

void gvvm_LoadVCDEvent(object sender, EventArgs e)
{
    this.AvailableModulesTreeView.ItemsSource = gvvm.ModuleHierarchyVM.TopLevelModules;
}

我认为使用TPL“任务并行库”会更容易

例如,如果要创建新线程,可以将其创建为任务。

var task = Task.Factory.StartNew(() =>
{
   // write what you want to do here
}

您可以从此链接中获得更多示例任务并行性(任务并行库)

因此,要从线程更新UI,可以在如下所示的UI线程中运行此线程:

        var uiContext = TaskScheduler.FromCurrentSynchronizationContext();

        _taskFactoryWrapper.StartTask(() => DoSomeWork(viewSettings.AnyValue)).ContinueWith(task =>
        {

                viewSettings.Result= task.Result;

        },TaskContinuationOptions.AttachedToParent)
            .ContinueWith(t => EndingUploadingProgress(viewSettings), uiContext);

因此,您可以创建与当前UI线程关联的TaskScheduler。]

您最有可能在UI线程以外的其他线程上创建,读取或修改ObservableCollection。 此外,请确保您没有在UI线程以外的任何对象上添加或从ObservableCollection中删除。

要进行调试,请在访问/修改可观察集合的任何地方放置一个断点,并记下击中该断点的线程号(VS中的“线程”窗口)。 它应该始终是相同的。

您可以使用其他结构(列表/数组)保存结果,然后调用UI线程来更新/创建ObservableCollection。 更新ObservableCollection是廉价的,即使是数百个项目也是如此。

昂贵的是,ObservableCollection将在每次更改时引发一个change事件,这可由UI组件处理以更改其布局,无论如何,这必须在UI线程上完成。 此UI事件处理是为什么ObservableCollection阻止您跨线程进行修改的原因。

如果要添加/删除大量项目,则最好创建一个新集合并重新分配数据源。 如果您始终这样做,则可以使用列表。 ObservableCollection适用于要修改列表并让控件仅更改最小量的情况。 更改数据源(例如更改为列表)将清除控件并重建它,这对于许多更改而言可能更好。

看到:

在单独的线程中更新ObservableCollection

如何通过辅助线程更新ObservableCollection?

从另一个线程更新ObservableCollection的最佳方法是什么?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM