簡體   English   中英

更改集合時更新 ObservableCollection 中的項目

[英]Update items in a ObservableCollection when Collection is changed

問題:當引發 CollectionChanged-Event 時,如何更新 ObservableCollection 中的所有項目?

我搜索了很多,但沒有找到任何合適的問題來回答這個問題。 也許我搜索錯了,或者我的代碼完全是廢話...... ;-)

背景:我從用戶那里得到了一個項目列表,需要在某些條件下檢查它們。 標准之一是一個屬性需要在提交的項目中沒有間隙。 這個屬性對用戶來說是不直接可見的,所以我不能只是把他扔回去給我正確的物品。 那是因為我向他展示了一個 Datagrid,其中包含哪些項目可以,哪些不可以的信息。 現在,他可以通過選擇項目並按 del(我無法決定應該刪除哪個項目)來刪除項目,以便最終列表中的所有項目都有效。 因為現在其他項目可能不再有效,所以我需要在列表更改后再次檢查每個項目。 一切正常,直到我刪除某些內容,然后在標記點出現“無法在 CollectionChanged-Event 期間更改集合”錯誤。

我的項目包含更復雜的代碼(和檢查),但為簡單起見,我嘗試將一些代碼作為示例。

假設我從用戶那里得到了一個數字列表,每個數字都需要有一個后繼者。 所以我有 class 接受號碼和信息

public class Number() : INotifyPropertyChanged
{
        public Number(int i)
        {
            digit = i;
            HasSuccessor = null;
        }

        private int digit;
        public int Digit { 
            get { return digit; }
            set { digit = value; OnPropertyChanged(); } }

        private bool? hasSuccessor;
        public bool? HasSuccessor {
            get { return hasSuccessor; }
            set { hasSuccessor = value; OnPropertyChanged(); } }

        public event PropertyChangedEventHandler? PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
        {
            if (PropertyChanged == null) return;
            else PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
}

在我的 Viewmodel 我有一個 ObservableCollection

public class Viewmodel : INotifyPropertyChanged
{
    private ObservableCollection<Lib.Number> numbers = new()
    {
        // Some Debugdata
        new Lib.Number(1),
        new Lib.Number(3),
        new Lib.Number(5),
        new Lib.Number(2),
        new Lib.Number(6)
    };

    public ObservableCollection<Lib.Number> Numbers {
        get { return numbers; }
        set { numbers = value; OnPropertyChanged(); } }

    public Viewmodel()
    {
        Numbers.CollectionChanged += RefreshCheck;
    }

    private void RefreshCheck(object? sender, NotifyCollectionChangedEventArgs e)
    {
        Check();
    }

    public void Check()
    {
        Lib.Model.CheckNumbers(ref numbers);
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
    {
        if (PropertyChanged == null) return;
        else PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

在我的 Model

public static class Model
    {
        public static bool CheckNumbers(ref ObservableCollection<Number> numbers)
        {
            List<Exception> exception = new();

            for (int i = 0; i < numbers.Count; i++)
            {
                Number number = numbers[i];
                Exception? result = CheckNumber(ref number, numbers);
                numbers[i] = number; //line that create the exception

                if(result != null) exception.Add(result);
            }

            if( exception.Any() == true) return false;
            else return true;
        }

        public static Exception? CheckNumber(ref Number number, ObservableCollection<Number> numbers)
        {
            Exception? error = null;

            Number num = number;
            if(numbers != null && numbers.Any() == true)
            {
                if (numbers.Where(item => item.Digit == (num.Digit + 1)).Any() == true)
                    number.HasSuccessor = true;
                else if (numbers.MaxBy(item => item.Digit).Digit == num.Digit)
                    number.HasSuccessor = true;
                else if (numbers.MinBy(item => item.Digit).Digit == num.Digit)
                    number.HasSuccessor = true;
                else
                    number.HasSuccessor = false;
            }
            else
            {
                Exception exception = new ("numbers was null");
            }

            return error;
        }
    }

因此,假設用戶提交 1,3,5,2,6 -> 缺少 4 個。 我會在 5,6 號給他看錯誤標記。 但他可以決定刪除 1,2,3 並導入 5,6,因為它們現在有效。

我可以強制用戶在更改項目后按下按鈕,或者創建一個“觸發並忘記”任務,在開始重新檢查之前等待幾毫秒,但我認為這不是干凈的解決方案......也許類似於交易數據庫停止更改事件,直到我完成更改...

有人給我提示或鏈接嗎?

您不能在CollectionChanged事件期間修改集合。

您不需要 ref 參數。 Number是一個引用類型,因此在集合之外更改它的值也會更改集合中的 object。
它是一種引用類型,這意味着復制的是引用而不是值(內存位置)。 盡管引用不相等,但它們仍然指向相同的 memory 位置。 因此,原始和復制兩個引用都修改了相同的 memory 位置,即相同的 object 實例。

例子

public static void Main()
{
  var person = new Person() { Age = 20 };
  person.Age = 30;
  Console.WriteLine(Person.Age); // Age == 30

  ChangeAgeTo100(person);
  Console.WriteLine(person.Age); // Age == 100

  ChangePersonInstance(person, 50);
  Console.WriteLine(person.Age); // Age == 100

  ChangePersonInstanceRef(ref person, 50);
  Console.WriteLine(person.Age); // Age == 50
}

private void ChangeAgeTo100(Person person)
{
  person.Age = 100;
}

private void ChangePersonInstance(Person person, int newAge)
{
  person = new Person() { Age = newAge };
}

private void ChangePersonInstanceRef(ref Person person, int newAge)
{
  person = new Person() { Age = newAge };
}

此外,您不應傳遞Exception對象。 必須使用 throw 關鍵字拋出異常。 如果出現參數或 null 引用異常,您希望應用程序崩潰,以便開發人員可以修復他的代碼。 異常消息將顯示引發異常的確切代碼行。

以下代碼是關鍵代碼部分的固定和改進/簡化版本:

// Will throw an exception in case the numbers collection is null or empty
public static void CheckNumbers(ObservableCollection<Number> numbers)
{
  foreach (Number number in numbers)
  {
    CheckNumber(number, numbers);  
  }
}

public static void CheckNumber(Number number, ObservableCollection<Number> numbers)
{
  if (numbers == null)
  {
    throw new ArgumentNullException(nameof(numbers));
  }

  if (!numbers.Any())
  {
    throw new ArgumentException("Collection is empty", nameof(numbers));
  }
        
  number.HasSuccessor = 
    numbers.Any(item => item.Digit == number.Digit + 1)
      || numbers.Max(item => item.Digit) == number.Digit
      || numbers.Min(item => item.Digit) == number.Digit;
}

另外,因為您使用的是空條件運算符?. OnPropertyChanged 中的顯式OnPropertyChanged檢查是多余的:

protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
  PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

暫無
暫無

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

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