2013-12-11 70 views
1

我有我的GUI和線程的問題。 GUI包含DataGrid。每X次該程序做一些查詢並獲取我想要填充到DataGrid中的項目列表。Wpf應用程序和線程

到目前爲止好:

private void loadTaskList() //Call every X time 
{ 
    List<myObject> myList = myquery(); 
    this.Dispatcher.Invoke((Action)(() => 
    { 
     TaskListTable.Items.Clear(); //Clear the DataGrid 
     foreach (myObject O in myList) //Add the items from the new query. 
     { 
      TaskListTable.Items.Add(O); 
     } 
    }));      
    FindSelectionObject(); // <-- see next explanation. 
} 

當DataGrid中的對象之一的用戶點擊,線的顏色變化(正常工作),但是當程序重新加載表,該畫線消失(我打算清除並添加新的對象)。

來對付它,我創建了功能FindSelectionObject():

private void FindSelectionObject() 
{ 
    this.Dispatcher.Invoke((Action)(() => 
    { 
     this.SelectedIndex = TaskListTable.Items.IndexOf((myObject)lastSelectionObject); //find index of the new object that equels to the last selection object. 
     var row = TaskListTable.ItemContainerGenerator.ContainerFromIndex(SelectedIndex) as DataGridRow; //get the row with the index 
     row.Background = Brushes.LightGoldenrodYellow; //repaint 
    })); 
} 

問題:一切正常,但有時當程序重新加載,每秒線閃爍,然後強調了回來,有時它根本不畫(直到下一次重新加載)。

enter image description here

我不明白爲什麼會這樣。我想可能FindSelectionObject()loadTaskList()結束之前開始運行,調用all並將新對象添加到數據網格中。 但如果是這樣 - 爲什麼?我該如何解決它?

在底線,我想,經過每一個重載線重新漆立即..

感謝您的諮詢!

回答

1

有幾件事情要考慮:

你應該記住的是,DataGrid使用虛擬化,這意味着,在您的項目源的每個項目不獲得它自己的UI元素。創建UI元素以填充可見區域,然後根據當前綁定哪個數據源項目(當您滾動實例或更改項目源時發生更改)重新使用。如果您使用目前的方法,這可能會在未來導致您的問題,因此請記住這一點。

另一件事是DataGrid可能需要佈局過程更多的「週期」,以更新其用戶界面。您可能只是過早地致電FindSelectionObject。您在loadTaskList調用後立即排隊FindSelectionObject。如果DataGrid需要在項目源發生更改後執行在調度程序中排隊的某些操作,則這些操作將在調用FindSelectionObject後執行。 試試這個:

private void loadTaskList() //Call every X time 
{ 
    List<myObject> myList = myquery(); 
    this.Dispatcher.Invoke((Action)(() => 
    { 
     TaskListTable.Items.Clear(); //Clear the DataGrid 
     foreach (myObject O in myList) //Add the items from the new query. 
     { 
      TaskListTable.Items.Add(O); 
     } 

     // The items of the grid have changed, NOW we QUEUE the FindSelectionObject 
     // operation on the dispatcher. 

     FindSelectionObject(); // <-- ((MOVE IT HERE)) !! 
    })); 
} 

編輯:好吧,如果失敗,那麼也許這將覆蓋在上面的解決方案失敗的情況下:訂閱LoadingRow事件DataGrid,如果該行設置相應的背景顏色是選定的一個。因此,在創建新行的情況下,將調用此事件(由於虛擬化,它不會在項目源中針對每個項目調用,而是按實際行UI元素調用)。在事件參數中,您將有權訪問創建的DataGridRow實例。

+0

謝謝@ odyss-jii。你的答案的第二部分是我以爲發生了什麼,我試過你的代碼,但結果是它重新加載後從未突出行。我明白,我的解決方案是真的凌亂,但我沒有其他的想法,任何提示? – Yaron

+0

嗯,你可能想嘗試'LoadingRow'事件。我已經用關於這方面的一些信息更新了答案。我希望這能更好地工作。 –

+0

It Works!我註冊到事件'LoadingRow'並運行'FindSelectionObject'。現在我甚至可以從'loadTaskList'中刪除'FindSelectionObject'。謝謝@ odyss-jii! – Yaron

0

你是否在某處維護對lastSelectionObject的引用?你說你添加了新的對象,如果它們真的是新的,那麼引用將會不同,並且在IndexOf中發生的引用比較將無法找到它。

+0

它的工作原理是因爲每個myObject都有id,並且我用equels()來檢查這個id,所以IndexOf()可以找到它。 @阿蘭老人 – Yaron

1

我覺得這個問題可能是一個可視化的線程同步。爲此,您可以創建並使用類似這樣的方法:

public void LockAndDoInBackground(Action action, string text, Action beforeVisualAction = null, Action afterVisualAction = null) 
    { 
     var currentSyncContext = SynchronizationContext.Current; 
     var backgroundWorker = new BackgroundWorker(); 
     backgroundWorker.DoWork += (_, __) => 
     { 
      Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US"); 
      Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US"); 
      currentSyncContext.Send((t) => 
      { 
       IsBusy = true; 
       BusyText = string.IsNullOrEmpty(text) ? "Espere por favor..." : text; 
       if (beforeVisualAction != null) 
        beforeVisualAction(); 
      }, null); 
      action(); 
      currentSyncContext.Send((t) => 
      { 
       IsBusy = false; 
       BusyText = ""; 
       if (afterVisualAction != null) 
        afterVisualAction(); 
      }, null); 
     }; 
     backgroundWorker.RunWorkerAsync(); 
    } 

IsBusyBusyText是特定的屬性,你可以刪除。 action變量將是在後臺執行的操作(例如加載項目)。 beforeVisualActionafterVisualAction是您可能想要在後臺操作之前和之後執行的視覺操作。以下是任何可視化更新,例如選擇您的項目,更改顏色,設置引發綁定更新的視圖模型變量,...(任何更新視圖的操作)。 希望這種方法有所幫助。