List和ObservableCollection的轉換

孤沉發表於2024-03-31

1、我們後臺查詢全部List資料的時候,前臺需要ObservableCollection展示
這個時候List需要轉換成ObservableCollection

 public static ObservableCollection<T> ToObservableCollection<T>(this IEnumerable<T> source)
 {
     if (source == null)
     {
         throw new ArgumentNullException("source");
     }
     return new ObservableCollection<T>(source);
 }

2、但是這會出現一個問題,假如我們使用的非同步查詢

  var asideMenus = await _asideMenuService.QueryListAsync();
  AsideMenus = asideMenus.ToObservableCollection();

此時AsideMenus為空,這是因為asideMenus是在後臺查詢到的,即使你轉化成了ObservableCollection它還沒有更新到UI
3、解決方法
解決1、需要使用Foreach,ForEach 是 LINQ 中的一個擴充套件方法,它遍歷集合中的每個元素並對其執行指定的操作。當您在 UI 執行緒上呼叫 ForEach 時,它將直接更新 ObservableCollection,並且 CollectionChanged 事件將被觸發,從而使 UI 得到更新

(await _asideCreateControlService.QueryListAsync()).ForEach(x => PlayListInputDtos.Add(x));

解決2、使用Dispatcher,WPF 使用 Dispatcher 來管理執行緒間的操作。當您從後臺執行緒呼叫 Dispatcher.Invoke 或 Dispatcher.BeginInvoke 時,您實際上是在請求 UI 執行緒稍後執行指定的操作。這對於更新 UI 控制元件和集合是必要的,因為 WPF 要求這些操作必須在建立它們的執行緒上執行

  foreach (var menu in await _asideMenuService.QueryListAsync())
  {
      Dispatcher.CurrentDispatcher.Invoke(() => AsideMenus.Add(menu));
  }

解決3、但是當你操作大量UI時,透過減少 Dispatcher.Invoke 的呼叫次數,您可以減少執行緒建立和管理的開銷,從而提高效能。在這種情況下,您可以先在後臺執行緒中收集所有資料,然後在 UI 執行緒上一次性更新 ObservableCollection

  foreach (var menu in await _asideMenuService.QueryListAsync())
  {
      Application.Current.Dispatcher.Invoke(() => AsideMenus.Add(menu));
  }

解決4、是對解決3的擴充套件,重寫OnCollectionChanged方法

 public class ThreadSafeObservableCollection<T> : ObservableCollection<T>
 {
     public ThreadSafeObservableCollection()
     {
         
     }
     public ThreadSafeObservableCollection(IEnumerable<T> list) : base(new List<T>(list ?? throw new ArgumentNullException(nameof(list))))
     {
     }
     protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
     {
         // 確保 CollectionChanged 事件在 UI 執行緒上觸發
         if (Thread.CurrentThread.ManagedThreadId == Dispatcher.CurrentDispatcher.Thread.ManagedThreadId)
         {
             base.OnCollectionChanged(e);
         }
         else
         {
             Dispatcher.CurrentDispatcher.Invoke(() => base.OnCollectionChanged(e));
         }
     }

     // 其他需要重寫的 OnCollectionChanged 相關方法,如 OnAddingNew, OnRemoved, OnReplaced 等
 }

相關文章