簡體   English   中英

如何在可觀察集合的 UI 中立即反映任何添加、刪除、字段更改

[英]How to reflect any Adds, Removes, field changes immediately in the UI for Observable Collection

我在獲取對我的 Observable 集合(添加、刪除、字段更改)的更改以立即顯示在我的 TableView UI 中時遇到了真正的問題。

我寫了一個小程序來測試功能。 它可以工作,但不會立即反映更改,它會等到所有添加、刪除、更改完成后才顯示結果。

我希望它在發生時顯示每個更改。 即,如果我添加一條記錄,它會立即顯示更改。 UI 應該向下滾動屏幕,顯示添加發生時。

在我的代碼中,我包含了添加、刪除和更改之間的睡眠來證明這一點。

我需要做什么才能立即反映所有更改(添加、刪除、字段更改)?

附件是我擁有的一些代碼。

namespace ObservableTest
{
  internal static class Common
  {
    public static ObservableCollection OC = new ObservableCollection();

    public static void AddRecords(int HowMany = 100)
    {
        for(int i = 0; i < HowMany; i++)
        {
            OC.Add(new TestObservable("StringData " + i.ToString(), i));
            Thread.Sleep(1000); // Sleep to see outcome in UI
        }
    }

    public static void RemoveRecords(int HowMany = 5)
    {
        var data = OC.ToList();
        foreach(var field in data)
        {
            if(field.IntField < HowMany)
            {
                OC.Remove(field);
                Thread.Sleep(1000); // Sleep to see outcome in UI
            }
        }
    }

    public static void ChangeRecords(int HowMany = 5)
    {
        var data = OC.ToList();
        foreach(var field in data)
        {
            field.StringField += " C";
            Thread.Sleep(1000); // Sleep to see outcome in UI
        }
    }
}
public class TestObservable : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    public void NotifyPropertyChanged(string propName)
    { this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName)); }

    private string stringField = "";
    private int intField = 0;

    public TestObservable(string StringField, int IntField)
    {
        this.stringField = StringField;
        this.intField = IntField;
    }
    public string StringField
    {
        get { return stringField; }
        set
        {
            if (this.stringField != value)
            {
                this.stringField = value;
                this.NotifyPropertyChanged("StringField");
            }
        }
    }
    
    public int IntField
    {
        get { return intField; }
        set
        {
            if (this.intField != value)
            {
                this.intField = value;
                this.NotifyPropertyChanged("IntField");
            }
        }
     }
  }
}

您應該異步執行阻塞代碼。

要將您的示例轉換為異步,只需將阻塞Thread.Sleep替換為非阻塞Task.Delay

關鍵是 Thread.Sleep 確實阻塞了 UI 線程。 當 UI 線程處於休眠狀態時,它無法執行任何其他操作,例如渲染更改。
Thread.Sleep 不談:當您的阻塞(同步)代碼異步執行時,您允許 UI 線程保持響應。 UI 線程將能夠在執行異步代碼時呈現視圖中的更改。

internal static class Common
{
  public static ObservableCollection<TestObservable> OC = new ObservableCollection<TestObservable>();

  public static async Task AddRecords(int HowMany = 100)
  {
    for (int i = 0; i < HowMany; i++)
    {
      OC.Add(new TestObservable("StringData " + i.ToString(), i));
      await Task.Delay(1000); // Sleep to see outcome in UI
    }
  }

  public static async Task RemoveRecords(int HowMany = 5)
  {
    var data = OC.ToList();
    foreach (var field in data)
    {
      if (field.IntField < HowMany)
      {
        OC.Remove(field);
        await Task.Delay(1000); // Sleep to see outcome in UI
      }
    }
  }

  public static async Task ChangeRecords(int HowMany = 5)
  {
    var data = OC.ToList();
    foreach (var field in data)
    {
      field.StringField += " C";
      await Task.Delay(1000); // Sleep to see outcome in UI
    }
  }
}

您需要使用異步方法。
所有長時間運行的工作都應該在線程池上執行的任務中進行。 並且只有直接改變WPF View中綁定的集合才應該發生在應用程序的主線程上。
要在主線程上執行操作,首先需要獲取它,然后通過它的方法委托將其放入執行隊列。
放入隊列后,您可以等待它們的執行完成(如我的示例中所示),但您也可以跳過等待並立即繼續進一步處理。
這取決於您正在實施的算法以及處理結果的需要。

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;

namespace ObservableTest
{
    public static class Common
    {
        public static ObservableCollection<TestObservable> OC { get; }
            = new ObservableCollection<TestObservable>();

        public static Dispatcher Dispatcher { get; } = Application.Current.Dispatcher;

        private static readonly Action<TestObservable> AddRecord = OC.Add;
        private static readonly Func<TestObservable, bool> RemoveRecord = OC.Remove;
        private static readonly Action<int, TestObservable> ReplaceRecord = (index, item) => OC[index] = item;

        // Synchronous method. It is undesirable to call it from the UI thread.
        public static void AddRecords(int howMany = 100)
        {
            for (int i = 0; i < howMany; i++)
            {
                TestObservable test = new TestObservable("StringData " + i.ToString(), i);
                DispatcherOperation operation = Dispatcher.BeginInvoke(AddRecord, test);
                DispatcherOperationStatus status = operation.Wait();
                Thread.Sleep(1000); // Sleep to see outcome in UI
            }
        }

        // Asynchronous method. It can be called from the UI thread.
        public static async void AddRecordsAsync(int howMany = 100)
            => await Task.Run(() => AddRecords(howMany));

        public static void RemoveRecords(int howMany = 5)
        {
            var data = OC.ToList();
            foreach (TestObservable field in data)
            {
                if (field.IntField < howMany)
                {
                    DispatcherOperation operation = Dispatcher.BeginInvoke(RemoveRecord, field);
                    DispatcherOperationStatus status = operation.Wait();
                    Thread.Sleep(1000); // Sleep to see outcome in UI
                }
            }
        }
        public static async void RemoveRecordsAsync(int howMany = 5)
            => await Task.Run(() => RemoveRecords(howMany));

        public static void ChangeRecords(int howMany = 5)
        {
            var data = OC.ToList();
            foreach (TestObservable field in data)
            {
                field.StringField += " C";
                Thread.Sleep(1000); // Sleep to see outcome in UI
            }
        }
        public static async void ChangeRecordsAsync(int howMany = 5)
            => await Task.Run(() => ChangeRecords(howMany));
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM