![](/img/trans.png)
[英]How to update ListBox when ObservableCollection items changed?
[英]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.