簡體   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