一、分頁控件功能說明
實現如上圖所示的分頁控件,需要實現一下幾個功能:
- 可以設置每頁能夠展示的最大列數(例如每頁8列、每頁16列等等)。
- 加載的數組總數量超過設置的每頁列數后,需分頁展示。
- 可以直接點擊指定的列數或者上下頁按鈕進行頁面跳轉
二、自定義分頁控件使用說明
為了實現以上功能,主要進行以下工作:
1、添加一個自定義按鈕PagerButton類,聲明一個依賴屬性IsActive,用於記錄當前頁面所在頁數的按鈕,此時該按鈕邊框高亮顯示,具體代碼如下:
public class PagerButton : Button
{
public bool IsActive
{
get { return (bool)GetValue(IsActiveProperty); }
set { SetValue(IsActiveProperty, value); }
}
public static readonly DependencyProperty IsActiveProperty =
DependencyProperty.Register("IsActive", typeof(bool), typeof(PagerButton), new PropertyMetadata(false));
}
<Style x:Key="PagerButtonStyle" TargetType="controls:PagerButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="controls:PagerButton">
<Border x:Name="b1" CornerRadius="2" Background="{StaticResource Themes}"
BorderThickness="1" BorderBrush="{StaticResource Disabled}"
Width="32" Height="32" Margin="4,0">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"></ContentPresenter>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="b1" Property="BorderBrush" Value="{StaticResource Accent}"></Setter>
</Trigger>
<Trigger Property="IsActive" Value="True">
<Setter TargetName="b1" Property="BorderBrush" Value="{StaticResource Accent}"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
</Style.Triggers>
</Style>
2、添加自定義分頁控件PagerBar類,用於展示分頁按鈕(使用PagerButton類)和實現分頁跳轉,需要定義當前頁面、總頁數、設置的每頁最大列數三個依賴屬性,具體如下:
public int CurrentPageIndex
{
get { return (int)GetValue(CurrentPageIndexProperty); }
set { SetValue(CurrentPageIndexProperty, value); }
}
public int PageSize
{
get { return (int)GetValue(PageSizeProperty); }
set { SetValue(PageSizeProperty, value); }
}
public int PageCount
{
get { return (int)GetValue(PageCountProperty); }
set { SetValue(PageCountProperty, value); }
}
public static readonly DependencyProperty CurrentPageIndexProperty = DependencyProperty.Register("CurrentPageIndex", typeof(int), typeof(PagerBar), new PropertyMetadata(-1, CurrentPageIndexPropertyChangedCallback));
public static readonly DependencyProperty PageSizeProperty = DependencyProperty.Register("PageSize", typeof(int), typeof(PagerBar), new PropertyMetadata(10, PageSizePropertyChangedCallback));
public static readonly DependencyProperty PageCountProperty = DependencyProperty.Register("PageCount", typeof(int), typeof(PagerBar), new PropertyMetadata(-1, PageCountPropertyChangedCallback));
3、添加一個工具類Pager,用於將數據按照要求分頁,該工具類聲明當前頁數、總頁數、設置的每頁最大列數三個屬性,具體如下:
public int PageSize
{
get => _pageSize;
set
{
if (value == _pageSize)
return;
_pageSize = value;
OnPropertyChanged();
SetPageSize(_pageSize);
}
}
public int PageCount
{
get => _pageCount;
set
{
if (value == _pageCount)
return;
_pageCount = value;
// 最少頁為1頁
if (_pageCount == 0)
_pageCount = 1;
OnPropertyChanged();
}
}
public int CurPageIndex
{
get => _curPageIndex;
set
{
if (value == _curPageIndex)
return;
_curPageIndex = value;
OnPropertyChanged();
GotoPageOf(_curPageIndex);
}
}
4、將Pager類和PageBar控件進行數據綁定。具體操作為:在綁定的ViewModel上聲明一個Pager類,然后將該Pager類的屬性和PagrBar進行一一對應綁定。具體代碼如下:
<controls:PagerBar Grid.Row="1" Margin="5"
HorizontalAlignment="Center"
PageSize="{Binding Path=Pager.PageSize, Mode=TwoWay}"
PageCount="{Binding Path=Pager.PageCount, Mode=TwoWay}"
CurrentPageIndex="{Binding Path=Pager.CurPageIndex, Mode=TwoWay}"></controls:PagerBar>
5、初始化Pager類時,在界面綁定的ViewModel里面重新聲明一個數組,用於保存分頁后的數組,將該數組綁定到待展示的條目控件(例如ListBox)上,具體如下:
StudentCollection=new ObservableCollection<Student>();
for (int i = 0; i < 10; i++)
{
StudentCollection.Add(new Student()
{
Id = Index = i,
Source = 10 * (i + 1),
});
}
AddCommand =new RelayCommand(ExecuteAddCommand);
DeleteCommand=new RelayCommand(ExecuteDeleteCommand);
SortCommand=new RelayCommand(ExecuteSortCommand);
Pager=new Pager<Student>(8,StudentCollection);
Pager.PagerUpdated += items =>
{
StudentCollectionPaging = new ObservableCollection<Student>(items);
};
Pager.CurPageIndex = 1;
<ListBox x:Name="ListBoxStudent" Grid.Row="0"
ItemsSource="{Binding StudentCollectionPaging}"
ItemTemplate="{StaticResource StudentDateTemplate}"></ListBox>
三、總結說明
為了實現綁定的數組添加、刪除時,PageBar分組控件也能立即更新控件,Pager工具類增加以下構造函數,首先保證數組為同一引用,其次使用ObservableCollection保證界面和ViewModel即時更新:
public Pager(int pageSize, ObservableCollection<T> source)
{
_pageSize = pageSize;
_itemsSource = source;
_itemsSource.CollectionChanged += ItemsSourceOnCollectionChanged;
CalculatePaging();
}
private void ItemsSourceOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
CalculatePaging();
GotoPageOf(CurPageIndex);
}
四、示例源碼
示例源碼地址:分頁控件源碼
五、問題
在實際應用中,發現如下問題:當需要對綁定的整個數組進行排序時,由於Pager類初始化傳入的數組必須和實際數為同一個引用且必須為ObservableCollection數組,那么使用LINQ排序完成后,必須要使用ToList進行轉換,就必須重新初始化ObservableCollection,那么后續當該ObservableCollection數組變化時,也無法通知到Pager類的數組了。為了解決該問題,想到以下兩個方法:
(1)自定義排序,針對ObservableCollection的每個數組對象進行排序。這樣排序完成后ObservableCollection引用無變化。
(2)先用臨時變量保存排序完成的數組,然后清空ObservableCollection,然后再迭代添加臨時數組。如此,ObservableCollection對象前后也為統一引用。
public ICommand SortCommand { get; set; }
private void ExecuteSortCommand(object obj)
{
var list = StudentCollection.ToList().OrderByDescending(item=>item.Source).ToList();
StudentCollection.Clear();
foreach (var item in list)
{
StudentCollection.Add(item);
}
}
目前暫時采用了第二種方法,但該方法效率不高。后續有更好的解決方法可以留言討論。