【问题标题】:Comparing two ObservableCollection(s) to see if they are different比较两个 ObservableCollection(s) 以查看它们是否不同
【发布时间】:2012-01-01 00:17:09
【问题描述】:

我正在比较两个版本的列表视图,用于设置表单。我需要知道用户是否真的修改了列表,在这种情况下,当他们点击“保存”时,我实际上会保存。如果他们没有更改任何内容,那么当他们单击“保存”时,我不会浪费内存/时间来重新保存他们没有更改的内容。

无论如何,我如何比较两个 ObservableCollections 以查看它们是否完全不同?

提前致谢!

【问题讨论】:

    标签: c# wpf linq observablecollection


    【解决方案1】:

    您可以使用 LINQ except 方法:产生两个序列的集合差。

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

    考虑以下示例方法...

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

    Except 方法在第一个集合上调用,并将第二个集合作为参数传递。结果将包含差异。然后,您可以查询结果并采取相应措施。如果两个序列相等,则结果计数为零。

    话虽如此,值得注意的是,MVVM 世界中的首选策略是使用此方法来控制是否启用“保存”按钮。在这种方法中,如果两个集合相等,“保存”按钮将被禁用,用户无法访问它。

    但无论哪种方式,LINQ 方法都提供了一种非常简洁的方式来实现您所追求的...

    添加:看到您在回复 'Dumb's' 评论时所做的 cmets,您的 'oldList' 将对应于上面示例代码中的 numbersB...


    还有来自“Stonetip”的comment(感谢)...

    More succinct: if(numbersA.Except(numbersB).Any()) { // do something } 
    

    【讨论】:

    • 我认为这些话有点令人困惑,“oldList 只是旧列表(用户更改它们之前的当前设置)”意味着他可能会说一个已经保存到设置中并且当前列表意味着不保存。他可能没有两个集合,但一个已经保存(他将其虚拟化为一个集合)和一个在列表视图中。如果他在内存中有两个集合,那么它很好地使用除了 LINQ 方法。 +1 用于使用 LINQ(简洁方法)。让我们听听他对他作品的更多描述。
    • @Dumb,你好,新年快乐!请您使用“编辑”工具来修改令人困惑的部分,使其清楚吗?您的意思是这些设置不是持久的吗?
    • 新年快乐!我正在等待他进一步澄清,以便我们准确了解他的需求,然后我们可以对其进行编辑。是的。我知道他的数据不是持久的。但肯定有两个版本似乎与他在 cmets 中所说的不一致!所以 Mebbe 他想在每次使用 listview 完成某些事情时检查,这就是为什么我建议检查事件以使用 listview 方法。但是如果他有两个版本的持久数据,我会推荐使用你的方法!
    • 目前他还没有接受答案,所以也许他打算进一步澄清......
    • 更简洁: if(numbersA.Except(numbersB).Any()) { // 做点什么 }
    【解决方案2】:

    我们处理这个问题的方式需要更多的前期工作,但可以使用 VS 宏或 CodeSmith 等代码生成工具来自动化。

    但是,这种方法可以扩展到集合绑定到的任何 UI 构造,并且每次您需要知道是否有更改时都不必在 UI 中重新实现。

    这个概念是更新集合和业务对象中的标志,以确定集合成员是否已更改或集合中的任何给定记录是否已更改。

    实现相当简单:

    向业务对象类添加 HasChanged 属性。

    将 AnyDeleted 属性添加到集合中。仅当从集合中删除项目时才会设置此设置。

    在从数据库中读取记录后,将这些值初始化为 false。

    (现在是半乏味的部分)对于类中的每个属性,如果值实际更改,则将 HasChanged 属性设置为 true。注意空值。例如:

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

    修改集合以在删除记录时将 AnyDeleted 属性设置为 true:

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

    最后,向集合中添加一个方法来指示是否有任何更改。这是您将调用以确定是否需要保存任何更改的方法:

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

    【讨论】:

    • 哇,所以 ObservableCollections 没有内置方法来比较内容?来自 c++ 我觉得这很奇怪。
    【解决方案3】:

    我认为,您关注的是错误的方法。您不应该比较绑定到 ListView 的 2 个列表的内容,因为它们包含的项目数量可能非常大。

    • 最好专注于定义单一(如果可能的话)和统一的方式,以便能够从 API 更改集合的内容,为您的类消费者提供能够更改的通用方式收藏的东西。如果使用该方法,您可以保存一个布尔值 flag 来标识是否有更改。

    • 或者您可以假设,如果有人在绑定集合属性中使用set 方法,则意味着集合已更改。

    换句话说,中继或在您的应用程序的预定义工作流程上,或定义一个API 来更改内容,这样您就能够弄清楚whenif 集合的内容已更改。

    还有一个概念:让用户点击Save不保存 是没有意义的。如果可以单击Save,则必须执行用户请求的命令。如果您对性能持谨慎态度(您不想保存某些内容,如果它与上次保存相比没有更改),那么如果保存不合适,请禁用Save 按钮。换句话说,填写UI 并按照您的应用程序预期的方式运行。让用户清楚应用现在做什么和不做什么。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-14
      • 1970-01-01
      • 1970-01-01
      • 2017-05-02
      • 2011-01-31
      • 2014-03-22
      • 1970-01-01
      相关资源
      最近更新 更多