// 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
{
///
/// Tabs 标签页
///
/// 选项卡切换组件。
[Description("Tabs 标签页")]
[ToolboxItem(true)]
[DefaultEvent("SelectedIndexChanged")]
[DefaultProperty("Pages")]
[Designer(typeof(TabControlDesigner))]
public partial class Tabs : IControl
{
public Tabs() { style = SetType(type); }
#region 属性
Color? fore;
///
/// 文字颜色
///
[Description("文字颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public new Color? ForeColor
{
get => fore;
set
{
if (fore == value) fore = value;
fore = value;
Invalidate();
}
}
Color? fill;
///
/// 颜色
///
[Description("颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? Fill
{
get => fill;
set
{
if (fill == value) return;
fill = value;
Invalidate();
}
}
///
/// 悬停颜色
///
[Description("悬停颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? FillHover { get; set; }
///
/// 激活颜色
///
[Description("激活颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? FillActive { get; set; }
TabAlignment alignment = TabAlignment.Top;
///
/// 位置
///
[Description("位置"), Category("外观"), DefaultValue(TabAlignment.Top)]
public TabAlignment Alignment
{
get => alignment;
set
{
if (alignment == value) return;
alignment = value;
LoadLayout();
}
}
bool centered = false;
///
/// 标签居中展示
///
[Description("标签居中展示"), Category("外观"), DefaultValue(false)]
public bool Centered
{
get => centered;
set
{
if (centered == value) return;
centered = value;
LoadLayout();
}
}
TabTypExceed typExceed = TabTypExceed.Button;
///
/// 超出UI类型
///
[Description("超出UI类型"), Category("外观"), DefaultValue(TabTypExceed.Button)]
public TabTypExceed TypExceed
{
get => typExceed;
set
{
if (typExceed == value) return;
typExceed = value;
bitblock_l?.Dispose();
bitblock_r?.Dispose();
bitblock_l = bitblock_r = null;
LoadLayout();
}
}
Color? scrollback;
///
/// 滚动条颜色
///
[Description("滚动条颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? ScrollBack
{
get => scrollback;
set
{
if (scrollback == value) return;
scrollback = value;
bitblock_l?.Dispose();
bitblock_r?.Dispose();
bitblock_l = bitblock_r = null;
Invalidate();
}
}
Color? scrollfore;
///
/// 滚动条文本颜色
///
[Description("滚动条文本颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? ScrollFore
{
get => scrollfore;
set
{
if (scrollfore == value) return;
scrollfore = value;
Invalidate();
}
}
///
/// 滚动条悬停文本颜色
///
[Description("滚动条悬停文本颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? ScrollForeHover { get; set; }
///
/// 滚动条悬停颜色
///
[Description("滚动条悬停颜色"), Category("外观"), DefaultValue(null)]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? ScrollBackHover { get; set; }
#region 样式
IStyle style;
///
/// 样式
///
[Description("样式"), Category("外观")]
public IStyle Style
{
get => style;
set => style = value;
}
TabType type = TabType.Line;
///
/// 样式类型
///
[Description("样式类型"), Category("外观"), DefaultValue(TabType.Line)]
public TabType Type
{
get => type;
set
{
if (type == value) return;
type = value;
style = SetType(value);
LoadLayout();
}
}
IStyle SetType(TabType type)
{
switch (type)
{
case TabType.Card:
if (style is StyleCard stylec) return stylec;
return new StyleCard(this);
case TabType.Card2:
if (style is StyleCard2 stylec2) return stylec2;
return new StyleCard2(this);
case TabType.Line:
default:
if (style is StyleLine stylel) return stylel;
return new StyleLine(this);
}
}
#endregion
int _gap = 8;
///
/// 间距
///
[Description("间距"), Category("外观"), DefaultValue(8)]
public int Gap
{
get => _gap;
set
{
if (_gap == value) return;
_gap = value;
LoadLayout();
}
}
float iconratio = .7F;
///
/// 图标比例
///
[Description("图标比例"), Category("外观"), DefaultValue(.7F)]
public float IconRatio
{
get => iconratio;
set
{
if (iconratio == value) return;
iconratio = value;
LoadLayout();
}
}
bool _tabMenuVisible = true;
[Description("是否显示头"), Category("外观"), DefaultValue(true)]
public bool TabMenuVisible
{
get => _tabMenuVisible;
set
{
_tabMenuVisible = value;
LoadLayout();
}
}
int? _itemSize = null;
///
/// 自定义项宽度
///
[Description("自定义项大小"), Category("外观"), DefaultValue(null)]
public int? ItemSize
{
get => _itemSize;
set
{
if (_itemSize == value) return;
_itemSize = value;
LoadLayout();
}
}
internal Dictionary HandItemSize(Dictionary rect_dir, ref int sizewh)
{
if (_itemSize.HasValue)
{
int Size = (int)Math.Ceiling(_itemSize.Value * Config.Dpi);
var rect_dirtmp = new Dictionary(rect_dir.Count);
foreach (var it in rect_dir) rect_dirtmp.Add(it.Key, new Size(Size, it.Value.Height));
if (alignment == TabAlignment.Left || alignment == TabAlignment.Right) sizewh = Size;
return rect_dirtmp;
}
return rect_dir;
}
public override Rectangle DisplayRectangle
{
get => ClientRectangle.PaddingRect(Margin, Padding, _padding);
}
#region 数据
TabCollection? items;
///
/// 数据
///
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
[Description("集合"), Category("数据")]
public TabCollection Pages
{
get
{
items ??= new TabCollection(this);
return items;
}
set => items = value.BindData(this);
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public TabPage? SelectedTab
{
get
{
if (items == null || items.Count <= _select || _select < 0) return null;
return items[_select];
}
set
{
if (items == null || value == null) return;
var index = items.IndexOf(value);
SelectedIndex = index;
}
}
public void SelectTab(string tabPageName)
{
if (items == null) return;
foreach (var it in items)
{
if (it.Text == tabPageName)
{
SelectedTab = it;
return;
}
}
}
public void SelectTab(TabPage tabPage)
{
SelectedTab = tabPage;
}
public void SelectTab(int index)
{
SelectedIndex = index;
}
#region 动画
int _select = 0;
[Description("选中序号"), Category("数据"), DefaultValue(0)]
public int SelectedIndex
{
get => _select;
set
{
if (_select == value) return;
int old = _select;
_select = value;
style.SelectedIndexChanged(value, old);
SelectedIndexChanged?.Invoke(this, new IntEventArgs(value));
Invalidate();
ShowPage();
}
}
internal void ShowPage()
{
if (showok)
{
BeginInvoke(new Action(() =>
{
Controls.Clear();
if (items == null) return;
if (items.Count <= _select || _select < 0) return;
var item = items[_select];
item.DpiAuto();
Controls.Add(item);
}));
}
}
[Browsable(false)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public new ControlCollection Controls => base.Controls;
protected override void Dispose(bool disposing)
{
style.Dispose();
base.Dispose(disposing);
}
#endregion
#endregion
#endregion
#region 布局
bool showok = false;
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
LoadLayout(false);
showok = true;
ShowPage();
}
protected override void OnMarginChanged(EventArgs e)
{
LoadLayout(true);
base.OnMarginChanged(e);
}
protected override void OnFontChanged(EventArgs e)
{
LoadLayout(false);
base.OnFontChanged(e);
}
protected override void OnSizeChanged(EventArgs e)
{
LoadLayout(false);
base.OnSizeChanged(e);
}
Padding _padding = new Padding(0);
bool SetPadding(int x, int y, int r, int b)
{
if (_padding.Left == x && _padding.Top == y && _padding.Right == r && _padding.Bottom == b) return true;
_padding.Left = x;
_padding.Top = y;
_padding.Right = r;
_padding.Bottom = b;
base.OnSizeChanged(EventArgs.Empty);
return false;
}
internal void LoadLayout(bool r = true)
{
if (IsHandleCreated)
{
if (items == null) return;
if (_tabMenuVisible)
{
var rect = ClientRectangle;
if (rect.Width > 0 && rect.Height > 0)
{
var rect_t = rect.DeflateRect(Margin);
style.LoadLayout(this, rect_t, items);
if (r) Invalidate();
}
}
else SetPadding(0, 0, 0, 0);
}
}
#endregion
#region 渲染
readonly StringFormat s_c = Helper.SF_ALL(), s_f = Helper.SF_NoWrap();
protected override void OnPaint(PaintEventArgs e)
{
if (items == null || items.Count == 0 || !_tabMenuVisible) return;
var g = e.Graphics.High();
style.Paint(this, g, items);
base.OnPaint(e);
}
void PaintBadge(Graphics g, TabPage page, Rectangle rect)
{
if (page.Badge != null)
{
var color = page.BadgeBack ?? AntdUI.Style.Db.Error;
using (var brush_fore = new SolidBrush(AntdUI.Style.Db.ErrorColor))
{
float borsize = 1F * Config.Dpi;
using (var font = new Font(Font.FontFamily, Font.Size * page.BadgeSize))
{
if (string.IsNullOrEmpty(page.Badge) || page.Badge == "" || page.Badge == " ")
{
var size = (int)Math.Floor(g.MeasureString(Config.NullText, font).Width / 2);
var rect_badge = new RectangleF(rect.Right - size - page.BadgeOffsetX * Config.Dpi, rect.Y + page.BadgeOffsetY * Config.Dpi, size, size);
using (var brush = new SolidBrush(color))
{
g.FillEllipse(brush, rect_badge);
using (var pen = new Pen(brush_fore.Color, borsize))
{
g.DrawEllipse(pen, rect_badge);
}
}
}
else
{
var size = g.MeasureString(page.Badge, font);
var size_badge = size.Height * 1.2F;
if (size.Height > size.Width)
{
var rect_badge = new RectangleF(rect.Right - size_badge - page.BadgeOffsetX * Config.Dpi, rect.Y + page.BadgeOffsetY * Config.Dpi, size_badge, size_badge);
using (var brush = new SolidBrush(color))
{
g.FillEllipse(brush, rect_badge);
using (var pen = new Pen(brush_fore.Color, borsize))
{
g.DrawEllipse(pen, rect_badge);
}
}
g.DrawStr(page.Badge, font, brush_fore, rect_badge, s_f);
}
else
{
var w_badge = size.Width * 1.2F;
var rect_badge = new RectangleF(rect.Right - w_badge - page.BadgeOffsetX * Config.Dpi, rect.Y + page.BadgeOffsetY * Config.Dpi, w_badge, size_badge);
using (var brush = new SolidBrush(color))
{
using (var path = rect_badge.RoundPath(rect_badge.Height))
{
g.FillPath(brush, path);
using (var pen = new Pen(brush_fore.Color, borsize))
{
g.DrawPath(pen, path);
}
}
}
g.DrawStr(page.Badge, font, brush_fore, rect_badge, s_f);
}
}
}
}
}
}
#endregion
#region 鼠标
protected override void OnMouseDown(MouseEventArgs e)
{
if (items == null)
{
base.OnMouseDown(e);
return;
}
if (MouseDownPre(e.X, e.Y)) return;
if (_tabMenuVisible)
{
int i = 0, x = e.X + scroll_x, y = e.Y + scroll_y;
foreach (var item in items)
{
if (item.Visible && item.Contains(x, y))
{
item.MDown = true;
Invalidate();
return;
}
i++;
}
}
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (items == null) { base.OnMouseUp(e); return; }
if (_tabMenuVisible)
{
int i = 0, x = e.X + scroll_x, y = e.Y + scroll_y;
foreach (var item in items)
{
if (item.MDown)
{
item.MDown = false;
if (item.Contains(x, y))
{
if (style.MouseClick(item, i, x, y)) return;
SelectedIndex = i;
}
else Invalidate();
return;
}
i++;
}
}
base.OnMouseUp(e);
}
int hover_i = -1;
int Hover_i
{
get => hover_i;
set
{
if (hover_i == value) return;
hover_i = value;
Invalidate();
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (items == null) return;
if (MouseMovePre(e.X, e.Y))
{
Hover_i = -1;
SetCursor(true);
base.OnMouseMove(e);
return;
}
int i = 0, x = e.X + scroll_x, y = e.Y + scroll_y;
foreach (var item in items)
{
if (item.Visible && item.Contains(x, y))
{
SetCursor(true);
Hover_i = i;
style.MouseMove(x, y);
return;
}
i++;
}
style.MouseMove(x, y);
SetCursor(false);
base.OnMouseMove(e);
}
protected override void OnMouseLeave(EventArgs e)
{
style.MouseLeave();
Hover_l = Hover_r = false;
Hover_i = -1;
SetCursor(false);
base.OnMouseLeave(e);
}
protected override void OnMouseWheel(MouseEventArgs e)
{
style.MouseWheel(e.Delta);
base.OnMouseWheel(e);
}
#endregion
#region 滚动条
internal bool scroll_show = false;
int _scroll_x = 0, _scroll_y = 0, scroll_max = 0;
int scroll_x
{
get => _scroll_x;
set
{
if (value < 0) value = 0;
else if (scroll_max > 0 && value > scroll_max) value = scroll_max;
if (value == _scroll_x) return;
_scroll_x = value;
Invalidate();
}
}
int scroll_y
{
get => _scroll_y;
set
{
if (value < 0) value = 0;
else if (scroll_max > 0 && value > scroll_max) value = scroll_max;
if (value == _scroll_y) return;
_scroll_y = value;
Invalidate();
}
}
#region 超出
LayeredFormSelectDown? subForm = null;
bool MouseMovePre(int x, int y)
{
if (items == null) return false;
switch (typExceed)
{
case TabTypExceed.Button:
if (scroll_show && rect_r.Contains(x, y))
{
if (subForm == null)
{
var objs = new List(items.Count);
foreach (var item in items) objs.Add(new SelectItem(item.Text, item));
subForm = new LayeredFormSelectDown(this, 6, objs.ToArray(), SelectedTab, rect_r);
subForm.Disposed += (a, b) =>
{
subForm = null;
};
subForm.MouseEnter += (a, b) =>
{
if (a is LayeredFormSelectDown form) form.tag1 = false;
};
subForm.MouseLeave += (a, b) =>
{
if (a is LayeredFormSelectDown form) form.IClose();
};
subForm.Leave += (a, b) =>
{
if (a is LayeredFormSelectDown form) form.IClose();
};
subForm.Show(this);
}
return true;
}
else
{
subForm?.IClose();
subForm = null;
}
break;
case TabTypExceed.LR:
case TabTypExceed.LR_Shadow:
if (scroll_show)
{
if (MouseDownLRL(x, y, out bool lr))
{
Hover_l = true;
return true;
}
else Hover_l = false;
if (MouseDownLRR(x, y, out _))
{
Hover_r = true;
return true;
}
else Hover_r = false;
}
break;
}
return false;
}
bool MouseDownPre(int x, int y)
{
if (items == null) return false;
if (scroll_show && (typExceed == TabTypExceed.LR || typExceed == TabTypExceed.LR_Shadow))
{
if (MouseDownLRL(x, y, out bool lr))
{
if (lr) scroll_y -= 120;
else scroll_x -= 120;
return true;
}
else if (MouseDownLRR(x, y, out bool lr2))
{
if (lr2) scroll_y += 120;
else scroll_x += 120;
return true;
}
}
return false;
}
bool MouseDownLRL(int x, int y, out bool lr)
{
switch (alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
lr = true;
if (scroll_y > 0 && rect_l.Contains(x, y)) return true;
return false;
case TabAlignment.Top:
case TabAlignment.Bottom:
default:
lr = false;
if (scroll_x > 0 && rect_l.Contains(x, y)) return true;
return false;
}
}
bool MouseDownLRR(int x, int y, out bool lr)
{
switch (alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
lr = true;
if (scroll_max != scroll_y && rect_r.Contains(x, y)) return true;
return false;
case TabAlignment.Top:
case TabAlignment.Bottom:
default:
lr = false;
if (scroll_max != scroll_x && rect_r.Contains(x, y)) return true;
return false;
}
}
#region 绘制超出部分
public virtual int SizeExceed(Rectangle rect, Rectangle first, Rectangle last)
{
switch (typExceed)
{
case TabTypExceed.Button:
int size = last.Height;
switch (alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
rect_r = new Rectangle(last.X, rect.Bottom - size, last.Width, size);
break;
case TabAlignment.Top:
case TabAlignment.Bottom:
default:
rect_r = new Rectangle(rect.Right - size, last.Y, size, size);
break;
}
return size;
case TabTypExceed.None:
default:
rect_r = Rectangle.Empty;
return 0;
}
}
public virtual Rectangle PaintExceedPre(Rectangle rect, int size)
{
switch (typExceed)
{
case TabTypExceed.Button:
switch (alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
if (scroll_max != scroll_y) return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height - size);
else return rect;
case TabAlignment.Top:
case TabAlignment.Bottom:
default:
if (scroll_max != scroll_x) return new Rectangle(rect.X, rect.Y, rect.Width - size, rect.Height);
else return rect;
}
case TabTypExceed.LR:
case TabTypExceed.LR_Shadow:
switch (alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
int sizeLR2 = (int)(size * .6F);
if (scroll_max != scroll_y)
{
if (scroll_y > 0) return new Rectangle(rect.X, rect.Y + sizeLR2, rect.Width, rect.Height - sizeLR2 * 2);
else return new Rectangle(rect.X, rect.Y, rect.Width, rect.Height - sizeLR2);
}
else if (scroll_y > 0) return new Rectangle(rect.X, rect.Y + sizeLR2, rect.Width, rect.Height - sizeLR2);
else return rect;
case TabAlignment.Top:
case TabAlignment.Bottom:
default:
int sizeLR = (int)(size * .6F);
if (scroll_max != scroll_x)
{
if (scroll_x > 0) return new Rectangle(rect.X + sizeLR, rect.Y, rect.Width - sizeLR * 2, rect.Height);
else return new Rectangle(rect.X, rect.Y, rect.Width - sizeLR, rect.Height);
}
else if (scroll_x > 0) return new Rectangle(rect.X + sizeLR, rect.Y, rect.Width - sizeLR, rect.Height);
else return rect;
}
case TabTypExceed.None:
default:
return rect;
}
}
Bitmap? bitblock_l = null, bitblock_r = null;
public virtual void PaintExceed(Graphics g, Color color, int radius, Rectangle rect, Rectangle first, Rectangle last, bool full)
{
switch (typExceed)
{
case TabTypExceed.Button:
PaintExceedButton(g, color, radius, rect, first, last, full);
break;
case TabTypExceed.LR:
PaintExceedLR(g, color, radius, rect, first, last, full);
break;
case TabTypExceed.LR_Shadow:
PaintExceedLR_Shadow(g, color, radius, rect, first, last, full);
break;
case TabTypExceed.None:
default:
break;
}
}
public virtual void PaintExceedButton(Graphics g, Color color, int radius, Rectangle rect, Rectangle first, Rectangle last, bool full)
{
g.ResetClip();
g.ResetTransform();
switch (alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
if (scroll_y > 0 || scroll_max != scroll_y)
{
int gap = (int)(_gap * Config.Dpi), gap2 = gap * 2;
int size = last.Height, icosize = (int)(size * 0.4F);
var rect_cr = new Rectangle(last.X, rect.Bottom - size, last.Width, size);
if (scroll_y > 0)
{
var rect_l = new Rectangle(first.X, first.Y, rect_cr.Width, gap2);
if (full) rect_l.Y = 0;
if (bitblock_l == null || bitblock_l.Width != rect_l.Width || bitblock_l.Height != rect_l.Height)
{
bitblock_l?.Dispose();
bitblock_l = new Bitmap(rect_l.Width, rect_l.Height);
using (var g_bmp = Graphics.FromImage(bitblock_l).HighLay())
{
using (var brush = new SolidBrush(color))
{
using (var path = new Rectangle(0, 0, bitblock_l.Width, gap).RoundPath(gap, false, false, true, true))
{
g_bmp.FillPath(brush, path);
}
}
}
Helper.Blur(bitblock_l, gap);
}
g.DrawImage(bitblock_l, rect_l, .1F);
}
if (scroll_max != scroll_y)
{
var rect_r = new Rectangle(rect_cr.X, rect_cr.Y - gap2, rect_cr.Width, gap2);
if (bitblock_r == null || bitblock_r.Width != rect_r.Width || bitblock_r.Height != rect_r.Height)
{
bitblock_r?.Dispose();
bitblock_r = new Bitmap(rect_r.Width, rect_r.Height);
using (var g_bmp = Graphics.FromImage(bitblock_r).HighLay())
{
using (var brush = new SolidBrush(color))
{
using (var path = new Rectangle(0, gap, bitblock_r.Width, gap).RoundPath(gap, true, true, false, false))
{
g_bmp.FillPath(brush, path);
}
}
}
Helper.Blur(bitblock_r, gap);
}
g.DrawImage(bitblock_r, rect_r, .1F);
}
var rect_ico = new Rectangle(rect_cr.X + (rect_cr.Width - icosize) / 2, rect_cr.Y + (rect_cr.Height - icosize) / 2, icosize, icosize);
g.GetImgExtend(SvgDb.IcoMore, rect_ico, color);
}
break;
case TabAlignment.Top:
case TabAlignment.Bottom:
default:
if (scroll_x > 0 || scroll_max != scroll_x)
{
int gap = (int)(_gap * Config.Dpi), gap2 = gap * 2;
int size = last.Height, icosize = (int)(size * 0.4F);
var rect_cr = new Rectangle(rect.Right - size, last.Y, size, size);
if (scroll_x > 0)
{
var rect_l = new Rectangle(first.X, first.Y, gap2, size);
if (full) rect_l.X = 0;
if (bitblock_l == null || bitblock_l.Width != rect_l.Width || bitblock_l.Height != rect_l.Height)
{
bitblock_l?.Dispose();
bitblock_l = new Bitmap(rect_l.Width, rect_l.Height);
using (var g_bmp = Graphics.FromImage(bitblock_l).HighLay())
{
using (var brush = new SolidBrush(color))
{
using (var path = new Rectangle(0, 0, gap, bitblock_l.Height).RoundPath(gap, false, true, true, false))
{
g_bmp.FillPath(brush, path);
}
}
}
Helper.Blur(bitblock_l, gap);
}
g.DrawImage(bitblock_l, rect_l, .1F);
}
if (scroll_max != scroll_x)
{
var rect_r = new Rectangle(rect_cr.X - gap2, rect_cr.Y, gap2, size);
if (bitblock_r == null || bitblock_r.Width != rect_r.Width || bitblock_r.Height != rect_r.Height)
{
bitblock_r?.Dispose();
bitblock_r = new Bitmap(rect_r.Width, rect_r.Height);
using (var g_bmp = Graphics.FromImage(bitblock_r).HighLay())
{
using (var brush = new SolidBrush(color))
{
using (var path = new Rectangle(gap, 0, gap, bitblock_r.Height).RoundPath(gap, true, false, false, true))
{
g_bmp.FillPath(brush, path);
}
}
}
Helper.Blur(bitblock_r, gap);
}
g.DrawImage(bitblock_r, rect_r, .1F);
}
var rect_ico = new Rectangle(rect_cr.X + (rect_cr.Width - icosize) / 2, rect_cr.Y + (rect_cr.Height - icosize) / 2, icosize, icosize);
g.GetImgExtend(SvgDb.IcoMore, rect_ico, color);
}
break;
}
}
public virtual void PaintExceedLR(Graphics g, Color color, int radius, Rectangle rect, Rectangle first, Rectangle last, bool full)
{
g.ResetClip();
g.ResetTransform();
switch (alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
if (scroll_y > 0 || scroll_max != scroll_y)
{
int size = (int)(last.Height * .6F);
using (var brush = new SolidBrush(scrollback ?? AntdUI.Style.Db.FillSecondary))
using (var brush_hover = new SolidBrush(ScrollBackHover ?? AntdUI.Style.Db.Primary))
using (var pen = new Pen(scrollfore ?? color, 2F * Config.Dpi))
using (var pen_hover = new Pen(ScrollForeHover ?? AntdUI.Style.Db.PrimaryColor, 2F * Config.Dpi))
{
if (scroll_y > 0)
{
rect_l = new Rectangle(last.X, rect.Y, last.Width, size);
using (var path = Helper.RoundPath(rect_l, radius))
{
if (hover_l)
{
g.FillPath(brush_hover, path);
g.DrawLines(pen_hover, TAlignMini.Top.TriangleLines(rect_l, .5F));
}
else
{
g.FillPath(brush, path);
g.DrawLines(pen, TAlignMini.Top.TriangleLines(rect_l, .5F));
}
}
}
if (scroll_max != scroll_y)
{
rect_r = new Rectangle(last.X, rect.Y + rect.Height - size, last.Width, size);
using (var path = Helper.RoundPath(rect_r, radius))
{
if (hover_r)
{
g.FillPath(brush_hover, path);
g.DrawLines(pen_hover, TAlignMini.Bottom.TriangleLines(rect_r, .5F));
}
else
{
g.FillPath(brush, path);
g.DrawLines(pen, TAlignMini.Bottom.TriangleLines(rect_r, .5F));
}
}
}
}
}
break;
case TabAlignment.Top:
case TabAlignment.Bottom:
default:
if (scroll_x > 0 || scroll_max != scroll_x)
{
int size = (int)(last.Height * .6F);
using (var brush = new SolidBrush(scrollback ?? AntdUI.Style.Db.FillSecondary))
using (var brush_hover = new SolidBrush(ScrollBackHover ?? AntdUI.Style.Db.Primary))
using (var pen = new Pen(scrollfore ?? color, 2F * Config.Dpi))
using (var pen_hover = new Pen(ScrollForeHover ?? AntdUI.Style.Db.PrimaryColor, 2F * Config.Dpi))
{
if (scroll_x > 0)
{
rect_l = new Rectangle(rect.X, last.Y, size, last.Height);
using (var path = Helper.RoundPath(rect_l, radius))
{
if (hover_l)
{
g.FillPath(brush_hover, path);
g.DrawLines(pen_hover, TAlignMini.Left.TriangleLines(rect_l, .5F));
}
else
{
g.FillPath(brush, path);
g.DrawLines(pen, TAlignMini.Left.TriangleLines(rect_l, .5F));
}
}
}
if (scroll_max != scroll_x)
{
rect_r = new Rectangle(rect.X + rect.Width - size, last.Y, size, last.Height);
using (var path = Helper.RoundPath(rect_r, radius))
{
if (hover_r)
{
g.FillPath(brush_hover, path);
g.DrawLines(pen_hover, TAlignMini.Right.TriangleLines(rect_r, .5F));
}
else
{
g.FillPath(brush, path);
g.DrawLines(pen, TAlignMini.Right.TriangleLines(rect_r, .5F));
}
}
}
}
}
break;
}
}
public virtual void PaintExceedLR_Shadow(Graphics g, Color color, int radius, Rectangle rect, Rectangle first, Rectangle last, bool full)
{
g.ResetClip();
g.ResetTransform();
switch (alignment)
{
case TabAlignment.Left:
case TabAlignment.Right:
if (scroll_y > 0 || scroll_max != scroll_y)
{
int gap = (int)(_gap * Config.Dpi), gap2 = gap * 2, size = (int)(last.Height * .6F);
using (var brush = new SolidBrush(scrollback ?? AntdUI.Style.Db.FillSecondary))
using (var brush_hover = new SolidBrush(ScrollBackHover ?? AntdUI.Style.Db.Primary))
using (var pen = new Pen(scrollfore ?? color, 2F * Config.Dpi))
using (var pen_hover = new Pen(ScrollForeHover ?? AntdUI.Style.Db.PrimaryColor, 2F * Config.Dpi))
{
if (scroll_y > 0)
{
rect_l = new Rectangle(last.X, rect.Y, last.Width, size);
var Rect_l = new Rectangle(rect_l.X, rect_l.Bottom, rect_l.Width, gap2);
if (bitblock_l == null || bitblock_l.Width != Rect_l.Width || bitblock_l.Height != Rect_l.Height)
{
bitblock_l?.Dispose();
bitblock_l = new Bitmap(Rect_l.Width, Rect_l.Height);
using (var g_bmp = Graphics.FromImage(bitblock_l).HighLay())
{
using (var path = new Rectangle(0, 0, bitblock_l.Width, gap).RoundPath(gap, false, false, true, true))
{
g_bmp.FillPath(brush, path);
}
}
Helper.Blur(bitblock_l, gap);
}
g.DrawImage(bitblock_l, Rect_l, .1F);
using (var path = Helper.RoundPath(rect_l, radius, true, true, false, false))
{
if (hover_l)
{
g.FillPath(brush_hover, path);
g.DrawLines(pen_hover, TAlignMini.Top.TriangleLines(rect_l, .5F));
}
else
{
g.FillPath(brush, path);
g.DrawLines(pen, TAlignMini.Top.TriangleLines(rect_l, .5F));
}
}
}
if (scroll_max != scroll_y)
{
rect_r = new Rectangle(last.X, rect.Y + rect.Height - size, last.Width, size);
var Rect_r = new Rectangle(rect_r.X, rect_r.Y - gap2, rect_r.Width, gap2);
if (bitblock_r == null || bitblock_r.Width != Rect_r.Width || bitblock_r.Height != Rect_r.Height)
{
bitblock_r?.Dispose();
bitblock_r = new Bitmap(Rect_r.Width, Rect_r.Height);
using (var g_bmp = Graphics.FromImage(bitblock_r).HighLay())
{
using (var path = new Rectangle(0, gap, bitblock_r.Width, gap).RoundPath(gap, true, true, false, false))
{
g_bmp.FillPath(brush, path);
}
}
Helper.Blur(bitblock_r, gap);
}
g.DrawImage(bitblock_r, Rect_r, .1F);
using (var path = Helper.RoundPath(rect_r, radius, false, false, true, true))
{
if (hover_r)
{
g.FillPath(brush_hover, path);
g.DrawLines(pen_hover, TAlignMini.Bottom.TriangleLines(rect_r, .5F));
}
else
{
g.FillPath(brush, path);
g.DrawLines(pen, TAlignMini.Bottom.TriangleLines(rect_r, .5F));
}
}
}
}
}
break;
case TabAlignment.Top:
case TabAlignment.Bottom:
default:
if (scroll_x > 0 || scroll_max != scroll_x)
{
int gap = (int)(_gap * Config.Dpi), gap2 = gap * 2, size = (int)(last.Height * .6F);
using (var brush = new SolidBrush(scrollback ?? AntdUI.Style.Db.FillSecondary))
using (var brush_hover = new SolidBrush(ScrollBackHover ?? AntdUI.Style.Db.Primary))
using (var pen = new Pen(scrollfore ?? color, 2F * Config.Dpi))
using (var pen_hover = new Pen(ScrollForeHover ?? AntdUI.Style.Db.PrimaryColor, 2F * Config.Dpi))
{
if (scroll_x > 0)
{
rect_l = new Rectangle(rect.X, last.Y, size, last.Height);
var Rect_l = new Rectangle(rect_l.Right, rect_l.Y, gap2, rect_l.Height);
if (bitblock_l == null || bitblock_l.Width != Rect_l.Width || bitblock_l.Height != Rect_l.Height)
{
bitblock_l?.Dispose();
bitblock_l = new Bitmap(Rect_l.Width, Rect_l.Height);
using (var g_bmp = Graphics.FromImage(bitblock_l).HighLay())
{
using (var path = new Rectangle(0, 0, gap, bitblock_l.Height).RoundPath(gap, false, true, true, false))
{
g_bmp.FillPath(brush, path);
}
}
Helper.Blur(bitblock_l, gap);
}
g.DrawImage(bitblock_l, Rect_l, .1F);
using (var path = Helper.RoundPath(rect_l, radius, true, false, false, true))
{
if (hover_l)
{
g.FillPath(brush_hover, path);
g.DrawLines(pen_hover, TAlignMini.Left.TriangleLines(rect_l, .5F));
}
else
{
g.FillPath(brush, path);
g.DrawLines(pen, TAlignMini.Left.TriangleLines(rect_l, .5F));
}
}
}
if (scroll_max != scroll_x)
{
rect_r = new Rectangle(rect.X + rect.Width - size, last.Y, size, last.Height);
var Rect_r = new Rectangle(rect_r.X - gap2, rect_r.Y, gap2, rect_r.Height);
if (bitblock_r == null || bitblock_r.Width != Rect_r.Width || bitblock_r.Height != Rect_r.Height)
{
bitblock_r?.Dispose();
bitblock_r = new Bitmap(Rect_r.Width, Rect_r.Height);
using (var g_bmp = Graphics.FromImage(bitblock_r).HighLay())
{
using (var path = new Rectangle(gap, 0, gap, bitblock_r.Height).RoundPath(gap, true, false, false, true))
{
g_bmp.FillPath(brush, path);
}
}
Helper.Blur(bitblock_r, gap);
}
g.DrawImage(bitblock_r, Rect_r, .1F);
using (var path = Helper.RoundPath(rect_r, radius, false, true, true, false))
{
if (hover_r)
{
g.FillPath(brush_hover, path);
g.DrawLines(pen_hover, TAlignMini.Right.TriangleLines(rect_r, .5F));
}
else
{
g.FillPath(brush, path);
g.DrawLines(pen, TAlignMini.Right.TriangleLines(rect_r, .5F));
}
}
}
}
}
break;
}
}
Rectangle rect_l, rect_r;
bool hover_l, hover_r = false;
bool Hover_l
{
get => hover_l;
set
{
if (hover_l == value) return;
hover_l = value;
Invalidate();
}
}
bool Hover_r
{
get => hover_r;
set
{
if (hover_r == value) return;
hover_r = value;
Invalidate();
}
}
#endregion
#endregion
#endregion
#region 事件
///
/// SelectedIndex 属性值更改时发生
///
[Description("SelectedIndex 属性值更改时发生"), Category("行为")]
public event IntEventHandler? SelectedIndexChanged = null;
///
/// 关闭页面前发生
///
[Description("关闭页面前发生"), Category("行为")]
public event ClosingPageEventHandler? ClosingPage;
#endregion
}
public class TabCollection : iCollection
{
public TabCollection(Tabs it)
{
BindData(it);
}
internal TabCollection BindData(Tabs it)
{
action = render =>
{
if (render) it.LoadLayout(false);
it.Invalidate();
};
action_add = item =>
{
item.PARENT = it;
item.Dock = DockStyle.Fill;
it.ShowPage();
};
action_del = item =>
{
int old = it.SelectedIndex;
int index = IndexOf(item);
if (old == index)
{
it.Controls.Remove(item);
int _new = index - 1;
if (_new > -1) it.SelectedIndex = _new;
else it.ShowPage();
}
else
{
it.Controls.Remove(item);
it.SelectedIndex = old - 1;
}
};
return this;
}
}
[ToolboxItem(false)]
[Designer(typeof(IControlDesigner))]
public class TabPage : ScrollableControl
{
public TabPage()
{
SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw |
ControlStyles.DoubleBuffer |
ControlStyles.SupportsTransparentBackColor |
ControlStyles.ContainerControl |
ControlStyles.UserPaint, true);
UpdateStyles();
}
#region 属性
Image? icon = null;
///
/// 图标
///
[Category("外观"), Description("图标"), DefaultValue(null)]
public Image? Icon
{
get => icon;
set
{
if (icon == value) return;
icon = value;
PARENT?.LoadLayout();
}
}
string? iconSvg = null;
///
/// 图标
///
[Category("外观"), Description("图标SVG"), DefaultValue(null)]
public string? IconSvg
{
get => iconSvg;
set
{
if (iconSvg == value) return;
iconSvg = value;
PARENT?.LoadLayout();
}
}
///
/// 是否包含图标
///
public bool HasIcon
{
get => iconSvg != null || icon != null;
}
bool readOnly = false;
///
/// 只读
///
[Description("只读"), Category("行为"), DefaultValue(false)]
public bool ReadOnly
{
get => readOnly;
set
{
if (readOnly == value) return;
readOnly = value;
PARENT?.LoadLayout();
}
}
#region 徽标
string? badge = null;
[Description("徽标内容"), Category("徽标"), DefaultValue(null)]
public string? Badge
{
get => badge;
set
{
if (badge == value) return;
badge = value;
Invalidate();
}
}
float badgeSize = .6F;
[Description("徽标比例"), Category("徽标"), DefaultValue(.6F)]
public float BadgeSize
{
get => badgeSize;
set
{
if (badgeSize != value)
{
badgeSize = value;
if (badge != null) PARENT?.Invalidate();
}
}
}
Color? badgeback = null;
[Description("徽标背景颜色"), Category("徽标"), DefaultValue(null)]
public Color? BadgeBack
{
get => badgeback;
set
{
if (badgeback == value) return;
if (badgeback != value)
{
badgeback = value;
if (badge != null) PARENT?.Invalidate();
}
}
}
///
/// 徽标偏移X
///
[Description("徽标偏移X"), Category("徽标"), DefaultValue(1)]
public int BadgeOffsetX { get; set; } = 1;
///
/// 徽标偏移Y
///
[Description("徽标偏移Y"), Category("徽标"), DefaultValue(1)]
public int BadgeOffsetY { get; set; } = 1;
#endregion
#endregion
#region 坐标
internal bool MDown = false;
internal Rectangle Rect = new Rectangle(-10, -10, 0, 0);
internal bool Contains(int x, int y)
{
return Rect.Contains(x, y);
}
internal Rectangle SetRect(Rectangle rect)
{
Rect = rect;
return Rect;
}
internal Rectangle SetOffset(int x, int y)
{
Rect.Offset(x, y);
return Rect;
}
#endregion
#region 变更
internal Tabs? PARENT;
protected override void OnTextChanged(EventArgs e)
{
PARENT?.LoadLayout();
base.OnTextChanged(e);
}
protected override void OnVisibleChanged(EventArgs e)
{
PARENT?.LoadLayout();
base.OnVisibleChanged(e);
}
bool isdpi = false;
public void DpiAuto()
{
if (isdpi) return;
isdpi = true;
if (DesignMode) return;
if (Config.Dpi != 1F)
{
SuspendLayout();
Helper.DpiAuto(Config.Dpi, this);
ResumeLayout();
}
}
#endregion
public override string ToString() => Text;
}
}