💎 Splitter 分隔面板增加折叠功能

* Splitter 分隔面板增加折叠功能
This commit is contained in:
Write-Bugs
2025-03-03 09:20:49 +00:00
committed by Tom
parent 74c05560f1
commit 91ef9fc25b

View File

@@ -16,8 +16,11 @@
// 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
@@ -30,6 +33,42 @@ namespace AntdUI
[ToolboxItem(true)]
public class Splitter : SplitContainer
{
/// <summary>
/// 记录折叠前的分隔距离
/// </summary>
private int _lastDistance;
//记录折叠控件最小大小
//折叠忽略最小,最小只在拖动分割栏时有效
private int _minSize;
/// <summary>
/// 当前鼠标状态
/// null = 无
/// false = 移动
/// true = 箭头
/// </summary>
private bool? _MouseState;
/// <summary>
/// 记录折叠控件的当前大小
/// </summary>
private int _size;
/// <summary>
/// 箭头SVG
/// </summary>
private string[] arrowSvg = new string[4]
{
"UpOutlined" , "DownOutlined",
"LeftOutlined" , "RightOutlined"
};
/// <summary>
/// 鼠标是否在箭头区域
/// </summary>
private bool m_bIsArrowRegion;
public Splitter()
{
SetStyle(
@@ -37,60 +76,468 @@ namespace AntdUI
ControlStyles.AllPaintingInWmPaint |
ControlStyles.OptimizedDoubleBuffer |
ControlStyles.ResizeRedraw, true);
SplitterMoving += Splitter_SplitterMoving;
SplitterMoved += Splitter_SplitterMoved;
}
#region
/// <summary>
/// SplitPanelState 属性值更改时发生
/// </summary>
[Description("SplitPanelState 属性值更改时发生"), Category("行为")]
public event BoolEventHandler? SplitPanelStateChanged = null;
#endregion
#region
/// <summary>
/// 滑块大小
/// </summary>
[Description("滑块大小"), Category("行为"), DefaultValue(20)]
public int SplitterSize { get; set; } = 20;
private Color? _arrawBackHoverColor;
#endregion
private Color? _arrowBackColor;
private Color? _arrowColor;
private int _arrowSize = 30;
private ADCollapsePanel _collapsePanel = ADCollapsePanel.Panel1;
/// <summary>
/// 当前折叠状态
/// </summary>
private bool _splitPanelState = true;
private Color? back;
public enum ADCollapsePanel
{
None = 0,
Panel1 = 1,
Panel2 = 2,
}
[Description("箭头背景色"), DefaultValue(null), Category("外观")]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? ArrawBackColor
{
get => _arrowBackColor;
set
{
_arrowBackColor = value;
Invalidate();
}
}
[Description("鼠标悬浮箭头背景色"), DefaultValue(null), Category("外观")]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? ArrawBackHoverColor
{
get => _arrawBackHoverColor;
set
{
_arrawBackHoverColor = value;
Invalidate();
}
}
[Description("箭头颜色"), DefaultValue(null), Category("外观")]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public Color? ArrowColor
{
get => _arrowColor;
set
{
_arrowColor = value;
Invalidate();
}
}
/// <summary>
/// 箭头大小
/// </summary>
[Description("箭头大小"), Category("外观"), DefaultValue(30)]
public int ArrowSize
{
get => _arrowSize;
set
{
if (_arrowSize == value) return;
_arrowSize = value;
Invalidate();
}
}
/// <summary>
/// 背景颜色
/// </summary>
[Description("背景颜色"), DefaultValue(null), Category("外观")]
[Editor(typeof(Design.ColorEditor), typeof(UITypeEditor))]
public new Color? BackColor
{
get => back;
set
{
if (back == value) return;
back = value;
Invalidate();
}
}
[Description("点击后收起的Panel"), Category("行为"), DefaultValue(ADCollapsePanel.Panel1)]
public ADCollapsePanel CollapsePanel
{
get => _collapsePanel;
set
{
if (_collapsePanel != value)
{
Expand();
_collapsePanel = value;
Invalidate();
}
}
}
[Description("拆分器是水平的还是垂直的"), Category("行为"), DefaultValue(Orientation.Vertical)]
public new Orientation Orientation
{
get => base.Orientation;
set
{
if (base.Orientation == value) return;
base.Orientation = value;
if (!SplitPanelState)
{
SplitPanelState = true;
SplitPanelState = false;
}
Invalidate();
}
}
[Description("分隔栏宽度"), Category("外观"), DefaultValue(15)]
public new int SplitterWidth
{
get => base.SplitterWidth;
set
{
if (base.SplitterWidth == value) return;
base.SplitterWidth = value;
Invalidate();
}
}
[Description("是否进行展开"), Category("行为"), DefaultValue(true)]
private bool SplitPanelState
{
get => _splitPanelState;
set
{
if (_splitPanelState == value)
return;
if (value)
Expand();
else
Collapse();
_splitPanelState = value;
}
}
/// <summary>
/// 折叠
/// </summary>
public void Collapse()
{
if (_collapsePanel == ADCollapsePanel.None)
return;
_lastDistance = SplitterDistance;
if (_collapsePanel == ADCollapsePanel.Panel1)
{
_minSize = Panel1MinSize;
Panel1MinSize = 0;
SplitterDistance = 0;
}
else
{
int width = Orientation == Orientation.Horizontal ?
Height : Width;
_minSize = Panel2MinSize;
Panel2MinSize = 0;
SplitterDistance = width - SplitterWidth - Padding.Vertical;
}
_splitPanelState = false;
Invalidate();
}
/// <summary>
/// 展开
/// </summary>
public void Expand()
{
if (_collapsePanel == ADCollapsePanel.None)
return;
SplitterDistance = _lastDistance;
if (_collapsePanel == ADCollapsePanel.Panel1)
Panel1MinSize = _minSize;
else
Panel2MinSize = _minSize;
_splitPanelState = true;
Invalidate();
}
#endregion
#region
/// <summary>
/// 箭头背景区域
/// </summary>
private Rectangle ArrowRect
{
get
{
if (_collapsePanel == ADCollapsePanel.None)
return Rectangle.Empty;
int size = ArrowSize;
Rectangle rect = SplitterRectangle;
if (Orientation == Orientation.Horizontal)
{
rect.X = (int)((Width - size) / 2);
rect.Width = (int)(size);
}
else
{
rect.Y = (int)((Height - size) / 2);
rect.Height = (int)(size);
}
return rect;
}
}
protected override void OnPaint(PaintEventArgs e)
{
if (Panel1Collapsed || Panel2Collapsed) return;
var g = e.Graphics.High();
var rect = SplitterRectangle;
if (moving) g.Fill(Style.Db.PrimaryBg, rect);
else g.Fill(Style.Db.FillTertiary, rect);
int size = (int)(SplitterSize * Config.Dpi);
if (Orientation == Orientation.Horizontal) g.Fill(Style.Db.Fill, new Rectangle(rect.X + (rect.Width - size) / 2, rect.Y, size, rect.Height));
else g.Fill(Style.Db.Fill, new Rectangle(rect.X, rect.Y + (rect.Height - size) / 2, rect.Width, size));
//绘制背景
g.Fill(back ?? Colour.Split.Get("Splitter"), rect);
//不进行折叠
if (_collapsePanel == ADCollapsePanel.None)
return;
//画箭头背景
Point[] points = GetHandlePoints();
if (m_bIsArrowRegion)
g.FillPolygon(_arrawBackHoverColor ?? Colour.PrimaryBgHover.Get("Splitter"), points);
else
g.FillPolygon(_arrowBackColor ?? Colour.PrimaryBg.Get("Splitter"), points);
//计算显示图标
bool is_p1 = _collapsePanel == ADCollapsePanel.Panel1;
int index = 0;
if (Orientation == Orientation.Horizontal)
index = 1;
else
index = 3;
if (is_p1 == SplitPanelState)
index--;
//绘制箭头
DrawArrow(g, arrowSvg[index]);
}
#endregion
protected override void OnSizeChanged(EventArgs e)
{
base.OnSizeChanged(e);
Invalidate();
}
private void DrawArrow(Canvas g, string svg)
{
int size = Math.Min(ArrowSize, SplitterWidth);
Point point;
//居中显示
if (Orientation == Orientation.Horizontal)
point = new Point((SplitterRectangle.Width - size) / 2, SplitterRectangle.Top);
else
point = new Point(SplitterRectangle.Left, (SplitterRectangle.Height - size) / 2);
Rectangle rectangle = new Rectangle(point.X, point.Y, size, size);
SvgExtend.GetImgExtend(g, svg, rectangle, _arrowColor ?? Colour.PrimaryBorder.Get("Splitter"));
}
private Point[] GetHandlePoints()
{
var points = new List<Point>();
bool isPanel1 = CollapsePanel == ADCollapsePanel.Panel1;
bool isCollapsed = isPanel1 == SplitPanelState;
int size = 5;
if (Orientation == Orientation.Horizontal)
{
int y1 = 0, y2 = 0;
if (isCollapsed)
{
y1 = ArrowRect.Bottom;
y2 = ArrowRect.Top;
}
else
{
y1 = ArrowRect.Top;
y2 = ArrowRect.Bottom;
}
points.Add(new Point(ArrowRect.Left, y1));
points.Add(new Point(ArrowRect.Right, y1));
points.Add(new Point(ArrowRect.Right - size, y2));
points.Add(new Point(ArrowRect.Left + size, y2));
}
else if (Orientation == Orientation.Vertical)
{
int x1 = 0, x2 = 0;
if (isCollapsed)
{
x1 = ArrowRect.Right;
x2 = ArrowRect.Left;
}
else
{
x1 = ArrowRect.Left;
x2 = ArrowRect.Right;
}
points.Add(new Point(x1, ArrowRect.Top));
points.Add(new Point(x1, ArrowRect.Bottom));
points.Add(new Point(x2, ArrowRect.Bottom - size));
points.Add(new Point(x2, ArrowRect.Top + size));
}
return points.ToArray();
}
#endregion
#region
bool moving = false;
private void Splitter_SplitterMoving(object? sender, SplitterCancelEventArgs e)
/// <summary>
/// 重载鼠标按下事件
/// </summary>
/// <param name="e">鼠标参数</param>
protected override void OnMouseDown(MouseEventArgs e)
{
if (e.Cancel) return;
moving = true;
if (DesignMode)
return;
//点位在箭头矩形内
if (ArrowRect.Contains(e.Location))
_MouseState = true;
else if (!SplitPanelState)
_MouseState = null;
//点位在分割线内
else if (SplitterRectangle.Contains(e.Location))
_MouseState = false;
if (_MouseState != true)
base.OnMouseDown(e);
}
/// <summary>
/// 重载鼠标离开事件
/// </summary>
/// <param name="e">鼠标参数</param>
protected override void OnMouseLeave(EventArgs e)
{
if (DesignMode)
return;
base.Cursor = Cursors.Default;
m_bIsArrowRegion = false;
Invalidate();
base.OnMouseLeave(e);
}
private void Splitter_SplitterMoved(object? sender, SplitterEventArgs e)
/// <summary>
/// 重载鼠标移动事件
/// </summary>
/// <param name="e">鼠标参数</param>
protected override void OnMouseMove(MouseEventArgs e)
{
moving = false;
if (DesignMode)
return;
//如果鼠标的左键没有按下,重置鼠标状态
if (e.Button != MouseButtons.Left)
_MouseState = null;
//鼠标在Arrow矩形里并且不是在拖动
if (ArrowRect.Contains(e.Location) && _MouseState != false)
{
Cursor = Cursors.Hand;
m_bIsArrowRegion = true;
Invalidate();
return;
}
if (_MouseState == true)
return;
m_bIsArrowRegion = false;
Invalidate();
//鼠标在分隔栏矩形里
if (SplitterRectangle.Contains(e.Location))
{
//如果已经折叠,就不允许拖动了
if (_collapsePanel != ADCollapsePanel.None && !SplitPanelState)
Cursor = Cursors.Default;
//鼠标没有按下设置Split光标
else if (_MouseState == null && !IsSplitterFixed)
Cursor = Orientation == Orientation.Horizontal ? Cursors.HSplit : Cursors.VSplit;
return;
}
//正在拖动分隔栏
if (_MouseState == false && !IsSplitterFixed)
{
Cursor = Orientation == Orientation.Horizontal ? Cursors.HSplit : Cursors.VSplit;
base.OnMouseMove(e);
return;
}
base.Cursor = Cursors.Default;
}
protected override void Dispose(bool disposing)
/// <summary>
/// 重载鼠标抬起事件
/// </summary>
/// <param name="e">鼠标参数</param>
protected override void OnMouseUp(MouseEventArgs e)
{
SplitterMoving -= Splitter_SplitterMoving;
SplitterMoved -= Splitter_SplitterMoved;
base.Dispose(disposing);
if (DesignMode)
return;
base.OnMouseUp(e);
Invalidate();
if (_MouseState == true && e.Button == MouseButtons.Left && ArrowRect.Contains(e.Location))
{
SplitPanelState = !SplitPanelState;
SplitPanelStateChanged?.Invoke(this, new BoolEventArgs(SplitPanelState));
}
_MouseState = null;
}
#endregion
#endregion
}
}