.net 当ObservableCollection更改时重新评估LINQ查询

.net 当ObservableCollection更改时重新评估LINQ查询,.net,linq,linq-to-objects,observablecollection,.net,Linq,Linq To Objects,Observablecollection,我有一个共同的问题,我想(希望)找到一个更好的解决方案来继续前进。我有一个包含数据主列表的ObservableCollection。在我的客户机代码中,我需要将数据“转换”为新的形式,以便向用户显示。我使用LINQ语句,如: var newList = (from item in observable select new { FirstInitial = item.Name[0] }); 我知道这是相当基本的,但足以证明这个问题。(注意投影,这不是一个简单的筛选

我有一个共同的问题,我想(希望)找到一个更好的解决方案来继续前进。我有一个包含数据主列表的ObservableCollection。在我的客户机代码中,我需要将数据“转换”为新的形式,以便向用户显示。我使用LINQ语句,如:

var newList = (from item in observable
               select new { FirstInitial = item.Name[0] });
我知道这是相当基本的,但足以证明这个问题。(注意投影,这不是一个简单的筛选或分组语句。)然后我通过数据绑定在UI中显示newList

问题是,当原始集合更改时,UI不会更改。到目前为止,我应用的解决方案是将事件处理程序附加到原始集合的CollectionChanged事件,该事件重新计算查询并更新绑定属性

虽然这很好,但每次我遇到这个场景时都会有大量重复的代码。有没有一种方法可以让LINQ查询返回一个ObservableCollection,当源Observable被更改时,它会“自动”更新

换句话说,我想实现一些“固定”的功能,以便在出现这种情况时可以简单地重用它

更新

感谢Scroog1帮助我看到我原来的帖子与UI的耦合度太高,无法满足我真正的要求。以以下示例更好地描述问题:

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;
    }
}
公共类SomeClass
{
所有员工的私人可观察收集;
私人可观察收集(U current employees);;
公共可观测集合
{
得到
{
如果(_currentEmployees==null)
_currentEmployees=_allEmployees.Where(e=>!e.IsTerminated);
返回当前员工;
}
}
}
公共类视图模型
{
私有ICollectionView\u视图;
公共ICollectionView当前视图
{
如果(_view==null)
{
var cvs=new CollectionViewSource()
{
Source=someClass.CurrentEmployees
}
Add(新的SortDescription(“Name”,ListSortDirection.升序));
_view=cvs.view;
}
返回视图;
}
}

如您所见,查询所在的代码不是直接绑定到UI的代码。我用这个例子来说明,我是从一个更一般的用例而不是严格的UI数据绑定场景来提问的。

我会这样做(假设一个名为observable的ObservableCollection,类实现INotifyPropertyChanged with RaisePropertyChanged方法):

更新

上述方法通常适用于属性,因为每次访问它时都会得到最新的值,并且可以使用INPC来告诉事物重新读取它

对于更有趣的集合,我将实现一个自定义类,该类实现INotifyCollectionChanged和IEnumerable,并包装LINQ。例如:

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() { ... }
    ...
}
更新2

您甚至可以将依赖项传递给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() { ... }
    ...
}
公共类CustomObservableCollection:INotifyCollectionChanged,
InotifyProperty已更改,
数不清
{
私有只读IEnumerable\u集合;
公共CustomObservableCollection(IEnumerable collection,
参数ObservableCollection[]依赖项)
{
_收集=收集;
foreach(依赖项中的var dep)
dep.CollectionChanged+=RaiseCollectionChanged();
}
公共IEnumerator GetEnumerator()
{
_collection.GetEnumerator();
}
public void RaiseCollectionChanged(){…}
...
}

为什么不直接绑定到
可观察的
,然后选择所需的属性

e、 g:

public IEnumerable Items{get{返回this.observable;}

正如我在对Scroog1答案的评论中所提到的,我能够接受他的答案并对其进行轻微修改,以便在我的应用程序中使用。我最终得到了一个包装类,它接受原始集合、谓词和选择器函数作为构造函数参数,如下所示:

public class ObservableWrapper<TSource, TElement> : IEnumerable<TElement>,
                                                    INotifyCollectionChanged
{
    private Collection<TElement> _items;
    private Func<TSource, Boolean> _predicate;
    private Func<TSource, TElement> _selector;
    private ObservableCollection<TSource> _source;

    public ObservableWrapper(ObservableCollection<TSource> source,
                             Func<TSource, Boolean> predicate,
                             Func<TSource, TElement> selector)
    {
        _predicate = predicate;
        _selector = selector;
        _source = source;

        _source.CollectionChanged += SourceCollectionChanged;
    }

    public IEnumerator<TElement> GetEnumerator()
    {
        EnsureItems();

        return _items.GetEnumerator();
    }

    private void EnsureItems()
    {
        if (_items == null)
        {
            _items = new Collection<TElement>();

            RefreshItems();
        }
    }

    private void NotifyCollectionChanged(NotifyCollectionChangedAction action)
    {
        var handlers = CollectionChanged;

        if (handlers != null)
        {
            var args = new NotifyCollectionChangedEventArgs(action);

            handlers(this, args);
        }
    }

    private void RefreshItems()
    {
        _items.Clear();

        foreach (var element in _source)
        {
            if (_predicate(element))
            {
                var item = _selector(element);

                _items.Add(item);
            }
        }

        NotifyCollectionChanged(NotifyCollectionChangedAction.Reset);
    }

    private void SourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
    {
        RefreshItems();
    }

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
公共类ObservableWrapper:IEnumerable,
INotifyCollectionChanged
{
私人收藏项目;
私有Func_谓词;
专用函数选择器;
私有可观测采集源;
公共ObservableWrapper(ObservableCollection源,
Func谓词,
Func选择器)
{
_谓词=谓词;
_选择器=选择器;
_来源=来源;
_source.CollectionChanged+=SourceCollectionChanged;
}
公共IEnumerator GetEnumerator()
{
EnsureItems();
返回_items.GetEnumerator();
}
私人无效
{
如果(_items==null)
{
_items=新集合();
刷新项目();
}
}
私有void NotifyCollectionChanged(NotifyCollectionChangedAction操作)
{
var handlers=CollectionChanged;
if(处理程序!=null)
{
var args=new NotifyCollectionChangedEventArgs(操作);
处理程序(this,args);
}
}
私有项目()
{
_items.Clear();
foreach(源中的var元素)
{
if(_谓词(元素))
{
变量项=_选择器(元素);
_项目。添加(项目);
}
}
NotifyCollectionChanged(NotifyCollectionChangedAction.Reset);
}
私有void SourceCollectionChanged(对象发送方,NotifyCollectionChangedEventArgs e)
{
刷新项目();
}
公共事件通知CollectionChangedEventHandler CollectionChanged;
IEnumerator IEnumerable.GetEnumerator()
{
返回GetEnumerator();
var newList = new CustomObservableCollection(from item in observable
                                             select item.Name);
observable.CollectionChanged += delegate { newList.RaiseCollectionChanged(); };
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() { ... }
    ...
}
public IEnumerable<Item> Items { get { return this.observable;} }

<ItemsControl ItemsSource="{Binding Items}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <TextBlock Text="{Binding Name}" />
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>
public class ObservableWrapper<TSource, TElement> : IEnumerable<TElement>,
                                                    INotifyCollectionChanged
{
    private Collection<TElement> _items;
    private Func<TSource, Boolean> _predicate;
    private Func<TSource, TElement> _selector;
    private ObservableCollection<TSource> _source;

    public ObservableWrapper(ObservableCollection<TSource> source,
                             Func<TSource, Boolean> predicate,
                             Func<TSource, TElement> selector)
    {
        _predicate = predicate;
        _selector = selector;
        _source = source;

        _source.CollectionChanged += SourceCollectionChanged;
    }

    public IEnumerator<TElement> GetEnumerator()
    {
        EnsureItems();

        return _items.GetEnumerator();
    }

    private void EnsureItems()
    {
        if (_items == null)
        {
            _items = new Collection<TElement>();

            RefreshItems();
        }
    }

    private void NotifyCollectionChanged(NotifyCollectionChangedAction action)
    {
        var handlers = CollectionChanged;

        if (handlers != null)
        {
            var args = new NotifyCollectionChangedEventArgs(action);

            handlers(this, args);
        }
    }

    private void RefreshItems()
    {
        _items.Clear();

        foreach (var element in _source)
        {
            if (_predicate(element))
            {
                var item = _selector(element);

                _items.Add(item);
            }
        }

        NotifyCollectionChanged(NotifyCollectionChangedAction.Reset);
    }

    private void SourceCollectionChanged(Object sender, NotifyCollectionChangedEventArgs e)
    {
        RefreshItems();
    }

    public event NotifyCollectionChangedEventHandler CollectionChanged;

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}
var newList = new ObservableWrapper<Person, Char>(observable,
                                                  item => { return true; },
                                                  item => { return item.Name[0]; });