自定义WPF常用控件(1)--页码控件
页码显示和翻页控件是使用相当广泛的一种控件,几乎是随处可见。
总结起来,此类控件,一般都具有页码显示、前跳、后跳、快速跳转等功能,某些控件可能包含页码特殊设置,当前页/总页数显示等。于是乎我们就有了自定义此类控件的思路。
一、控件预览
先把成品给大家看看吧:
控件外观
这个控件功能倒是有了,但是挺难看的啊?别急,配合依赖属性和属性回调我们可以实现如其他内置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>
[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>
[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>
[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;
}
}
设置每页显示条数:
/// 获取或设置每页的条数
/// </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>
[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类,可以减少声明委托的代码。
以“前页”按钮为例:
事件声明:
/// 返回上一个的事件
/// </summary>
public event EventHandler<EventArgs> GoPrev;
事件实现中,仅改变当前页数字,具体事情,由控件使用者来实现:
/// 点击"上页"按钮的事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void BtnPrev_Click(object sender, RoutedEventArgs e)
{
PageNow--;
if (GoPrev != null)
GoPrev(sender, e);
}
其他的事件原理相同,可以看源码
四、控件样式
控件样式也是很重要的,做WPF可不能忘了这个,但是我们要把样式设置暴露给控件使用者,还得能够实时改变,借助依赖属性和属性回调可以轻松实现。
样式的设置我分为了几种类型:
1、 文本提示样式,也就是“当前页”,“总页数”,“页条数”等文本的样式。
2、 文本样式,就是输入跳转页的TextBox的样式
3、 下拉框头样式
4、 下拉框Item样式
5、 按钮样式
由于代码几乎相同,我就介绍下文本提示样式。
首先是依赖属性注册:
/// 提示文本样式
/// </summary>
private static readonly DependencyProperty TipTextStyleProperty =
DependencyProperty.Register(
"TipTextStyle",
typeof(Style),
typeof(BottomBtns),
new FrameworkPropertyMetadata(new PropertyChangedCallback(TipTextStyleChangedCallback)));
然后是编写对应公有属性
/// 获取或设置文本样式
/// </summary>
[Category("外观"), Description("显示文本样式")]
public Style TextStyle
{
get { return (Style)GetValue(TextStyleProperty); }
set { SetValue(TextStyleProperty, value); }
}
再就是回调,VS的WPF/Silverlight设计器要调用此方法来实时显示效果
/// 文本提示样式属性改变回调
/// </summary>
/// <param name="d"></param>
/// <param name="e"></param>
private static void TipTextStyleChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
BottomBtns BB = ((BottomBtns)d);
BB.TxbPageNow.Style =
BB.TxbPageTotal.Style =
BB.TxbSlashTip.Style =
BB.TxbPageNowTip.Style =
BB.TxbSourceCountTip.Style =
BB.TxbSourceCount.Style =
BB.TxbPageSizeTip.Style = BB.TipTextStyle;
}