6

I'm working on comparing two versions of a listview, for a settings form. I need to know if the user actually modified the list at all, in which case when they click "Save" I'll actually save. If they didn't change anything, when they click "Save" I won't be wasting memory/time re-saving something they didn't change.

Anyway, how can I compare two ObservableCollections to see if they are at all different?

Thanks in advance!

Gayot Fow
  • 8,710
  • 1
  • 35
  • 48
mattsven
  • 22,305
  • 11
  • 68
  • 104

3 Answers3

5

You can use the LINQ Except method: Produces the set difference of two sequences.

http://msdn.microsoft.com/en-us/library/system.linq.enumerable.except.aspx

Consider the following sample method...

public void ExceptFunctioni()
{
    int[] numbersA = { 0, 2, 4, 5, 6, 8, 9 };
    int[] numbersB = { 1, 3, 5, 7, 8 };
    IEnumerable<int> aOnlyNumbers = numbersA.Except(numbersB);
    if(aOnlyNumbers.Count()>0)
    {
        // do something
    }
}

The Except method is invoked on the first collection and passed the second collection as an argument. The result will contain the differences. You can then query the result and take action accordingly. If both sequences are equal, the result's count will be zero.

Having said that, it's worthy to note that the preferred strategy in the MVVM world would be to use this method to control whether or not your 'Save' button is enabled. In this approach, if the two collections are equal, the 'Save' button would be disabled and the user could not access it.

But either way, the LINQ method offers a very condensed way of achieving what you're after...

ADDING: seeing the comments you made in reply to 'Dumb's' comment, your 'oldList' would correspond to numbersB in the sample code above...


Also the comment from 'Stonetip' (to whom thanks)...

More succinct: if(numbersA.Except(numbersB).Any()) { // do something } 
Community
  • 1
  • 1
Gayot Fow
  • 8,710
  • 1
  • 35
  • 48
  • I think those words are little confusing and "oldList is the is just the old list (the current settings before the user changes them)" means he may be saying that one is saved already to the settings and the current list means not saving. He may not be having two collections but one saved already ( which he virtualises to us as one collection) and one on the listview. If he has both collections in memory , then its good to use except method of LINQ. +1 for using LINQ(concise approach). Lets hear from him about more description on his work. – King Jan 01 '12 at 14:59
  • @Dumb, hi and happy new year! Would you please use the 'edit' facility to amend the confusing part so that it is clear? Are you meaning that the settings are not meant to be persistent? – Gayot Fow Jan 01 '12 at 15:10
  • Happy New year ! I'm waiting for him to clarify a bit more so that we will understand exactly what he needs and we can edit those. Yes. I get the idea that his data is not persistent. But certainly two versions doesn't seem coherent with what he has said in comments! So Mebbe he wants to check everytime something is done with listview, which is why I suggested checking out the events for listview approach. But if he has two versions of persistent data, I would recommend using your approach ! – King Jan 01 '12 at 15:27
  • As yet, he did not accept an answer, so perhaps he intends to to clarify further... – Gayot Fow Jan 01 '12 at 15:47
  • 2
    More succinct: if(numbersA.Except(numbersB).Any()) { // do something } – Stonetip Sep 13 '13 at 15:42
2

The way that we handle this requires a little more work up front, but it can be automated using VS macros or code-gen tools such as CodeSmith.

However, this approach is extensible to any UI construct that the collection is bound to and doesn't have to be re-implemented in the UI each time you need to know whether there are changes or not.

The concept is to update flags within the collection and business object to determine whether or not the collection membership has changed or any given record has changed within the collection.

The implementation is fairly simple:

Add a HasChanged property to the business object class.

Add an AnyDeleted property to the collection. This will only be set if items are deleted from the collection.

Initialize these values to false after the records are read from the DB.

(Now the semi-tedious part) For each property in the class, set the HasChanged property to true if the value actually changes. Be careful of null values. For example:

    public bool IsSelected
    {
        get
        {
            return m_fIsSelected;
        }
        set
        {
            if (m_fIsSelected != value)
            {
                this.HasChanged = true;
                m_fIsSelected = value;
            }
        }
    }

Modify the collection to set the AnyDeleted property to true when a record is deleted:

    protected override void RemoveItem(int index)
    {
        this.AnyDeleted = true;

        base.RemoveItem(index);
    }

Finally, add a method to the collection to indicate whether or not anything has changed. This is the method that you will call to determine whether or not any changes need to be saved:

   public bool HasAnyChanges()
    {
        // Exceptions are handled by the caller

        // If anything was deleted, return true
        if (this.AnyDeleted)
        {
            return true;
        }
        else
        {
            foreach (T theItem in this)
            {
                if (theItem.HasAnyChanges())
                {
                    return true;
                }
            }
        }

        return false;
    }
competent_tech
  • 44,465
  • 11
  • 90
  • 113
  • wow, so ObservableCollections has no built in way to compare contents? coming from c++ I find this really odd. – uceumern Oct 23 '18 at 11:10
-1

I think, you are focusing on the wrong approach. You shouldn't compare the content of 2 lists binded to a ListView, also because the quantity of items they contain can be prohibitively large.

  • It's better to focus on defining a single (if this is possible) and uniform way to be able to change the content of a collection from the API, providing to your class consumer a general way to be able to change something in collection. If that method is used, you can hold a boolean flag that identifies if something was changed.

  • Or you can suppose, that if someone uses set method inside binded collection property, means the collection was changed.

In other words, relay or on predefined workflow of your application, or define an API for changing content, so you will be able to figure out when and if the content of the collections was changed.

And another notion yet: there is no sense to let to the user click Save and do not save. If it's possible to click Save the command requested by the user has to be executed. If you are wary about performance (you don't want to save something, if it wasn't change from last save), so disable the Save button, if the save is not appropriate. In other words, make a UI fill and behave as it expected by your app. Making to the user clear what app does now and what it does not.

Gayot Fow
  • 8,710
  • 1
  • 35
  • 48
Tigran
  • 61,654
  • 8
  • 86
  • 123