在最近的一个项目中,涉及到一个自定义翻页控制的控件,下面就这一个控件做详细的说明,这个自定义控件的主要作用是对数据源进行翻页控制,比如说:“上一页、下一页、首页、末页”等相关操作,由于在一个项目中有多个界面要用到这一部分,所以我们将其封装成一个自定义控件,从而使软件整体结构更加清楚明了,首先我们来将控件的界面展示出来。

整个控件可以分成三个部分,最左边是一个Combobox控件,中间是常见的上一页、下一页、向前、向后操作,最右边是显示当前记录。下面我们重点来介绍中间部分的封装,在这里我们将每一个按钮封装成一个ImageButton控件,这里我们贴出相应的前台、后台代码,从而从整体上进行分析。

<Button x:Class="CustomControlLibrary.ImageButton"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" x:Name="loc"
d:DesignHeight="44" d:DesignWidth="75">
<Button.Resources>
<ResourceDictionary Source="Themes/Generic.xaml" />
</Button.Resources>
<Image Name="innerImage" Source="{Binding ImageSource,ElementName=loc}" Stretch="None" />
</Button>

  这里前台的代码比较简单,我们将Button的样式写在了一个资源字典中,同时在Button上面放置一个Image控件,从而组成我们的ImageButton控件,在后台的代码中我们主要定义了两个依赖项属性,ImageSource和GrayImageSource,ImageSource是当按钮正常使用的时候,其显示的背景,GrayImageSource是当按钮的IsEnable=false的时候,其显示的背景,这里先贴出其后台代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes; namespace CustomControlLibrary {
/// <summary>
/// ImageButton.xaml 的交互逻辑
/// </summary>
public partial class ImageButton : Button
{
public ImageButton()
{
InitializeComponent();
this.Style = this.FindResource("ImageButtonStyle") as Style;
this.IsEnabledChanged += new DependencyPropertyChangedEventHandler(ImageButton_IsEnabledChanged);
}
void ImageButton_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (this.IsEnabled && ImageSource != null)
{
innerImage.Source = ImageSource;
} else if (!this.IsEnabled && GrayImageSource != null)
{
innerImage.Source = GrayImageSource;
}
} public ImageSource ImageSource
{
get { return (ImageSource)GetValue(ImageSourceProperty); }
set { SetValue(ImageSourceProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register("ImageSource", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata(null)); public ImageSource GrayImageSource
{
get { return (ImageSource)GetValue(GrayImageSourceProperty); }
set { SetValue(GrayImageSourceProperty, value); }
} // Using a DependencyProperty as the backing store for GrayImageSource. This enables animation, styling, binding, etc...
public static readonly DependencyProperty GrayImageSourceProperty =
DependencyProperty.Register("GrayImageSource", typeof(ImageSource), typeof(ImageButton), new UIPropertyMetadata(null)); public override void OnApplyTemplate()
{
base.OnApplyTemplate();
if (this.IsEnabled && ImageSource != null)
{
innerImage.Source = ImageSource;
} else if (!this.IsEnabled && GrayImageSource != null)
{
innerImage.Source = GrayImageSource;
}
}
}

  这里首先分析一下WPF的依赖项属性和普通的.net属性有什么不同,依赖属性是WPF平台中一个新增的概念,出现的目的是用来实现WPF中的样式、自动绑定及实现动画等特性。依赖属性的出现是WPF这种特殊的呈现原理派生出来的,与.NET普通属性不同的是,依赖属性的值是依靠多个提供程序来判断的,并且其具有内建的传递变更通知的能力。

依赖属性的定义与普通的.NET属性的定义有区别,普通的.NET属性定义只需要定义其set和get区块的赋值与设置即可,而依赖属性需要向.NET的属性系统进行注册。这里我们定义了ImageSource和GrayImageSource两个依赖项属性。另外这里还重写了基类的OnApplyTemplate方法,这个方法是FrameworkElement中定义的方法,在派生类中重写后,每当应用程序代码或内部进程调用 ApplyTemplate,都将调用此方法。FrameworkElement 派生类可以使用 OnApplyTemplate 类处理程序获知显式调用或由布局系统调用此方法的情况。OnApplyTemplate 在完全生成模板并将其附加到逻辑树之后调用。

然后就是具体的DataPager控件的实现了,这里也分别贴出相应的前台和后台的代码。

<UserControl x:Class="CustomControlLibrary.DataPager"  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:loc="clr-namespace:CustomControlLibrary"
x:Name="dp" Margin="3"
mc:Ignorable="d"
d:DesignHeight="46" d:DesignWidth="590" Loaded="DataPager_Loaded">
<Grid>
<!--<Grid.Resources>
<Style TargetType="{x:Type Image}">
<Setter Property="Margin" Value="3,0,3,0" />
<Setter Property="Cursor" Value="Hand" />
</Style>
</Grid.Resources>-->
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox Grid.Column="0" VerticalAlignment="Center" Name="cboPageSize"
MinWidth="40" Margin="5,0,0,0" ItemsSource="{Binding Path=PageSizeItems,ElementName=dp}"
SelectedItem="{Binding PageSize,Mode=TwoWay,ElementName=dp}" SelectionChanged="cbpPageSize_SelectionChanged" Visibility="Visible"/>
<StackPanel Grid.Column="1" VerticalAlignment="Center" Orientation="Horizontal" Margin="5,0,0,0">
<loc:ImageButton Click="btnFirst_Click" x:Name="btnFirst"
ImageSource="/CustomControlLibrary;component/Images/pagination_first.gif"
GrayImageSource="/CustomControlLibrary;component/Images/pagination_first_gray.gif" />
<loc:ImageButton Click="btnPrev_Click" x:Name="btnPrev"
ImageSource="/CustomControlLibrary;component/Images/pagination_prev.gif"
GrayImageSource="/CustomControlLibrary;component/Images/pagination_prev_gray.gif" />
<TextBlock Text="第页 " VerticalAlignment="Center"/>
<TextBox Width="30" Text="{Binding Path=PageIndex,ElementName=dp}" Name="tbPageIndex" PreviewKeyDown="tbPageIndex_PreviewKeyDown" LostFocus="tbPageIndex_LostFocus" />
<TextBlock Text=" 共 " VerticalAlignment="Center"/>
<TextBlock Text="{Binding Path=PageCount, ElementName=dp}" VerticalAlignment="Center"/>
<loc:ImageButton Click="btnNext_Click" x:Name="btnNext"
ImageSource="/CustomControlLibrary;component/Images/pagination_next.gif"
GrayImageSource="/CustomControlLibrary;component/Images/pagination_next_gray.gif" />
<loc:ImageButton Click="btnLast_Click" x:Name="btnLast"
ImageSource="/CustomControlLibrary;component/Images/pagination_last.gif"
GrayImageSource="/CustomControlLibrary;component/Images/pagination_last_gray.gif" />
<!--<loc:ImageButton Click="btnRefresh_Click"
ImageSource="/CustomControlLibrary;component/Images/pagination_load.png" Visibility="Hidden"/>-->
</StackPanel>
<TextBlock Grid.Column="2" VerticalAlignment="Center" Margin="5,0,5,0" >显示记录
<TextBlock Text="{Binding Path=Start,ElementName=dp}" /> 到 <TextBlock Text="{Binding Path=End,ElementName=dp}"/> 共 <TextBlock Text="{Binding Path=Total,ElementName=dp}"/> 条记录
</TextBlock>
</Grid>
</UserControl>

  这里重点介绍后台的逻辑代码。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel; namespace CustomControlLibrary {
/// <summary>
/// DataPager.xaml 的交互逻辑
/// </summary>
public partial class DataPager : UserControl, INotifyPropertyChanged
{
public DataPager()
{
InitializeComponent();
} #region 依赖属性和事件
public string PageSizeList
{
get { return (string)GetValue(PageSizeListProperty); }
set { SetValue(PageSizeListProperty, value); }
} // Using a DependencyProperty as the backing store for PageSizeList. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PageSizeListProperty =
DependencyProperty.Register("PageSizeList", typeof(string), typeof(DataPager), new UIPropertyMetadata("5,10,20", (s, e) =>
{
DataPager dp = s as DataPager;
if (dp.PageSizeItems == null)
dp.PageSizeItems = new List<int>();
else
dp.PageSizeItems.Clear();
dp.RaisePropertyChanged("PageSizeItems");
})); public int PageSize
{
get { return (int)GetValue(PageSizeProperty); }
set { SetValue(PageSizeProperty, value); }
} // Using a DependencyProperty as the backing store for PageSize. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PageSizeProperty =
DependencyProperty.Register("PageSize", typeof(int), typeof(DataPager), new UIPropertyMetadata(16)); public int Total
{
get { return (int)GetValue(TotalProperty); }
set { SetValue(TotalProperty, value); }
} // Using a DependencyProperty as the backing store for Total. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TotalProperty =
DependencyProperty.Register("Total", typeof(int), typeof(DataPager), new UIPropertyMetadata(0)); public int PageIndex
{
get { return (int)GetValue(PageIndexProperty); }
set { SetValue(PageIndexProperty, value); }
} // Using a DependencyProperty as the backing store for PageIndex. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PageIndexProperty =
DependencyProperty.Register("PageIndex", typeof(int), typeof(DataPager), new UIPropertyMetadata(1)); public IEnumerable<object> ItemsSource
{
get { return (IEnumerable<object>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
} /// <summary>
/// ItemsSource数据源
/// </summary>
public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable<object>), typeof(DataPager), new UIPropertyMetadata(null)); public static readonly RoutedEvent PageChangedEvent = EventManager.RegisterRoutedEvent("PageChanged", RoutingStrategy.Bubble, typeof(PageChangedEventHandler), typeof(DataPager));
/// <summary>
/// 分页更改事件
/// </summary>
public event PageChangedEventHandler PageChanged
{
add
{
AddHandler(PageChangedEvent, value);
}
remove
{
RemoveHandler(PageChangedEvent, value);
}
}
#endregion #region 通知属性
private List<int> _pageSizeItems;
/// <summary>
/// 显示每页记录数集合
/// </summary>
public List<int> PageSizeItems
{
get
{
if (_pageSizeItems == null)
{
_pageSizeItems = new List<int>();
}
if (PageSizeList != null)
{
List<string> strs = PageSizeList.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
_pageSizeItems.Clear();
strs.ForEach(c => {
_pageSizeItems.Add(Convert.ToInt32(c));
});
}
return _pageSizeItems;
}
set
{
if (_pageSizeItems != value)
{
_pageSizeItems = value;
RaisePropertyChanged("PageSizeItems");
}
}
} private int _pageCount;
/// <summary>
/// 总页数
/// </summary>
public int PageCount
{
get { return _pageCount; }
set
{
if (_pageCount != value)
{
_pageCount = value;
RaisePropertyChanged("PageCount");
}
}
} private int _start;
/// <summary>
/// 开始记录数
/// </summary>
public int Start
{
get { return _start; }
set
{
if (_start != value)
{
_start = value;
RaisePropertyChanged("Start");
}
}
} private int _end;
/// <summary>
/// 结束记录数
/// </summary>
public int End
{
get { return _end; }
set
{
if (_end != value)
{
_end = value;
RaisePropertyChanged("End");
}
}
} public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion #region 字段、属性、委托
public delegate void PageChangedEventHandler(object sender, PageChangedEventArgs args);
private PageChangedEventArgs pageChangedEventArgs;
#endregion #region 引发分页更改事件
/// <summary>
/// 引发分页更改事件
/// </summary>
private void RaisePageChanged()
{
if (pageChangedEventArgs == null)
{
pageChangedEventArgs = new PageChangedEventArgs(PageChangedEvent, PageSize, PageIndex);
}
else
{
pageChangedEventArgs.PageSize = this.PageSize;
pageChangedEventArgs.PageIndex = this.PageIndex;
}
RaiseEvent(pageChangedEventArgs);
//calc start、end
if (ItemsSource != null)
{
int curCount = ItemsSource.Count();
Start = (PageIndex - 1) * PageSize + 1;
End = Start + curCount - 1; if (Total % PageSize != 0)
{
PageCount = Total / PageSize + 1;
}
else
{
PageCount = Total / PageSize;
}
}
else
{
Start = End = PageCount = Total = 0;
}
//调整图片的显示
btnFirst.IsEnabled = btnPrev.IsEnabled = (PageIndex != 1);
btnNext.IsEnabled = btnLast.IsEnabled = (PageIndex != PageCount);
}
#endregion #region 分页操作事件
void DataPager_Loaded(object sender, RoutedEventArgs e)
{
RaisePageChanged();
} private void cbpPageSize_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this.IsLoaded)
{
PageSize = (int)cboPageSize.SelectedItem;
RaisePageChanged();
}
} private void btnFirst_Click(object sender, RoutedEventArgs e)
{
PageIndex = 1;
RaisePageChanged();
} private void btnPrev_Click(object sender, RoutedEventArgs e)
{
if (PageIndex > 1)
{
--PageIndex;
}
RaisePageChanged();
} private void btnNext_Click(object sender, RoutedEventArgs e)
{
if (Total % PageSize != 0)
{
PageCount = Total / PageSize + 1;
}
else
{
PageCount = Total / PageSize;
} if (PageIndex < PageCount)
{
++PageIndex;
}
RaisePageChanged();
} private void btnLast_Click(object sender, RoutedEventArgs e)
{
if (Total % PageSize != 0)
{
PageCount = Total / PageSize + 1;
}
else
{
PageCount = Total / PageSize;
}
PageIndex = PageCount;
RaisePageChanged();
} private void btnRefresh_Click(object sender, RoutedEventArgs e)
{
RaisePageChanged();
} private void tbPageIndex_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
tbPageIndex_LostFocus(sender, null);
}
} private void tbPageIndex_LostFocus(object sender, RoutedEventArgs e)
{
int pIndex = 0;
try
{
pIndex = Convert.ToInt32(tbPageIndex.Text);
} catch { pIndex = 1; } if (pIndex < 1) PageIndex = 1;
else if (pIndex > PageCount) PageIndex = PageCount;
else PageIndex = pIndex; RaisePageChanged();
}
#endregion } /// <summary>
/// 分页更改参数
/// </summary>
public class PageChangedEventArgs : RoutedEventArgs
{
public int PageSize { get; set; }
public int PageIndex { get; set; } public PageChangedEventArgs(RoutedEvent routeEvent, int pageSize, int pageIndex)
: base(routeEvent)
{
this.PageSize = pageSize;
this.PageIndex = pageIndex;
}
}
}

  这里定义了一些该控件要使用的一些属性,PageSize属性用于定义每一页中的项的数量,Total属性定义总共有多少项,PageIndex定义当前的页码,ItemsSource用于定义数据源的集合,类型是IEnumerable<object>类型,这个里面定义的最重要的函数是RaisePageChanged,该函数将引发分页更改事件,在这个函数中我们将当前定义的路由事件PageChangedEvent,以及每一页项目的大小,以及当前页码传递到PageChangedEventArgs的一个对象中,然后调用RaiseEvent来引发特定的路由事件,这里需要注意的是必须将当前定义的路由事件传递到基类RoutedEventArgs中,否则不能触发该路由事件。

自定义控件DataPager的更多相关文章

  1. android自定义控件一站式入门

    自定义控件 Android系统提供了一系列UI相关的类来帮助我们构造app的界面,以及完成交互的处理. 一般的,所有可以在窗口中被展示的UI对象类型,最终都是继承自View的类,这包括展示最终内容的非 ...

  2. ASP.NET MVC学习之母版页和自定义控件的使用

    一.母板页_Layout.cshtml类似于传统WebForm中的.master文件,起到页面整体框架重用的目地1.母板页代码预览 <!DOCTYPE html> <html> ...

  3. C# 自定义控件VS用户控件

    1 自定义控件与用户控件区别 WinForm中, 用户控件(User Control):继承自 UserControl,主要用于开发 Container 控件,Container控件可以添加其他Con ...

  4. 自定义控件之 圆形 / 圆角 ImageView

    一.问题在哪里? 问题来源于app开发中一个很常见的场景——用户头像要展示成圆的:       二.怎么搞? 机智的我,第一想法就是,切一张中间圆形透明.四周与底色相同.尺寸与头像相同的蒙板图片,盖在 ...

  5. 如何开发FineReport的自定义控件?

    FineReport作为插件化开发的报表软件,有些特殊需求的功能需要自己开发,开发的插件包帆软官方有提提供,可以去帆软论坛上找,本文将主要介绍如何开发一个自定义控件,这里讲讲方法论. 第一步:实例化一 ...

  6. WPF自定义控件第二 - 转盘按钮控件

    继之前那个控件,又做了一个原理差不多的控件.这个控件主要模仿百度贴吧WP版帖子浏览界面左下角那个弹出的按钮盘.希望对大家有帮助. 这个控件和之前的也差不多,为了不让大家白看,文章最后发干货. 由于这个 ...

  7. 【Win 10应用开发】AdaptiveTrigger在自定义控件中是可以触发的

    前些天,看到有网友给我留言,说AdaptiveTrigger在自定义控件(模板化控件)中不能触发.因为当时我正在写其他的代码,就没有去做实验来验证,于是我就给这位网友提了使用GotoVisualSta ...

  8. WPF自定义控件与样式(3)-TextBox & RichTextBox & PasswordBox样式、水印、Label标签、功能扩展

    一.前言.预览 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要是对文本 ...

  9. Android自定义控件之自定义ViewGroup实现标签云

    前言: 前面几篇讲了自定义控件绘制原理Android自定义控件之基本原理(一),自定义属性Android自定义控件之自定义属性(二),自定义组合控件Android自定义控件之自定义组合控件(三),常言 ...

随机推荐

  1. Redis漏洞,远程攻击

    文章转自http://blog.csdn.net/whs_321/article/details/51734602 http://blog.knownsec.com/2015/11/analysis- ...

  2. Fedora安装Snapd和Snap软件包

    导读 Snappy包管理器是一个跨发行版的包管理器.它最初是为Ubuntu系统构建的,但现在其他主要的Linux发行版( Fedora, Linux Mint, RHEL, OpenSUSE,Arch ...

  3. 栈(stack)信息

    栈在JVM虚拟机中是线程的一块私有空间,比如存储函数的调用信息.局部变量等 特性 先进后出和后进先出即FIFO 借用网络的一个图,感觉看完就可以了解了 最先调用的函数压入栈低,最后压入得函数在栈顶,函 ...

  4. 一键搭建LNMP脚本

    还有不足的地方,请谅解   2天时间刚做到安装mysql这里.... # [root@localhost ~]# cat /etc/centos-release # CentOS release 6. ...

  5. CF11D A Simple Task 状压DP

    传送门 \(N \leq 19\)-- 不难想到一个状压:设\(f_{i,j,k}\)表示开头为\(i\).结尾为\(j\).经过的点数二进制下为\(k\)的简单路总数,贡献答案就看\(i,j\)之间 ...

  6. Luogu4606 SDOI2018 战略游戏 圆方树、虚树、链并

    传送门 弱化版 考虑到去掉一个点使得存在两个点不连通的形式类似割点,不难想到建立圆方树.那么在圆方树上对于给出的关键点建立虚树之后,我们需要求的就是虚树路径上所有圆点的数量减去关键点的数量. 因为没有 ...

  7. CF1097D Makoto and a Blackboard 积性函数、概率期望、DP

    传送门 比赛秒写完ABC结果不会D--最后C还fst了qwq 首先可以想到一个约数个数\(^2\)乘上\(K\)的暴力DP,但是显然会被卡 在\(10^{15}\)范围内因数最多的数是\(978217 ...

  8. PV原语操作详解

    from http://www.blogjava.net/wxqxs/archive/2009/05/10/277320.html PV原语通过操作信号量来处理进程间的同步与互斥的问题.其核心就是一段 ...

  9. WordPress更新提示无法创建目录的解决方案

    上一篇我们说到无法连接FTP服务器,我们已经完美的解决了,然后...发现...还是无法更新,啥情况??? 提示为无法创建目录 原因是执行更新程序的是www用户, 解决方案如下: 需要把插件或主程序下载 ...

  10. GeForce Experience关闭自动更新

    GeForce Experience驱动更新很烦,而且有时更新后就打不开了,找到种方法关闭更新 1.安装并登陆 2.打开 C:\ProgramData\NVIDIA Corporation 3.进入D ...