如何添加到带有LINQ Where()的Getter中的ObservableCollection <t>项?</t>

时间:2012-07-24 13:34:50

标签: c# linq mvvm

我最近在我的一个域实体中添加了一个Disabled属性。我在视图模型中有List<T>个实体。我刚刚更改了getter以根据UI设置过滤列表,以选择性地显示已禁用的项目。

public ObservableCollection<CustomVariableGroup> CustomVariableGroups
{
    get
    {
        if (this.ShowDisabled) { return this._customVariableGroups; }

        return new ObservableCollection<CustomVariableGroup>(this._customVariableGroups.Where(x => x.Disabled == false));
    }
}

问题在于,当我现在尝试这样做时(在相同的视图模型中),该项目不会被添加到集合中。

this.CustomVariableGroups.Add(newGroup);

我想我知道原因:newGroup被添加到新的ObservableCollection<T>,而不是_customVariableGroups的支持字段。

如果LINQ Where()扩展方法能够返回ObservableCollection<T>(私有支持字段本身的类型)而不是IEnumerable,我认为我没有这个问题。现在,由于Where()返回IEnumerable<T>,我需要将其包含在新的ObservableCollection<T>中。

在同一视图模型中添加新项目的正确/最佳方式是什么?我是否需要将其直接添加到私人支持字段?我不想这样做,因为我不想记住不要使用该属性本身。

5 个答案:

答案 0 :(得分:1)

而不是直接绑定到您的集合,您可以绑定到 ICollectionView 并在Disabled属性中设置Filter。无需更改源集合。

编辑:

视图模型:

 //this collection just init once - eg. in ctor, use add, remove, clear to alter
 public ObservableCollection<CustomVariableGroup> CustomVariableGroups {get; private set; }
 //create just once in ctor
 public ICollectionView MyView {get; private set;}

 //ctor
 this.CustomVariableGroups = new ObservableCollection<CustomVariableGroup>();
 this.MyView = CollectionViewSource.GetDefaultView(this.CustomVariableGroups);

 //in your disabled property set the filter
    public bool ShowDisabled
    {
        get { return _showDisabled; }
        set { 
            _showDisabled = value;

            if (_showDisabled)
                //show just disabled
                this.MyView.Filter = (item) =>
                                         {
                                             var myitem = (CustomVariableGroup) item;
                                             return myitem.Disabled;
                                         };
            else
            {
                //show all
                this.MyView.Filter = (item) => { return true; };
            }


            this.NotifyPropertyChanged("ShowDisabled"); }
    }

XAML

    <CheckBox Content="ShowDisabled" IsChecked="{Binding ShowDisabled}"/>
    <ListBox ItemsSource="{Binding MyView}" Grid.Row="1">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

这适用于我的testproject

答案 1 :(得分:1)

正如评论中所提到的,解决这个问题的正确方法是在视图中,而不是视图模型。这只是一个观点问题。我以为我会发布我是如何处理它的,以防万一这会帮助别人。

首先,getter(视图模型)应该每次都返回完整列表:

public ObservableCollection<CustomVariableGroup> CustomVariableGroups
{ get { return this._customVariableGroups; } }

然后,在视图中,我将过滤器事件添加到我的CollectionViewSource:

<CollectionViewSource x:Key="SortedGroups" Source="{Binding CustomVariableGroups}" Filter="CollectionViewSource_Filter">

每次点击Show Disabled复选框时都要刷新列表:

Command="{Binding RefreshVariableGroupCommand}"

然后,在后面的代码中,我实现了过滤器:

private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
{
    CustomVariableGroup customVariableGroup = e.Item as CustomVariableGroup;

    if (customVariableGroup == null) { return; }

    if ((bool)chkShowDisabled.IsChecked)
    {
        // Show everything
        e.Accepted = true;
        return;
    }

    // We are not showing disabled items, so set disabled items e.Accepted to false.
    if (customVariableGroup.Disabled == true)
    {
        e.Accepted = false;
        return;
    }

    e.Accepted = true;
}

关于这一点,我不喜欢的唯一部分是我正在使用MVVM并试图避免代码隐藏类。但是,拥有它比破解视图模型更好。

答案 2 :(得分:0)

我会维护你的支持列表,然后提高你的ObservableCollection的属性。

public ObservableCollection<CustomVariableGroup> CustomVariableGroups
{
    get
    {
        if (this.ShowDisabled) { return this._customVariableGroups; }

        return new ObservableCollection<CustomVariableGroup>  (this._customVariableGroups.Where(x => x.Disabled == false));
    }
}

// adds to the backing list but raises on property to rebuild and
// return the ObservableCollection
public void AddCustomVariableGroup(CustomVariableGroup newGroup)
{    
    this._customVariableGroups.Add(newGroup);
    OnPropertyChanged("CustomVariableGroups");
}

//Example invocation
AddCustomVariableGroup(newGroup);

答案 3 :(得分:0)

在显示过滤结果时,我遇到了与WinPhone应用程序类似的问题。我的解决方案是绑定到“ShowDisabled”属性更改,并在此事件上简单地Clear()并在这种情况下将内容读入主Observable:CustomVariableGroups。像这样:

public bool ShowDisabled
{
    get
    {
        return showDisabled;
    }
    set
    {
        if (showDisabled!= value)
        {
            showDisabled = value;
            NotifyPropertyChanged("ShowDisabled");
        }
    }
}

在构造函数中添加:this.PropertyChanged += new PropertyChangedEventHandler(MyViewModel_PropertyChanged);

在事件处理程序中:

 void MyViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
 {
     if (e.PropertyName == "ShowDisabled")
     {
         CustomVariableGroups.Clear();
         foreach (var cvg in AllVariableGroups.Where(x => !x.Disabled))
         {
             CustomVariableGroups.Add(cvg);
         }
     }
 }

绑定到CustomVariableGroups的组件应相应更新。

免责声明:当然会对此产生性能影响,您需要决定它是否可以忽略不计。

答案 4 :(得分:0)

我会做类似@Tallmaris的事情,但在MyViewModel_PropertyChanged中代替这一点:

     CustomVariableGroups.Clear();
     foreach (var cvg in CustomVariableGroups.Where(x => !x.Disabled))
     {
         CustomVariableGroups.Add(cvg);
     }

这样做:

     foreach(var cvg in CustomVariableGroups.Where( x => x.Disabled))
     {
         CustomVariableGroups.Remove(cvg);
     }

如果这不起作用(取决于Enumerable.Where使用迭代器块,我认为),你可以这样做:

     foreach(var cvg in Custom VariableGroups.Where( x=> x.Disabled).ToList())
     {
         CustomVariableGroups.Remove(cvg);
     }