2015-11-15 24 views
3

我正在做一個Visual Studio裝飾擴展。如果沒有用戶輸入至少2秒,我想更新裝飾。所以我建造了一個工人並試圖去除和添加裝飾物,但是VS說它不能被更新,因爲非UI線程已經調用了它。所以我等了沒有線程,然後我的編輯變得非常滯後(因爲UI線程等待)視覺工作室繪畫裝飾與懶惰更新

我想知道是否有更新裝飾與懶惰更新的方法。 繪圖裝飾是通過調用AddAdornment()完成的,我找不到如何調用ui線程來繪製。

下面是我的代碼

internal async void OnLayoutChanged(object sender, TextViewLayoutChangedEventArgs e) 
    { 
     Print("OnLayoutChanged Called"); 

     task = Task.Factory.StartNew(() => 
     { 
      Print("task Started"); 
      if (e.NewSnapshot != e.OldSnapshot) 
      { 
       parseStopwatch.Restart(); 
       shouldParse = true; 
      } 

      ParseWork(e); 
     }); 
     await task; 


    } 


    private async void ParseWork(object param) 
    { 
     var e = (TextViewLayoutChangedEventArgs)param; 
     if (e == null) 
     { 
      shouldParse = false; 
      parseStopwatch.Stop(); 
      CsharpRegionParser.ParseCs(this.view.TextSnapshot); 
      DrawRegionBox(); 
      return; 
     } 

     while (shouldParse) 
     { 
      Task.Delay(10); 
      if ((shouldParse && parseStopwatch.ElapsedMilliseconds > 2000) || parseStopwatch.ElapsedMilliseconds > 5000) 
      { 
       break; 
      } 

     } 
     shouldParse = false; 
     parseStopwatch.Stop(); 
     CsharpRegionParser.ParseCs(this.view.TextSnapshot); 
     DrawRequest(e); 

     return; 

    } 
+1

在後臺任務中等待,調用主線程更新 –

回答

4

我不確定爲什麼你被低估了,特別是因爲在處理擴展時這是一個有趣的問題。因此,對於你的第一個問題:Visual Studio與WPF有相同的需求(由於它的COM依賴性,還有一些增加的複雜性)。當您不在Main(UI)線程中時,您無法更新UI元素。不幸的是,如果你直接使用WPF使用的策略來處理它,你將會遇到另一個世界的問題(主要是死鎖)。

第一件事,首先,瞭解如何處理在Visual Studio擴展領域中從後臺切換到UI線程。我發現Asynchronous and Multithreaded programming within VS using JoinableTaskFactory有助於解釋。我不得不做類似昂貴的解析操作。這非常簡單。

我解析器執行就象一個IViewModelTagger實例的一部分,並使用下面的序列(大約):

  1. 它訂閱該事件ITextBuffer.ChangedLowPriorityasync void事件處理程序。
  2. 立即着火,它通過撥打CancellationToken.Cancel()來取消正在進行的任何解析操作。取消令牌被傳遞到支持它的所有東西(在Roslyn中,它支持的任何地方都可以)。
  3. 它開始解析操作,但在開始之前,我有一個Task.Delay(200, m_cancellationToken)調用。根據我的打字速度以及Roslyn的操作對於任何昂貴的東西(我的解析工作都相當輕量級)都有重載的事實,我需要200ms。因人而異。

我需要在UI線程頗有幾分WPF組件工作,他們的IViewModelTaggerIWpfTextViewListener內混雜。它們足夠輕便,可以跳過異步處理它們,但是在非常大的類中它們可以掛起UI。

爲了解決這個問題,我做了以下內容:

  1. 在TextViewLayoutChanged,我同意與async void事件處理程序。
  2. Task.Run()首先是昂貴的操作,防止UI被阻止。
  3. 當我最終創建WPF UI元素並將它們添加爲裝飾定稿(以及SDK中需要它的一些操作)時,我需要await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync()來獲取UI線程。

我提到「其他SDK操作」,這很重要。除了主線程(內存現在讓我失敗,但特別是TextView的某些部分將失敗,並且如果它們在後臺線程上訪問,則不一致),在SDK內部有幾件事情無法做到。

還有更多選項可用於執行UI線程的工作(普通Task.Run作品以及ThreadHelper.JoinableTaskFactory.Run)。之前在我的答案中鏈接的Andrew Arnott帖子解釋了所有的選擇。你會完全理解這一點,因爲根據任務的不同,有理由使用一些。

希望有幫助!

0

在你的代碼中使用Task.Delay返回時,你耽誤的是完成一個任務。如果你這樣稱呼而忽略了結果,那麼它並沒有做到你認爲的那樣。是不是調用Task.Factory.StartNew爲你做什麼,你大概意思,你想:

var cancellationTokenSource = new CancellationTokenSource(); 
Task.Delay(2000, cancellationTokenSource.Token).ContinueWith(() => DoWork(), cancellationTokenSource.Token, TaskScheduler.Current). 

這是說有效地「揭開序幕,一個計時器,會等待2秒鐘,然後一旦完成運行DoWork方法在UI線程上如果有更多的輸入發生,那麼你可以調用cancellationTokenSource.Cancel()並且再次運行

另外,我不得不問你的類型是「CSharpRegionParser」如果你需要區域信息和你使用的是Visual Studio 2015,那麼你可以從Roslyn那裏得到語法樹,你應該看着工作區更改事件而不是掛鉤LayoutChanged。你最好還是將系統構建成一個標記/裝飾管理器對因爲它可能會更清晰......我不清楚爲什麼你會在LayoutChanged中解析邏輯,因爲LayoutChanged是在視覺佈局過程中發生的事情,包括滾動,調整大小等。

+0

我試過但我失敗了。我需要的是一名調度員。 –