// COPYRIGHT (C) Tom. ALL RIGHTS RESERVED.
// THE AntdUI PROJECT IS AN WINFORM LIBRARY LICENSED UNDER THE Apache-2.0 License.
// LICENSED UNDER THE Apache License, VERSION 2.0 (THE "License")
// YOU MAY NOT USE THIS FILE EXCEPT IN COMPLIANCE WITH THE License.
// YOU MAY OBTAIN A COPY OF THE LICENSE AT
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, SOFTWARE
// DISTRIBUTED UNDER THE LICENSE IS DISTRIBUTED ON AN "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
// SEE THE LICENSE FOR THE SPECIFIC LANGUAGE GOVERNING PERMISSIONS AND
// LIMITATIONS UNDER THE License.
// GITEE: https://gitee.com/antdui/AntdUI
// GITHUB: https://github.com/AntdUI/AntdUI
// CSDN: https://blog.csdn.net/v_132
// QQ: 17379620
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Design;
using System.Windows.Forms;
namespace AntdUI
{
///
/// Pagination 分页
///
/// 采用分页的形式分隔长列表,每次只加载一个页面。
[Description("Pagination 分页")]
[ToolboxItem(true)]
[DefaultProperty("Current")]
[DefaultEvent("ValueChanged")]
public class Pagination : IControl
{
#region 属性
int current = 1;
///
/// 当前页数
///
[Description("当前页数"), Category("数据"), DefaultValue(1)]
public int Current
{
get => current;
set
{
if (value < 1) value = 1;
else if (value > PageTotal) value = PageTotal;
if (current == value) return;
current = value;
ValueChanged?.Invoke(this, new PagePageEventArgs(current, total, pageSize, PageTotal));
ButtonLayout();
Invalidate();
}
}
int total = 0;
///
/// 数据总数
///
[Description("数据总数"), Category("数据"), DefaultValue(0)]
public int Total
{
get => total;
set
{
if (total == value) return;
total = value;
ButtonLayout();
Invalidate();
}
}
int pageSize = 10;
///
/// 每页条数
///
[Description("每页条数"), Category("数据"), DefaultValue(10)]
public int PageSize
{
get => pageSize;
set
{
if (pageSize == value) return;
pageSize = value;
if (Math.Ceiling(total * 1.0 / pageSize) < current) current = (int)Math.Ceiling(total * 1.0 / pageSize);
ValueChanged?.Invoke(this, new PagePageEventArgs(current, total, pageSize, PageTotal));
if (input_SizeChanger != null)
{
string tips = Localization.Provider?.GetLocalizedString("ItemsPerPage") ?? "条/页";
input_SizeChanger.Clear();
input_SizeChanger.PlaceholderText = value.ToString() + " " + tips;
}
ButtonLayout();
Invalidate();
}
}
///
/// 最大显示总页数
///
[Description("最大显示总页数"), Category("行为"), DefaultValue(0)]
public int MaxPageTotal { get; set; } = 0;
///
/// 总页数
///
[Description("总页数"), Category("数据")]
public int PageTotal { get; private set; } = 1;
int _gap = 8;
///
/// 间距
///
[Description("间距"), Category("外观"), DefaultValue(8)]
public int Gap
{
get => _gap;
set
{
if (_gap == value) return;
_gap = value;
ButtonLayout();
Invalidate();
}
}
///
/// Value 属性值更改时发生
///
[Description("Value 属性值更改时发生"), Category("行为")]
public event PageValueEventHandler? ValueChanged;
///
/// 显示数据总量
///
[Description("用于显示数据总量"), Category("行为")]
public event PageValueRtEventHandler? ShowTotalChanged;
bool showSizeChanger = false;
///
/// 是否展示 PageSize 切换器
///
[Description("是否展示 PageSize 切换器"), Category("行为"), DefaultValue(false)]
public bool ShowSizeChanger
{
get => showSizeChanger;
set
{
if (showSizeChanger == value) return;
showSizeChanger = value;
if (!value) InputSizeChangerDispose();
ButtonLayout();
Invalidate();
}
}
int[]? pageSizeOptions = null;
///
/// 指定每页可以显示多少条
///
[Description("指定每页可以显示多少条"), Category("行为"), DefaultValue(null)]
public int[]? PageSizeOptions
{
get => pageSizeOptions;
set
{
if (pageSizeOptions == value) return;
pageSizeOptions = value;
InputSizeChangerDispose();
if (showSizeChanger)
{
ButtonLayout();
Invalidate();
}
}
}
int sizeChangerWidth = 0;
[Description("SizeChanger 宽度"), Category("行为"), DefaultValue(0)]
public int SizeChangerWidth
{
get => sizeChangerWidth;
set
{
if (sizeChangerWidth == value) return;
sizeChangerWidth = value;
if (showSizeChanger)
{
InputSizeChangerDispose();
ButtonLayout();
Invalidate();
}
}
}
int pyr = 0;
public override Rectangle DisplayRectangle
{
get => ClientRectangle.PaddingRect(Padding, 0, 0, pyr, 0, borderWidth * Config.Dpi);
}
Color? fill;
///
/// 颜色
///
[Description("颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? Fill
{
get => fill;
set
{
fill = value;
if (input_SizeChanger != null) input_SizeChanger.BorderColor = value;
Invalidate();
}
}
int radius = 6;
///
/// 圆角
///
[Description("圆角"), Category("外观"), DefaultValue(6)]
public int Radius
{
get => radius;
set
{
if (radius == value) return;
radius = value;
Invalidate();
}
}
float borderWidth = 1F;
///
/// 边框宽度
///
[Description("边框宽度"), Category("边框"), DefaultValue(1F)]
public float BorderWidth
{
get => borderWidth;
set
{
if (borderWidth == value) return;
borderWidth = value;
Invalidate();
}
}
RightToLeft rightToLeft = RightToLeft.No;
[Description("反向"), Category("外观"), DefaultValue(RightToLeft.No)]
public override RightToLeft RightToLeft
{
get => rightToLeft;
set
{
if (rightToLeft == value) return;
rightToLeft = value;
ButtonLayout();
Invalidate();
}
}
string? textdesc;
[Description("主动显示内容"), Category("外观"), DefaultValue(null)]
public string? TextDesc
{
get => textdesc;
set
{
if (textdesc == value) return;
textdesc = value;
ButtonLayout();
Invalidate();
}
}
#endregion
#region 渲染
readonly StringFormat s_f = Helper.SF_NoWrap();
protected override void OnPaint(PaintEventArgs e)
{
if (buttons.Length < 2) return;
var g = e.Graphics.High();
float border = borderWidth * Config.Dpi, _radius = radius * Config.Dpi;
if (Enabled)
{
Color fore = Style.Db.Text, color = fill ?? Style.Db.Primary;
using (var brush_hover = new SolidBrush(Style.Db.FillSecondary))
{
#region 渲染上下
var btn_previous = buttons[0];
if (btn_previous.hover)
{
using (var path_previous = btn_previous.rect.RoundPath(_radius))
{
g.FillPath(brush_hover, path_previous);
}
}
using (var pen_arrow = new Pen(btn_previous.enabled ? fore : Style.Db.TextQuaternary, border))
{
g.DrawLines(pen_arrow, TAlignMini.Left.TriangleLines(btn_previous.rect));
}
var btn_next = buttons[1];
if (btn_next.hover)
{
using (var path_next = btn_next.rect.RoundPath(_radius))
{
g.FillPath(brush_hover, path_next);
}
}
using (var pen_arrow = new Pen(btn_next.enabled ? fore : Style.Db.TextQuaternary, border))
{
g.DrawLines(pen_arrow, TAlignMini.Right.TriangleLines(btn_next.rect));
}
#endregion
using (var brush = new SolidBrush(fore))
{
if (showTotal != null) g.DrawStr(showTotal, Font, brush, rect_text, s_f);
for (int i = 2; i < buttons.Length; i++)
{
var btn = buttons[i];
if (btn.hover)
{
using (var path = btn.rect.RoundPath(_radius))
{
g.FillPath(brush_hover, path);
}
}
if (btn.prog > 0)
{
using (var brush_prog = new SolidBrush(Style.Db.TextQuaternary))
{
g.DrawStr("•••", Font, brush_prog, btn.rect, s_f);
}
}
else
{
if (current == btn.num)
{
using (var path = btn.rect.RoundPath(_radius))
{
using (var pen = new Pen(color, border))
{
g.DrawPath(pen, path);
}
}
}
g.DrawStr(btn.key, Font, brush, btn.rect, s_f);
}
}
}
}
}
else
{
#region 渲染上下
var btn_previous = buttons[0];
using (var pen_arrow = new Pen(Style.Db.TextQuaternary, border))
{
g.DrawLines(pen_arrow, TAlignMini.Left.TriangleLines(btn_previous.rect));
}
var btn_next = buttons[1];
using (var pen_arrow = new Pen(Style.Db.TextQuaternary, border))
{
g.DrawLines(pen_arrow, TAlignMini.Right.TriangleLines(btn_next.rect));
}
#endregion
using (var brush = new SolidBrush(Style.Db.TextQuaternary))
{
if (showTotal != null) g.DrawStr(showTotal, Font, brush, rect_text, s_f);
for (int i = 2; i < buttons.Length; i++)
{
var btn = buttons[i];
if (btn.prog > 0)
{
g.DrawStr("•••", Font, brush, btn.rect, s_f);
}
else
{
if (current == btn.num)
{
using (var path = btn.rect.RoundPath(_radius))
{
using (var brush_2 = new SolidBrush(Style.Db.Fill))
{
g.FillPath(brush_2, path);
}
}
}
g.DrawStr(btn.key, Font, brush, btn.rect, s_f);
}
}
}
}
}
#endregion
#region 鼠标
protected override void OnMouseMove(MouseEventArgs e)
{
if (buttons.Length > 0)
{
int count = 0, count_no = 0, count_hand = 0;
for (int i = 0; i < buttons.Length; i++)
{
var btn = buttons[i];
var hover = false;
if (btn.enabled) hover = btn.rect.Contains(e.Location);
else if ((i == 0 || i == 1) && btn.rect.Contains(e.Location))
{
count_no++;
}
if (btn.hover != hover)
{
btn.hover = hover;
count++;
}
if (btn.hover) count_hand++;
}
if (count_no > 0) SetCursor(CursorType.No);
else SetCursor(count_hand > 0);
if (count > 0) Invalidate();
}
base.OnMouseMove(e);
}
protected override void OnMouseLeave(EventArgs e)
{
SetCursor(false);
if (buttons.Length > 0)
{
int count = 0;
foreach (var btn in buttons)
{
if (btn.hover)
{
btn.hover = false;
count++;
}
}
if (count > 0) Invalidate();
}
base.OnMouseLeave(e);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (buttons.Length > 0)
{
for (int i = 0; i < buttons.Length; i++)
{
var btn = buttons[i];
if (btn.enabled && btn.rect.Contains(e.Location))
{
if (i == 0) Current = current - 1;
else if (i == 1) Current = current + 1;
else if (btn.prog > 0)
{
if (btn.prog == 2) Current = current - 5;
else Current = current + 5;
}
else Current = btn.num;
Focus();
return;
}
}
}
base.OnMouseDown(e);
}
#endregion
#region 布局
protected override void OnSizeChanged(EventArgs e)
{
ButtonLayout();
base.OnSizeChanged(e);
}
ButtonLoad[] buttons = new ButtonLoad[0];
internal string? showTotal = null;
internal Rectangle rect_text;
Input? input_SizeChanger = null;
void InputSizeChangerDispose()
{
if (InvokeRequired)
{
Invoke(new Action(InputSizeChangerDispose));
return;
}
input_SizeChanger?.Dispose();
input_SizeChanger = null;
}
void ButtonLayout()
{
var rect = ClientRectangle.PaddingRect(Padding, borderWidth * Config.Dpi);
if (showSizeChanger)
{
int wsize = (int)(4 * Config.Dpi);
rect.Y += wsize;
rect.Height -= wsize * 2;
}
bool sizeChanger = ShowSizeChanger;
int t_Width = rect.Width, _SizeChangerWidth = 0;
if (t_Width > 1)
{
if (sizeChanger)
{
_SizeChangerWidth = InitSizeChanger(rect);
t_Width -= _SizeChangerWidth;
}
int gap = (int)(_gap * Config.Dpi);
int total_button = t_Width / (rect.Height + gap);//总共多少按钮
if (total_button < 3)
{
buttons = new ButtonLoad[0];
return;
}
int total_page = (int)Math.Ceiling((total * 1.0) / pageSize);//总页数
if (total_page == 0) total_page = 1;
if (textdesc == null) showTotal = ShowTotalChanged?.Invoke(this, new PagePageEventArgs(current, total, pageSize, total_page));
else showTotal = textdesc;
int pyrn = Helper.GDI(g =>
{
var dir = new Dictionary();
int min = 100, max_size = rect.Height;
for (int i = 0; i <= total_page; i++)
{
if (i == min)
{
min = min * 10;
var size_font = g.MeasureString((i + 1).ToString(), Font).Size();
if (size_font.Width > rect.Height)
{
max_size = size_font.Width;
dir.Add(i.ToString().Length, size_font.Width);
}
}
}
int x = 0;
if (showTotal != null)
{
var size_font = g.MeasureString(showTotal, Font);
x = (int)Math.Ceiling(size_font.Width);
rect_text = new Rectangle(rect.X, rect.Y, x, rect.Height);
}
total_button = (int)Math.Floor((t_Width - x) / ((max_size * 1.0) + gap));//总共多少按钮
if (total_button < 3)
{
buttons = new ButtonLoad[0];
return 0;
}
if (MaxPageTotal > 0 && total_button > MaxPageTotal + 2) total_button = MaxPageTotal + 2;
int total_page_button = total_button - 2;
PageTotal = total_page;
bool has_previous = current > 1, has_next = total_page > current;//是否还有下一页
var _buttons = new List(total_button) {
new ButtonLoad(new Rectangle(x + rect.X, rect.Y, rect.Height, rect.Height),has_previous)
};
int n_x = _buttons[0].rect.Right + gap;
if (total_page > total_page_button)
{
//大于
int _tol = total_page_button;
int center_page_button = (int)Math.Ceiling(total_page_button / 2F);
if (current <= center_page_button)
{
for (int i = 0; i < _tol - 2; i++) n_x += AddButs(ref _buttons, dir, new ButtonLoad(i + 1, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true)) + gap;
n_x += AddButs(ref _buttons, dir, new ButtonLoad(_tol - 2, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true, 1)) + gap;
//添加最后一页
n_x += AddButs(ref _buttons, dir, new ButtonLoad(total_page, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true));
}
else if (current > total_page - center_page_button)
{
n_x += AddButs(ref _buttons, dir, new ButtonLoad(1, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true)) + gap;
int last_i = total_page - (_tol - 3);
n_x += AddButs(ref _buttons, dir, new ButtonLoad(last_i, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true, 2)) + gap;
for (int i = 0; i < _tol - 2; i++) n_x += AddButs(ref _buttons, dir, new ButtonLoad(last_i + i, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true)) + gap;
}
else
{
int last_i = total_page - (_tol - 3);
#region 前
n_x += AddButs(ref _buttons, dir, new ButtonLoad(1, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true)) + gap;
n_x += AddButs(ref _buttons, dir, new ButtonLoad(1, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true, 2)) + gap;
#endregion
int center_len = (_tol - 4);
var start = current - center_len / 2;
for (int i = 0; i < center_len; i++) n_x += AddButs(ref _buttons, dir, new ButtonLoad(start + i, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true)) + gap;
#region 后
n_x += AddButs(ref _buttons, dir, new ButtonLoad(last_i, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true, 1)) + gap;
n_x += AddButs(ref _buttons, dir, new ButtonLoad(total_page, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true));
#endregion
}
}
else
{
//不够
for (int i = 0; i < total_page; i++) n_x += AddButs(ref _buttons, dir, new ButtonLoad(i + 1, new Rectangle(n_x, rect.Y, rect.Height, rect.Height), true)) + gap;
}
_buttons.Insert(1, new ButtonLoad(new Rectangle(_buttons[_buttons.Count - 1].rect.Right + gap, rect.Y, rect.Height, rect.Height), has_next));
int valr = 0;
if (RightToLeft == RightToLeft.Yes)
{
var py = rect.Right - _buttons[1].rect.Right;
if (sizeChanger) py -= _SizeChangerWidth;
foreach (var btn in _buttons) btn._rect.Offset(py, 0);
rect_text.Offset(py, 0);
}
else if (sizeChanger) valr = rect.Right - _buttons[1].rect.Right - _SizeChangerWidth;
buttons = _buttons.ToArray();
return valr;
});
if (pyr == pyrn) return;
pyr = pyrn;
if (InvokeRequired) Invoke(new Action(() => { OnSizeChanged(EventArgs.Empty); }));
else OnSizeChanged(EventArgs.Empty);
}
}
int InitSizeChanger(Rectangle rect)
{
if (input_SizeChanger == null)
{
string tips = Localization.Provider?.GetLocalizedString("ItemsPerPage") ?? "条/页";
var placeholder = pageSize.ToString() + " " + tips;
bool r = rightToLeft == RightToLeft.Yes;
int width = GetSizeChangerWidth(placeholder);
if (pageSizeOptions == null || pageSizeOptions.Length == 0)
{
var input = new Input
{
PlaceholderText = placeholder,
Size = new Size(width, rect.Height),
Dock = DockStyle.Right,
Font = Font,
BorderColor = fill
};
input_SizeChanger = input;
}
else
{
var input = new Select
{
PlaceholderText = placeholder,
ListAutoWidth = true,
DropDownArrow = true,
Placement = TAlignFrom.Top,
Size = new Size(width, rect.Height),
Dock = DockStyle.Right,
Font = Font,
BorderColor = fill
};
foreach (var it in pageSizeOptions) input.Items.Add(it);
input.SelectedValue = pageSize;
input.Text = "";
input.SelectedValueChanged += (a, b) =>
{
if (b.Value is int pageSize) PageSize = pageSize;
};
input_SizeChanger = input;
}
if (InvokeRequired)
{
Invoke(new Action(() =>
{
Controls.Add(input_SizeChanger);
}));
}
else Controls.Add(input_SizeChanger);
input_SizeChanger.KeyPress += Input_SizeChanger_KeyPress;
return width;
}
else
{
if (sizeChangerWidth <= 0)
{
string tips = Localization.Provider?.GetLocalizedString("ItemsPerPage") ?? "条/页";
var placeholder = pageSize.ToString() + " " + tips;
int width = GetSizeChangerWidth(placeholder);
if (InvokeRequired)
{
Invoke(new Action(() =>
{
input_SizeChanger.Width = width;
}));
}
else input_SizeChanger.Width = width;
return width;
}
return input_SizeChanger.Width;
}
}
int GetSizeChangerWidth(string placeholder)
{
if (sizeChangerWidth > 0) return (int)(sizeChangerWidth * Config.Dpi);
int wsize = (int)(4 * Config.Dpi) * 2;
if (pageSizeOptions == null || pageSizeOptions.Length == 0)
{
return Helper.GDI(g =>
{
var size = g.MeasureString(placeholder, Font).Size();
return size.Width + wsize + (int)Math.Ceiling(size.Height * 0.6F);
});
}
else
{
return Helper.GDI(g =>
{
var size = g.MeasureString(placeholder, Font).Size();
return size.Width + wsize + (int)Math.Ceiling(size.Height * 1.32F);
});
}
}
protected override void OnFontChanged(EventArgs e)
{
base.OnForeColorChanged(e);
if (input_SizeChanger != null) input_SizeChanger.Font = Font;
}
int AddButs(ref List buttons, Dictionary dir, ButtonLoad button)
{
if (button.key.Length > 1)
{
if (dir.TryGetValue(button.key.Length, out var it))
{
button._rect.Width = it;
}
}
buttons.Add(button);
return button.rect.Width;
}
private void Input_SizeChanger_KeyPress(object? sender, KeyPressEventArgs e)
{
if (e.KeyChar == 13 && sender is Input input)
{
e.Handled = true;
if (int.TryParse(input.Text, out var num)) PageSize = num;
}
}
class ButtonLoad
{
public ButtonLoad(Rectangle _rect, bool enable)
{
rect = _rect;
key = "";
enabled = enable;
}
public ButtonLoad(int _num, Rectangle _rect, bool enable)
{
num = _num;
rect = _rect;
key = _num.ToString();
enabled = enable;
}
public ButtonLoad(int _num, Rectangle _rect, bool enable, int p)
{
num = _num;
rect = _rect;
key = _num.ToString();
enabled = enable;
prog = p;
}
internal Rectangle _rect;
public Rectangle rect
{
get => _rect;
set => _rect = value;
}
public string key { get; set; }
public int num { get; set; }
public bool enabled { get; set; }
public int prog { get; set; }
public bool hover { get; set; }
}
#endregion
public bool ProcessCmdKey(Keys keyData)
{
if (keyData == Keys.Left)
{
var val = current - 1;
if (val < 1 || val > PageTotal) return false;
Current = val;
return true;
}
else if (keyData == Keys.Right)
{
var val = current + 1;
if (val < 1 || val > PageTotal) return false;
Current = val;
return true;
}
return false;
}
}
}