页码显示和翻页控件是使用相当广泛的一种控件,几乎是随处可见。
总结起来,此类控件,一般都具有页码显示、前跳、后跳、快速跳转等功能,某些控件可能包含页码特殊设置,当前页/总页数显示等。于是乎我们就有了自定义此类控件的思路。
一、控件预览
先把成品给大家看看吧:

控件外观
这个控件功能倒是有了,但是挺难看的啊?别急,配合依赖属性和属性回调我们可以实现如其他内置WPF控件一样的样式设置功能,并且在VS的WPF/Silverlight设计器中实时显示。效果:

设置了样式的控件
如果嫌显示太长,不想要显示这么多内容,也可以进行设置,将指定的项隐藏起来,这些我是使用普通属性进行设置。下图是隐藏了总页数,和每页条数设置项的效果:

隐藏了部分显示项
下面说说这个控件的具体实现。实际上控件的设计很简单,将Grid分成若干份,隔一格放个控件就行了。

控件设计样式
二、控件逻辑
之后便是控件的内置逻辑关系,我设定的关系是以下几种:
1、设置当前页时,不能小于0,也不能大于总页数。
2、设置总页数时,不能小于0,也不能小于当前页。
3、如果当前页为0或者1,则“上页”按钮不可用,如果当前页等于总页数,则“下页”按钮不可用。
4、如果跳转页输入框输入了不合法的数,则“跳转”页按钮不可用;如果输入的数等于当前页或者等于总页数,则“跳转”按钮也不可用。
5、设置每页显示条数时,不能小于1,并且实时更新总页数;若重新设置每页条数后,之前的当前页页数大于新的总页数,则将当前页置1,否则保持现在的当前页页码,并加载对应页的数据。
6、若设置数据源总条数,则依据上面的规则,自动计算所有数值。
7、依据1、2规则可知:
(1)若控件是处于初始状态,则必须先于当前页设置总页数;
(2)若控件需要设为初始值,则必须在设置总页数为0之前,将当前页置为0。
我将以上规则,包含在了属性的set中。 属性设置使用了类Category,对控件属性进行分组,并使用了Description对控件进行描述。这样,在VS实时设计器中,就可以看到分组后的属性,并且有ToolTip描述,不至于所有的属性都被放到“通用”标签里面。
属性查看器
设置当前页:
/// <summary>
/// 获取或设置当前页数
/// </summary>
[Category("页码"), Description("当前显示的页码")]
public int PageNow
{
get { return m_PageNow; }
set
{
if (value >= 0)
{
if (m_PageTotal != -1)
{
if (value <= m_PageTotal)
{
TxbPageNow.Text = value.ToString();
m_PageNow = value;
//根据当前页设置 "上页" 按钮状态
if (m_PageNow > 1)
BtnPrev.IsEnabled = true;
else
BtnPrev.IsEnabled = false;
//根据当前页和总页数设置 "后页" 按钮状态
if (m_PageNow < m_PageTotal)
BtnNext.IsEnabled = true;
else
BtnNext.IsEnabled = false;
}
else
MessageBox.Show("当前页不能大于总页数", "属性设置", MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
TxbPageNow.Text = value.ToString();
m_PageNow = value;
}
}
else
PageNow = 0;
}
}
设置总页数:
/// <summary>
/// 获取或设置总页数
/// </summary>
[Category("页码"), Description("数据总页数")]
public int PageTotal
{
get { return m_PageTotal; }
private set
{
if (value >= 0)
{
if (m_PageNow != -1)
{
if (value >= m_PageNow)
{
m_PageTotal = value;
TxbPageTotal.Text = value.ToString();
//根据当前页和总页数设置 "后页" 按钮状态
if (m_PageNow < m_PageTotal)
BtnNext.IsEnabled = true;
else
BtnNext.IsEnabled = false;
}
else
MessageBox.Show("总页数不能小于当前页", "属性设置", MessageBoxButton.OK, MessageBoxImage.Error);
}
else
{
m_PageTotal = value;
TxbPageTotal.Text = value.ToString();
}
}
else
PageTotal = 0;
}
}
设置要跳转的页面:
/// <summary>
/// 获取或设置要跳转的页面
/// </summary>
[Category("页码"), Description("将要跳转的页码")]
public int PageToJump
{
get { return m_PageToJump; }
set
{
if (value >= 0)
{
if (m_PageTotal != -1)
{
if (value <= m_PageTotal)
{
m_PageToJump = value;
TxbxPage.Text = value.ToString();
//根据总页数,设置 "跳转" 按钮状态
if (m_PageToJump > 0 &&
m_PageToJump <= m_PageTotal &&
m_PageToJump != m_PageNow)
{
BtnJump.IsEnabled = true;
}
else
BtnJump.IsEnabled = false;
}
//如果输入的页数超出总页数,则禁用跳转按钮
else
BtnJump.IsEnabled = false;
}
else
{
m_PageToJump = value;
TxbxPage.Text = value.ToString();
}
}
//输入的数字小于0,禁用跳转按钮
else
PageToJump = 0;
}
}
设置每页显示条数:
View Code
/// <summary>
/// 获取或设置每页的条数
/// </summary>
[Category("页码"), Description("每页显示的条数")]
public int PageSize
{
get { return m_PageSize; }
set
{
if (value < 1)
{
MessageBox.Show("每页条数不能小于1", "属性设置", MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
m_PageSize = value;
if (m_SourceCount == 0)
PageTotal = PageNow = 0;
else
{
int PageNowTemp = m_PageNow;
PageNow = 0;
PageTotal = (int)Math.Ceiling((double)m_SourceCount / (double)m_PageSize);
if (PageNowTemp <= m_PageTotal && PageNowTemp > 0)
PageNow = PageNowTemp;
else
PageNow = 1;
}
ComboxPageSize.Text = m_PageSize.ToString();
}
}
设置数据源条数:
/// <summary>
/// 获取或设置数据源条数
/// </summary>
[Category("页码"), Description("数据源的总条数")]
public int SourceCount
{
get { return m_SourceCount; }
set
{
if (value <= 0)
m_SourceCount = PageTotal = PageNow = 0;
else
{
m_SourceCount = value;
int PageNowTemp = m_PageNow;
PageNow = 0;
PageTotal = (int)Math.Ceiling((double)m_SourceCount / (double)m_PageSize);
if (PageNowTemp <= m_PageTotal && PageNowTemp > 0)
PageNow = PageNowTemp;
else
PageNow = 1;
}
TxbSourceCount.Text = m_SourceCount.ToString();
}
}
三、控件事件
目前控件封装的事件有四个:“前页”按钮点击事件,“后页”按钮点击事件、跳转按钮点击事件、下拉框页码文本改变事件。使用的EventHandler类,可以减少声明委托的代码。
以“前页”按钮为例:
事件声明:
可不能忘了这个,但是我们要把样式设置暴露给控件使用者,还得能够实时改变,借助依赖属性和属性回调可以轻松实现。
样式的设置我分为了几种类型:
1、 文本提示样式,也就是“当前页”,“总页数”,“页条数”等文本的样式。
2、 文本样式,就是输入跳转页的TextBox的样式
3、 下拉框头样式
4、 下拉框Item样式
5、 按钮样式
由于代码几乎相同,我就介绍下文本提示样式。
首先是依赖属性注册:
然后是编写对应公有属性
再就是回调,VS的WPF/Silverlight设计器要调用此方法来实时显示效果