获取当前在listview WPF MVVM中显示的项目数

时间:2017-07-27 12:44:48

标签: wpf xaml listview collectionviewsource

我有一个可以使用文本框过滤的列表视图:

<TextBox TextChanged="txtFilter_TextChanged" Name="FilterLv"/>

在代码隐藏视图中,我执行以下操作:

    CollectionView view = (CollectionView)CollectionViewSource.GetDefaultView(this.lv.ItemsSource);
    view.Filter = UserFilter;

    private bool UserFilter(object item)
    {
        if (String.IsNullOrEmpty(FilterLv.Text))
            return true;
        else
        {
            DataModel m = (item as DataModel);
            bool result = (m.Name.IndexOf(Filter.Text, StringComparison.OrdinalIgnoreCase) >= 0 ||
                         //m.Surname.IndexOf(Filter.Text, StringComparison.OrdinalIgnoreCase) >= 0);

            return result;
        }
    }

    private void Filter_TextChanged(object sender, TextChangedEventArgs e)
    {
        CollectionViewSource.GetDefaultView(this.lv.ItemsSource).Refresh();
    }

现在我在视图中放置了一个标签,我希望这个标签显示当前在列表视图中显示的项目数。

我该怎么办?我找到了像this之类的东西,但我根本不了解什么是RowViewModelsCollectionView。在此链接中,建议绑定如下:

<Label Content="{Binding ModelView.RowViewModelsCollectionView.Count}"/>

有人可以解释一下,或提供一个关于如何做的非常简单的例子吗?

最终更新

查看模型

public class TestViewModel
{
// lv is populated later in code
public ObservableCollection<DataModel> lv = new ObservableCollection<DataModel>();

    public ObservableCollection<DataModel> LV
    {
        get
        {
            return this.lv;
        }

        private set
        {
            this.lv= value;
            OnPropertyChanged("LV");
        }
    }

private CollectionView view;

public TestViewModel()
{
        this.view = (CollectionView)CollectionViewSource.GetDefaultView(this.LV);
        view.Filter = UserFilter;
}

private string textFilter;
public string TextFilter
{
        get
        {
            return this.textFilter;
        }

        set
        {
            this.textFilter= value;
            OnPropertyChanged("TextFilter");

            if (String.IsNullOrEmpty(value))
                this.view.Filter = null;
            else
                this.view.Filter = UserFilter;
        }
}

private bool UserFilter(object item)
{
    if (String.IsNullOrEmpty(this.TextFilter))
        return true;
    else
    {
        DataModel m = (item as DataModel);
        bool result = (m.Name.IndexOf(this.TextFilter, StringComparison.OrdinalIgnoreCase) >= 0 ||
                     //m.Surname.IndexOf(this.TextFilter, StringComparison.OrdinalIgnoreCase) >= 0);

        return result;
    }
}



    /// <summary>
    /// Número de registros en la listview.
    /// </summary>
    public int NumberOfRecords
    {
        get
        {
            return this.view.Count;
        }
    }
}

查看(xaml)

 <!-- search textbox - filter -->
 <TextBox TextChanged="txtFilter_TextChanged"
          Text="{Binding TextFilter,  UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">

 <!-- label to show the number of records -->
<Label Content="{Binding NumberOfRecords}"/>

查看代码隐藏(xaml.cs)

    private void txtFilter_TextChanged(object sender, TextChangedEventArgs e)
    {          
        CollectionViewSource.GetDefaultView((DataContext as TestViewModel).LV).Refresh();
    }

当我输入搜索文本框并且listview正确更新但记录数始终为0时,它正在过滤。

我做错了什么?

ATTEMPT2 : 下面另一个尝试不起作用。如果我将listivew附加到模型视图中声明的视图,则不会显示任何项目。如果我在模型视图中将listview附加到LV,则显示项目,当我通过我的搜索文本框过滤它过滤好时,listview会更新,但listview中显示的行数始终保持为0.

注意:

  • 我使用的是.NET 3.5 Visual Studio 2008。
  • 我需要在模型视图中将View设置为可写,因为我没有设置它 在视图模型构造函数中,我将其设置在LoadData方法中 查看模型。从视图代码隐藏构造函数调用LoadData。

查看模型

namespace MyTest.Example
{
public Class TestViewModel : INotifyPropertyChanged // Implementations not here to simplify the code here.
{
private ObservableCollection<DataModel> lv;
public ObservableCollection<DataModel> LV
{
     get
     {
         return this.lv;
     }

     private set
     {
         this.lv = value;
         OnPropertyChanged("LV");
     }
}

public CollectionView View { get; set; }

public TestViewModel()
{
     this.LV = new ObservableCollection<DataModel>();
            // this.View = (CollectionView)CollectionViewSource.GetDefaultView(this.LV);
            // this.View.Filter = UserFilter;
}

private string textFilter = string.Empty;
public string TextFilter
{
      get
      {
           return this.textFilter ;
      }

      set
      {
           this.textFilter = value;
           OnPropertyChanged("TextFilter");

           this.View.Refresh();
      }
}

private bool UserFilter(object item)
{
    if (String.IsNullOrEmpty(this.TextFilter))
        return true;
    else
    {
        DataModel m = (item as DataModel);
        bool result = (m.Name.IndexOf(this.TextFilter, StringComparison.OrdinalIgnoreCase) >= 0 ||
                     //m.Surname.IndexOf(this.TextFilter, StringComparison.OrdinalIgnoreCase) >= 0);

        return result;
    }
}

public void LoadData()
{
    this.LV = LoadDataFromDB();
    this.View = (CollectionView)CollectionViewSource.GetDefaultView(this.LV);
    this.View.Filter = UserFilter;
}
} // End Class
} // End namespace

查看代码behing(xaml.cs)

namespace MyTest.Example
{
  public Class TestView
  {
       public TestView()
       {
            InitializeComponent();
            (DataContext as TestViewModel).LoadData();
       }       
  }
}

查看(xaml)

xmlns:vm="clr-namespace:MyTest.Example"

 <!-- search textbox - filter -->
 <TextBox Text="{Binding Path=TextFilter,  UpdateSourceTrigger=PropertyChanged}">

 <!-- label to show the number of records -->
<Label Content="{Binding Path=View.Count}" ContentStringFormat="No. Results: {0}"/>

<ListView Grid.Row="1" Grid.Column="0" ItemsSource="{Binding Path=View}" SelectionMode="Extended" AlternationCount="2">

ATTEMPT 3 : 最后我得到了它的工作。解决方案与ATTEMPT2相同,但进行以下更改:

我已经取代了这个:

public CollectionView View { get; set; }

这一个:

    private CollectionView view;
    public CollectionView View {
        get
        {
            return this.view;
        }

        private set
        {
            if (this.view == value)
            {
                return;
            }

            this.view = value;
            OnPropertyChanged("View");
        }
    }

其余所有内容与ATTEMPT2相同。在视图中View.Count并将View作为ItemsSource分配给我的listview现在完全正常工作。

3 个答案:

答案 0 :(得分:2)

你应该使用

<Label Content="{Binding ModelView.Count}"/>

而不是

<Label Content="{Binding ModelView.RowViewModelsCollectionView.Count}"/>
另一个问题中的

RowViewModelsCollectionView与您的ModelView相同。

修改

Count是来自CollectionView

的属性

有关详细信息,请查看MSDN

修改2

如果您不希望通过XAML执行此操作,就像在我的示例中一样,您必须实现INotifyPropertyChanged并在绑定属性更改时提高此值,因为其他UI未获得更改。 在您的情况下:您必须在过滤方法中调用OnPropertyChanged("NumberOfRecords");。但是像我之前写的那样通过xaml更容易实现。

答案 1 :(得分:1)

以下是视图模型中CollectionView的完整工作示例,过滤器计数自动流向绑定控件。它使用my mvvm library基础ViewModel类来提供INotifyPropertyChanged,但您应该可以轻松地替换自己的系统,我没有做任何特殊的事情。

完整的源代码可以从here

下载

application screenshot

XAML:

<Window
    x:Class="FilterWithBindableCount.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:FilterWithBindableCount"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="MainWindow"
    Width="525"
    Height="350"
    d:DataContext="{d:DesignInstance local:MainWindowVm}"
    mc:Ignorable="d">
    <Grid Margin="4">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Label
            Grid.Row="0"
            Grid.Column="0"
            Margin="4">
            Filter:
        </Label>
        <TextBox
            Grid.Row="0"
            Grid.Column="1"
            Margin="4"
            VerticalAlignment="Center"
            Text="{Binding Path=FilterText, UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock
            Grid.Row="1"
            Grid.Column="0"
            Grid.ColumnSpan="2"
            Margin="4"
            Text="{Binding Path=PeopleView.Count, StringFormat={}Count: {0}}" />
        <DataGrid
            Grid.Row="3"
            Grid.Column="0"
            Grid.ColumnSpan="2"
            Margin="4"
            CanUserAddRows="False"
            CanUserSortColumns="True"
            ItemsSource="{Binding Path=PeopleView}" />
    </Grid>
</Window>

查看模型:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Data;
using AgentOctal.WpfLib;

namespace FilterWithBindableCount
{
    class MainWindowVm : ViewModel
    {
        public MainWindowVm()
        {    
            People = new ObservableCollection<PersonVm>();
            PeopleView = (CollectionView) CollectionViewSource.GetDefaultView(People);
            PeopleView.Filter = obj =>
            {
                var person = (PersonVm)obj;
                return person.FirstName.ToUpper().Contains(FilterText.ToUpper() ) || person.LastName.ToUpper().Contains(FilterText.ToUpper());
            };

            People.Add(new PersonVm() { FirstName = "Bradley", LastName = "Uffner" });
            People.Add(new PersonVm() { FirstName = "Fred", LastName = "Flintstone" });
            People.Add(new PersonVm() { FirstName = "Arnold", LastName = "Rimmer" });
            People.Add(new PersonVm() { FirstName = "Jean-Luc", LastName = "Picard" });
            People.Add(new PersonVm() { FirstName = "Poppa", LastName = "Smurf" });    
        }
        public ObservableCollection<PersonVm> People { get; }
        public CollectionView PeopleView { get; }

        private string _filterText = "";
        public string FilterText
        {
            get => _filterText;
            set
            {
                if (SetValue(ref _filterText, value))
                {
                    PeopleView.Refresh();
                }
            }
        }    
    }

    class PersonVm:ViewModel
    {
        private string _firstName;
        public string FirstName
        {
            get {return _firstName;}
            set {SetValue(ref _firstName, value);}
        }

        private string _lastName;
        public string LastName
        {
            get {return _lastName;}
            set {SetValue(ref _lastName, value);}
        }
    }
}

答案 2 :(得分:0)

在正确使用MVVM时,这实际上要容易得多。 CollectionView或者在XAML中声明,或者作为viewmodel中的属性声明。这允许您直接绑定到CollectionView.Count

以下是如何从我的某个应用中将CollectionViewSource放入XAML的示例:

<UserControl
    x:Class="ChronoPall.App.TimeEntryList.TimeEntryListView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:app="clr-namespace:ChronoPall.App"
    xmlns:componentModel="clr-namespace:System.ComponentModel;assembly=WindowsBase"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="clr-namespace:ChronoPall.App.TimeEntryList"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DataContext="{d:DesignInstance local:TimeEntryListViewVm}"
    d:DesignHeight="300"
    d:DesignWidth="300"
    mc:Ignorable="d">
    <UserControl.Resources>
        <CollectionViewSource x:Key="TimeEntriesSource" Source="{Binding Path=TimeEntries}">
            <CollectionViewSource.SortDescriptions>
                <componentModel:SortDescription Direction="Descending" PropertyName="StartTime.Date" />
                <componentModel:SortDescription Direction="Ascending" PropertyName="StartTime" />
            </CollectionViewSource.SortDescriptions>
            <CollectionViewSource.GroupDescriptions>
                <PropertyGroupDescription PropertyName="EntryDate" />
            </CollectionViewSource.GroupDescriptions>
        </CollectionViewSource>
    </UserControl.Resources>
    <Grid IsSharedSizeScope="True">
        <ScrollViewer VerticalScrollBarVisibility="Auto">
            <ItemsControl ItemsSource="{Binding Source={StaticResource TimeEntriesSource}}">
                <ItemsControl.GroupStyle>
                    <GroupStyle>
                        <GroupStyle.HeaderTemplate>
                            <DataTemplate DataType="{x:Type CollectionViewGroup}">
                                <local:TimeEntryListDayGroup />
                            </DataTemplate>
                        </GroupStyle.HeaderTemplate>
                    </GroupStyle>
                </ItemsControl.GroupStyle>
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <local:TimeEntryListItem />
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </ScrollViewer>
    </Grid>
</UserControl>

它实际上并没有绑定到Count,但它可以很容易地用:

<TextBlock Text="{Binding Path=Count, Source={StaticResource TimeEntriesSource}}/>

要在viewmodel中执行此操作,您只需创建ICollectionView的只读属性,并将其设置为CollectionViewSource.GetDefaultView(SomeObservableCollection‌​),然后绑定到该属性。