我有一个可以使用文本框过滤的列表视图:
<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.
注意:
查看模型:
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现在完全正常工作。
答案 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
下载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)
,然后绑定到该属性。