简体   繁体   English

更改集合时更新 ObservableCollection 中的项目

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

Question: How can I update all items within an ObservableCollection when the CollectionChanged-Event is raised?问题:当引发 CollectionChanged-Event 时,如何更新 ObservableCollection 中的所有项目?

I search a lot but not find any suitable question with an answer for this.我搜索了很多,但没有找到任何合适的问题来回答这个问题。 Maybe I searched wrong or my code is crap at all... ;-)也许我搜索错了,或者我的代码完全是废话...... ;-)

Background: I got a list of items from the user and need to check them in some criteria.背景:我从用户那里得到了一个项目列表,需要在某些条件下检查它们。 One of the criteria is that one attribut needs to be gapless within the submitted items.标准之一是一个属性需要在提交的项目中没有间隙。 This attribut is not directly visible to the user, so I cannot just throw him back to give me correct items.这个属性对用户来说是不直接可见的,所以我不能只是把他扔回去给我正确的物品。 That is because I show him a Datagrid with the information which items are ok and which not.那是因为我向他展示了一个 Datagrid,其中包含哪些项目可以,哪些不可以的信息。 Now he is be able to delete items by selecting them and press del (I cannot decide which one should be deleted) so that finally all items in the list a valid.现在,他可以通过选择项目并按 del(我无法决定应该删除哪个项目)来删除项目,以便最终列表中的所有项目都有效。 Because it can happend that now other items not valid anymore, I need to check every item again after the list changed.因为现在其他项目可能不再有效,所以我需要在列表更改后再次检查每个项目。 Everything works fine until I delete something and then I got a "cannot change collection during CollectionChanged-Event"-error at the marked point.一切正常,直到我删除某些内容,然后在标记点出现“无法在 CollectionChanged-Event 期间更改集合”错误。

My project contains a more complex code (and checks), but for simplicity I try to show some code as example.我的项目包含更复杂的代码(和检查),但为简单起见,我尝试将一些代码作为示例。

Let us say I got a list of numbers from the user and every number needs to have an successor.假设我从用户那里得到了一个数字列表,每个数字都需要有一个后继者。 So I have class that take the Number and the information所以我有 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));
        }
}

in my Viewmodel I have a ObservableCollection在我的 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));
    }
}

in my Model在我的 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;
        }
    }

So, let say the user submit 1,3,5, 2, 6 -> with 4 missing.因此,假设用户提交 1,3,5,2,6 -> 缺少 4 个。 I will show him errormarks at number 5,6.我会在 5,6 号给他看错误标记。 but he can decide to delete 1,2,3 and import 5,6 because they now valid.但他可以决定删除 1,2,3 并导入 5,6,因为它们现在有效。

I can force the user to press button after change if items or create a "fire & forget" task that just wait a few milliseconds before starting to recheck, but I think that are not clean solutions... Maybe something like a transaction in a database with stop the change-event till i finish the changes...我可以强制用户在更改项目后按下按钮,或者创建一个“触发并忘记”任务,在开始重新检查之前等待几毫秒,但我认为这不是干净的解决方案......也许类似于交易数据库停止更改事件,直到我完成更改...

Anyone a hint or link for me?有人给我提示或链接吗?

You can't modify the collection during a CollectionChanged event.您不能在CollectionChanged事件期间修改集合。

You don't need the ref parameters.您不需要 ref 参数。 Number is a reference type and therefore changing it's values outside the collection does also change the object in the collection. Number是一个引用类型,因此在集合之外更改它的值也会更改集合中的 object。
It's a reference type, which means the reference is copied and not the value (memory location).它是一种引用类型,这意味着复制的是引用而不是值(内存位置)。 Although the references are not equal, they still point to the same memory location.尽管引用不相等,但它们仍然指向相同的 memory 位置。 Thus both references, original and copy, modify the same memory location ie same object instance.因此,原始和复制两个引用都修改了相同的 memory 位置,即相同的 object 实例。

Example例子

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 };
}

Furthermore, you should not pass around Exception objects.此外,您不应传递Exception对象。 Exceptions must be thrown using the throw keyword.必须使用 throw 关键字抛出异常。 In case of argument or null reference exceptions you want the application to crash, so that the developer can fix his code.如果出现参数或 null 引用异常,您希望应用程序崩溃,以便开发人员可以修复他的代码。 The exception message will show the exact line of code that has thrown the exception.异常消息将显示引发异常的确切代码行。

The following code is a fixed and improved/simplified version of your critical code section:以下代码是关键代码部分的固定和改进/简化版本:

// 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;
}

Also, because you are using the null-conditional operator ?.另外,因为您使用的是空条件运算符?. the explicit null check in OnPropertyChanged is redundant: OnPropertyChanged 中的显式OnPropertyChanged检查是多余的:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 ObservableCollection项目更改时如何更新ListBox? - How to update ListBox when ObservableCollection items changed? 当ObservableCollection中的集合更改时,将新条目添加到ReactiveList - When collection changed in ObservableCollection add new entries to ReactiveList ObservableCollection Collection更改事件未触发 - ObservableCollection Collection Changed event not firing ObservableCollection 项的异步更新 - Asynchronous update to ObservableCollection items 更改项目属性时如何进行ObservableCollection更新 - How to make an ObservableCollection update when an item property is changed 更改 ObservableCollection 的 item 属性时更新视图 - Update view when item's property of ObservableCollection is changed WPF绑定到ObservableCollection-仅在行完成时更新集合 - WPF Binding to ObservableCollection - only update collection when row is complete 当ObservableCollection中的元素的属性发生变化时更新ListBox项目 - Update ListBox Items when there are changes in property of element in ObservableCollection 绑定到ObservableCollection中的项目字段的总计,并在值更改时更新 - Bind to total of an items field in an ObservableCollection and update when values change 为什么将项目添加到 ObservableCollection 时我的视图没有更新 - Why doesn't my view update when adding items to the ObservableCollection
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM