重新评估LINQ查询时的ObservableCollection变化LINQ、ObservableCollection

2023-09-04 11:27:57 作者:少装逼丶少挨打。

我有,我想(希望)找到前进的更好的解决方案的共同课题。我有一个包含数据的主列表一个ObservableCollection。在我的客户code,我需要'改造'的数据转换成一种新的形式显示给用户。我使用的是LINQ的语句,如:

I have a common issue that I'd like to (hopefully) find a better solution for moving forward. I have an ObservableCollection containing a master list of data. In my client code I need to 'transform' the data into a new form for display to the user. I use a LINQ statement like:

var newList = (from item in observable
               select new { FirstInitial = item.Name[0] });

我知道这是pretty的简陋,但它足以说明问题。 (注意投影,这不是一个简单的过滤器或分组发言。)然后我通过数据绑定显示newList在我的UI。

I know it's pretty rudimentary but it is enough to demonstrate the problem. (Notice the projection, this is not a simple filter or grouping statement.) I then display newList in my UI via data-binding.

问题是,当原始集合的变化,在UI没有。我迄今所应用的解决方案是将事件处理程序附加到原始集合的CollectionChanged事件,重新评估的查询和更新绑定属性。

Problem is, when the original collection changes, the UI doesn't. The solution I've applied thus far has been to attach an event handler to the original collection's CollectionChanged event which re-evaluates the query and updates the bound property.

虽然这工作得很好,它的每一个我碰到这种情况下运行时有很多重复的code。有没有办法,我可以有LINQ查询返回自动时,可观察到的改变的源更新一个ObservableCollection的方式?

While this works fine, it's a lot of repeated code every time I run across this scenario. Is there a way that I can have the LINQ query return an ObservableCollection that "automatically" updates when the source Observable is changed?

换句话说,我想实现一些罐头功能,这样我可以简单地重复使用它,只要我有这种情况。

In other words, I'd like to implement some 'canned' functionality so I can simply reuse it whenever I have this scenario.

更新

感谢Scroog1帮我看看,我原来的职位太连接到用户界面的东西我真的问。看看下面的例子为更好的说明这个问题:

Thanks to Scroog1 for helping me see my original post was too coupled to the UI for what I was really asking. Take the following example as a better description of the problem:

public class SomeClass
{
    private ObservableCollection<Employee> _allEmployees;
    private ObservableCollection<Employee> _currentEmployees;

    public ObservableCollection<Employee> CurrentEmployees
    {
        get
        {
            if (_currentEmployees == null)
                _currentEmployees = _allEmployees.Where(e => !e.IsTerminated);

            return _currentEmployees;
        }
    }
}

public class SomeViewModel
{
    private ICollectionView _view;

    public ICollectionView CurrentView
    {
        if (_view == null)
        {
            var cvs = new CollectionViewSource()
            {
                Source = someClass.CurrentEmployees
            }

            cvs.Add(new SortDescription("Name", ListSortDirection.Ascending));

            _view = cvs.View;
        }

        return _view;
    }
}

正如你所看到的,code其中存在的查询是不是直接绑定到用户界面。我用这个例子来证明,我从一个更普遍的用例要求不是严格意义上的UI数据绑定方案。

As you can see, the code where the query exists is not what is directly bound to the UI. I use this example to demonstrate that I am asking from a more general use-case than strictly a UI data-binding scenario.

推荐答案

我会做这样的事情(假设一个ObservableCollection称为观察到的,类实现INotifyPropertyChanged的与RaisePropertyChanged法):

I would do something like this (assuming an ObservableCollection called observable and class implementing INotifyPropertyChanged with RaisePropertyChanged method):

public IEnumerable<string> NewList
{
    return from item in observable
           select item.Name;
}

observable.CollectionChanged += delegate { RaisePropertyChanged("NewList"); };

然后,可观察到,当被改变时,UI将被告知NewList已经改变并重新评估该查询。

Then the when observable is changed, the UI will be told that NewList has changed to and re-evaluate the query.

有关多个相关项目,你可以这样做:

For multiple dependent items you can do:

observable.CollectionChanged += delegate
    {
        RaisePropertyChanged("NewList",
                             "OtherProperty",
                             "YetAnotherProperty",
                             "Etc");
    };

更新

以上工作正常,在一般的属性,因为你每次都得到最新的价值,你访问它,INPC可以用来告诉的东西来重新阅读。

The above works fine in general for properties, as you will get the latest value every time you access it and INPC can be used to tell things to re-read it.

有关收藏的稍微更有趣的情况下,我将执行一个实现INotifyCollectionChanged和IEnumerable和包装了LINQ的自定义类。例如,

For the slightly more interesting case of collections, I would implement a custom class that implements INotifyCollectionChanged and IEnumerable and wraps the LINQ. E.g.,

public class CustomObservableCollection<T> : INotifyCollectionChanged,
                                             INotifyPropertyChanged,
                                             IEnumerable<T>
{
    private readonly IEnumerable<T> _collection;
    public CustomObservableCollection(IEnumerable<T> collection)
    {
        _collection = collection;
    }
    public IEnumerator<T> GetEnumerator()
    {
        _collection.GetEnumerator();
    }
    public void RaiseCollectionChanged() { ... }
    ...
}

然后,你可以这样做:

Then you can do:

var newList = new CustomObservableCollection(from item in observable
                                             select item.Name);
observable.CollectionChanged += delegate { newList.RaiseCollectionChanged(); };

更新2

您可以在依赖甚至传递到CustomObservableCollection:

You could even pass the dependency to CustomObservableCollection:

public class CustomObservableCollection<T> : INotifyCollectionChanged,
                                             INotifyPropertyChanged,
                                             IEnumerable<T>
{
    private readonly IEnumerable<T> _collection;
    public CustomObservableCollection(IEnumerable<T> collection,
                 params ObservableCollection[] dependencies)
    {
        _collection = collection;
        foreach (var dep in dependencies)
            dep.CollectionChanged += RaiseCollectionChanged();
    }
    public IEnumerator<T> GetEnumerator()
    {
        _collection.GetEnumerator();
    }
    public void RaiseCollectionChanged() { ... }
    ...
}