WPF (IronPython) – Xây dựng Filter trên DataGrid bằng CollectionViewSource
Bằng cách sử dụng 1 wrapper trung gian CollectionViewSource, chúng ta có thể dễ dàng xây dựng các chức năng sort, filter, group trên DataGrid Control. Bài viết này sẽ hướng dẫn xây dựng filter cơ bản thông qua ví dụ sau:
Yêu cầu: Có 1 datagrid show ra dữ liệu về sách (tựa sách, số trang). Khi nhập vào textbox phía trên và nhấn button Tìm thì datagrid sẽ được filter tựa sách theo nội dung vừa nhập vào (hình minh họa bên dưới)
Book model:
class Book: def __init__(self, title, pageCount): self.Title = title self.PageCount = pageCount
Viewmodel:
class MyViewModel(BaseModel): def __init__(self): super(MyViewModel, self).__init__() self.Books = [ Book('C++ The Programming Language', 630), Book('C# The Programming Language', 380), Book('Objective-C The Programming Language', 350), Book('Beginning Office Calculation', 125), Book('Design Pattern', 121), Book('Đường lối Đảng', 2000) ] self._filterValue = '' self.FilterValue = 'Program' @notify_property def FilterValue(self): return self._filterValue @FilterValue.setter def FilterValue(self, value): self._filterValue = value print 'filtervalue changed to %r' % self.FilterValue
View.CodeBehind:
class MyWindow(Window): def __init__(self): wpf.LoadComponent(self, 'TestGrid.xaml') self.ViewModel = MyViewModel() self.DataContext = self.ViewModel def CollectionViewSource_Filter(self, sender, e): book = e.Item book.__class__ = Book if self.ViewModel.FilterValue.Length < 0: e.Accepted = 1 elif self.ViewModel.FilterValue in book.Title : e.Accepted = 1 else: e.Accepted = 0 def ButtonSearch_Click(self, sender, e): CollectionViewSource.GetDefaultView(self.grdBooks.ItemsSource).Refresh();
View.XAML:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="TestGrid" Height="220" Width="300"> <Window.Resources> <CollectionViewSource Source="{Binding Books}" x:Key="cvsBooks" Filter="CollectionViewSource_Filter"/> </Window.Resources> <StackPanel> <StackPanel Orientation="Horizontal" Margin="5"> <TextBox Width="230" > <TextBox.Text> <Binding Path="FilterValue" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay" /> </TextBox.Text> </TextBox> <Button Content="Tìm" Width="50" Click="ButtonSearch_Click"/> </StackPanel> <DataGrid Name="grdBooks" ItemsSource="{Binding Source={StaticResource cvsBooks}}" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Header="Tựa sách" Binding="{Binding Title}" Width="220"/> <DataGridTextColumn Header="Số trang" Binding="{Binding PageCount}" Width="*" /> </DataGrid.Columns> </DataGrid> </StackPanel> </Window>
Cơ chế Observable và lớp cơ sở hỗ trợ Notify Properties
class notify_property(property): def __init__(self, getter): def newgetter(slf): try: return getter(slf) except AttributeError: return None super(notify_property, self).__init__(newgetter) def setter(self, setter): def newsetter(slf, newvalue): oldvalue = self.fget(slf) if oldvalue != newvalue: setter(slf, newvalue) slf.OnPropertyChanged(setter.__name__) return property( fget=self.fget, fset=newsetter, fdel=self.fdel, doc=self.__doc__) class BaseModel(INotifyPropertyChanged): PropertyChanged = None def __init__(self): self.PropertyChanged, self._propertyChangedCaller = pyevent.make_event() def add_PropertyChanged(self, value): self.PropertyChanged += value def remove_PropertyChanged(self, value): self.PropertyChanged -= value def OnPropertyChanged(self, propertyName): if self.PropertyChanged is not None: self._propertyChangedCaller(self, PropertyChangedEventArgs(propertyName))