【问题标题】:bind linq expression绑定 linq 表达式
【发布时间】:2010-06-12 17:11:23
【问题描述】:

我的 wpf 应用程序中有一个 ListView 和 ObservableCollection。 我想将 linq 表达式绑定到 ListView:

lv_Results.DataContext = store.Where(x => x.PR > 5).ToList();
tempStore = new M1Store()
                            {
                                SiteName = siteName,
                                Date = date,
                                url = IndexedPage,
                                TCY = YandexTCY.GetYandexTCY(IndexedPage),
                                PR = Pagerank.GetPR(IndexedPage)
                            };
            store.Add(tempStore);

但是当我添加新元素来存储集合时,lv_Results 不会更新。 如何更新 ListView?

【问题讨论】:

    标签: c# wpf linq data-binding


    【解决方案1】:

    您的问题是您希望对添加到“商店”集合的所有项目持续评估“位置”条件。内置的 LINQ “Where” 运算符并非设计用于执行此操作。相反,当它被 ListView 枚举时,它只会扫描您的集合一次,然后从那时起忽略它。

    查看Continuous LINQ。它旨在完全满足您的需求,并且几乎可以用作标准 LINQ 查询的直接替代品。

    内置 LINQ 实现的限制

    内置 LINQ 扩展方法有一个基本限制,即它们生成的集合不支持 INotifyPropertyChanged。因此,无论底层数据发生多大变化,客户端都不会收到集合已更改的通知,因此也不会刷新其数据显示。

    用户 jrista 在 cmets 中指出,如果重新枚举,内置 LINQ 方法实际上会生成最新数据。虽然是真的,但这没有实际效果。没有 WinForms 或 WPF 控件包含定期重新枚举其数据源的代码。当然,不这样做的原因很明显:这将是非常浪费的。如果您每秒重新枚举 10 次,并且重新枚举和扫描更改需要 10 毫秒,那么您将使用 10% 的 CPU 来进行一次控制!

    【讨论】:

    • 实际上,您对 Where 的评论完全不正确。每次迭代由它创建的 IEnumerable 时,将在哪里获取添加到商店集合的新项目。只有在调用 ToList 时,结果才会“及时确定”,因为它们随后会被迭代并添加到另一个集合中。请通过反射器查看内部 where 迭代器:WhereArrayIterator、WhereEnumerableIterator、WhereListIterator。在初始状态下,运行以下内容: this.enumerator = this.source.GetEnumerator();这会在迭代时从源中检索一个枚举器。
    • @jrista:不,抱歉,您对此不正确。在 Neir0 的代码中,您提到的“迭代时间”正是分配 DataContext 的那一刻。 ListView 立即枚举它的 ItemsSource 并且这样做恰好一次。因此,Where 条件只扫描一次。 Neir0 是否使用 ToList() 并不重要:他会得到完全相同的结果。 (事实上​​,他做到了。)它只会在 ListView 定期重新枚举集合时更新,但它不会。因此我的回答正确的。
    • @jrista:我并不是要暗示您不了解 Where 的工作原理。我只是想指出您如此专注于 Where 的内部结构,以至于您错过了大局 - NET Framework 中的内置 LINQ 功能都不是连续的,并且在 Neir0 的代码中删除 .ToList() 赢得了不要解决这个问题。 Continuous LINQ 人员开发了他们的系统来解决这个问题。几年前我也开发了一些非常相似的东西(所以我对 LINQ 也有非常深刻的理解),但我的代码还不是公共领域,所以我将 Neir0 指向 Continuous LINQ。
    • 听起来您在谈论 ListView 和 LINQ 交互的限制,而不是 LINQ 枚举器工作方式的限制。我会更新您的回复以表明,因为 Where(或任何其他 LINQ 方法)将在每次迭代它生成的枚举器时重新处理其源集合。
    • 显然你误解了我的意思。我绝对是在谈论 System.Data 的 LINQ 实现的限制,而不是任何特定的 LINQ 客户端。具体来说,由 Enumerable.Where 及其同类生成的集合不支持 INotifyCollectionChanged。 这使得 ListView 或 IEnumerable 的任何其他客户端 永远无法保持完美状态- 数据的最新视图。可以实现的最好的方法是以小的(比如 10 毫秒)间隔定期刷新,但这非常昂贵。 Emerald Data Foundation 和 Continuous LINQ 都解决了这个问题。
    【解决方案2】:

    将您的 LINQ 结果放入 ObservableCollection。 WPF 和 Silverlight 数据绑定要求集合引发更改通知,而 ObservableCollection 会为您做到这一点。否则你必须自己实现它,这更痛苦而且完全没有必要。

    【讨论】:

      【解决方案3】:

      LINQ 表达式的结果被输入一个新的 List(of T),它不会引发 PropertyChanged 或 CollectionChanged 事件。

      使其工作的最简单方法是检索您想要的结果,然后用您想要显示的结果填充 ObservableCollection(of T)。随着 ObservableCollection 添加到其中,新项目将出现在 ListView 中。

      【讨论】:

        【解决方案4】:

        将 .ToList() 添加到您的代码中,LINQ 评估惰性,因此它仅在需要时带来结果,.ToList() 是一个贪婪运算符并强制进行评估。

        [编辑]

        对不起,误解了你的第一个版本的问题:)

        这是我的解决方案:

        ObservableCollection<M1Store> l = store.Where(x => x.PR > 5);
        lv_Results.DataContext = l;
        

        仅此而已,在以下所有步骤中,更改 Observable 集合 l:

        List<M1Store> otherList = GetFromAnywhere();
        otherList.ForEach(e => l.Add(e));
        

        这里 Observable Collection 的内部将更新 UI 中的 listView。

        【讨论】:

        • 它不起作用。我在商店集合为空时绑定它并在绑定后添加元素。
        猜你喜欢
        • 1970-01-01
        • 2019-05-16
        • 2018-09-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多