C# WinForm编写一个六边形菜单
using System;
using System.Drawing;
using System.Windows.Forms;
namespace HexagonButton
{
public partial class HButton : PictureBox
{
//0-6 每个梯形位置,-1 中间位置
// 4
// 3 5
// -1
// 2 0
// 1
///
/// 菜单点击事件处理
///
public event Action
///
/// 每个梯形位置
///
private Trapezoid[] trapezoids = new Trapezoid[6];
///
/// 中间的小正六边形位置
///
private Point[] centerHexagon = new Point[6];
///
/// 鼠标当前位置
///
private Point? mouseHoverLocation = null;
///
/// 鼠标滑过时的层背景
///
private SolidBrush mouseHoverLayerBrush = new SolidBrush(Color.FromArgb(50, Color.White));
public HButton()
{
InitializeComponent();
DoubleBuffered = true;
}
///
/// 缩放窗体时(调用),自动修正位置
///
///
///
public void ResetSizeByForm(int formWidth, int formHeight)
{
var hHeight = (int)(formHeight * 0.8);
var hWidth = hHeight;
var hLeft = (formWidth - hWidth) / 2;
var hTop = (formHeight - hHeight) / 2;
this.Location = new Point(hLeft, hTop);
this.Width = hWidth;
this.Height = hHeight;
}
private void InitHexagonMenus()
{
this.BackColor = Color.Transparent;
//this.Image = Properties.Resources.button_bg;
this.SizeMode = PictureBoxSizeMode.Zoom;
this.Cursor = Cursors.Hand;
//计算图片缩放级别
//var scale = Properties.Resources.button_bg.Width / this.Width;
//计算原始图片高度和宽度之差,因为该背景非正六边形,所以计算一下宽度和高度之差,用以计算正确得位置
//var diffOfImageSize = (Properties.Resources.button_bg.Width - Properties.Resources.button_bg.Height) / scale;
var diffOfImageSize = 0;
var sideWidth = (this.Width - diffOfImageSize) / 2;
var offset = this.Width / 2;
//计算最外层大六边形顶点位置
var big = CalculateHexagonVertices((this.Width + diffOfImageSize / 2) / 2, offset);
//计算内部小六边形顶点位置
var small = CalculateHexagonVertices(sideWidth / 2, offset);
//计算两个六边形相交之后,形成得六边形环,分割为6个等腰梯形,用以检测点击事件
trapezoids = CalculateTrapezoids(big, small);
//计算内部小的正六边形,用以检测点击事件
centerHexagon = CalculateHexagonVertices(sideWidth / 2 - 20, offset);
}
///
/// 鼠标滑过的时候,重绘界面,然后设置鼠标位置
///
///
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
mouseHoverLocation = e.Location;
Invalidate();
}
///
/// 鼠标移除
///
///
protected override void OnMouseLeave(EventArgs e)
{
base.OnMouseLeave(e);
mouseHoverLocation = null;
}
///
/// 菜单点击事件
///
///
protected override void OnMouseClick(MouseEventArgs e)
{
var mouseLocation = e.Location;
//检测鼠标是否在中间的六边形中:
if (IsPointInPolygon(mouseLocation, centerHexagon))
{
OnMenuClicked?.Invoke(this, -1);
return;
}
//检测是否在某个梯形内部
for (int i = 0; i < trapezoids.Length; i++)
{
var trapezoid = trapezoids[i];
if (IsPointInTrapezoid(mouseLocation, trapezoid))
{
OnMenuClicked?.Invoke(this, i);
return;
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var g = e.Graphics;
#if DEBUG
//以下的代码可以直接删除,这里是作为标识多边形位置
g.FillPolygon(new SolidBrush(Color.FromArgb(180, Color.Red)), centerHexagon);
for (int i = 0; i < trapezoids.Length; i++)
{
var trapezoid = trapezoids[i];
g.FillPolygon(new SolidBrush(Color.FromArgb(10 * (i + 1), Color.Yellow)), trapezoid.Points);
}
#endif
if (mouseHoverLocation == null)
{
return;
}
//检测鼠标是否在中间的六边形中:
if (IsPointInPolygon(mouseHoverLocation.Value, centerHexagon))
{
g.FillPolygon(mouseHoverLayerBrush, centerHexagon);
return;
}
//检测是否在某个梯形内部
for (int i = 0; i < trapezoids.Length; i++)
{
var trapezoid = trapezoids[i];
if (IsPointInTrapezoid(mouseHoverLocation.Value, trapezoid))
{
g.FillPolygon(mouseHoverLayerBrush, trapezoid.Points);
return;
}
}
}
///
/// 计算正六边形的顶点坐标
///
///
///
///
private static Point[] CalculateHexagonVertices(int sideLength, int offset = 0)
{
Point[] vertices = new Point[6];
double angle = 2 * Math.PI / 6;
for (int i = 0; i < 6; i++)
{
int x = offset + (int)(sideLength * Math.Cos(i * angle));
int y = offset + (int)(sideLength * Math.Sin(i * angle));
vertices[i] = new Point(x, y);
}
return vertices;
}
///
/// 计算每个梯形的坐标
///
///
///
///
private static Trapezoid[] CalculateTrapezoids(Point[] hexagonVertices, Point[] smallHexagonVertices)
{
Trapezoid[] trapezoids = new Trapezoid[6];
for (int i = 0; i < 6; i++)
{
Point topLeft = hexagonVertices[i];
Point topRight = hexagonVertices[(i + 1) % 6];
Point bottomLeft = smallHexagonVertices[i];
Point bottomRight = smallHexagonVertices[(i + 1) % 6];
trapezoids[i] = new Trapezoid(topLeft, topRight, bottomLeft, bottomRight)
{
Index = i
};
}
return trapezoids;
}
///
/// 判断点是否在梯形内
///
///
///
///
private static bool IsPointInTrapezoid(Point checkPoint, Trapezoid trapezoid)
{
return IsPointInPolygon(checkPoint, trapezoid.Points);
}
///
/// 判断点是否在多边形内.
/// 来源:https://blog.csdn.net/xxdddail/article/details/49093635
/// ----------原理----------
/// 注意到如果从P作水平向左的射线的话,如果P在多边形内部,那么这条射线与多边形的交点必为奇数,
/// 如果P在多边形外部,则交点个数必为偶数(0也在内)。
///
/// 要判断的点
/// 多边形的顶点
///
private static bool IsPointInPolygon(Point checkPoint, Point[] polygonPoints)
{
bool inside = false;
int pointCount = polygonPoints.Length;
Point p1, p2;
for (int i = 0, j = pointCount - 1; i < pointCount; j = i, i++)//第一个点和最后一个点作为第一条线,之后是第一个点和第二个点作为第二条线,之后是第二个点与第三个点,第三个点与第四个点...
{
p1 = polygonPoints[i];
p2 = polygonPoints[j];
if (checkPoint.Y < p2.Y)
{//p2在射线之上
if (p1.Y <= checkPoint.Y)
{//p1正好在射线中或者射线下方
if ((checkPoint.Y - p1.Y) * (p2.X - p1.X) > (checkPoint.X - p1.X) * (p2.Y - p1.Y))//斜率判断,在P1和P2之间且在P1P2右侧
{
//射线与多边形交点为奇数时则在多边形之内,若为偶数个交点时则在多边形之外。
//由于inside初始值为false,即交点数为零。所以当有第一个交点时,则必为奇数,则在内部,此时为inside=(!inside)
//所以当有第二个交点时,则必为偶数,则在外部,此时为inside=(!inside)
inside = (!inside);
}
}
}
else if (checkPoint.Y < p1.Y)
{
//p2正好在射线中或者在射线下方,p1在射线上
if ((checkPoint.Y - p1.Y) * (p2.X - p1.X) < (checkPoint.X - p1.X) * (p2.Y - p1.Y))//斜率判断,在P1和P2之间且在P1P2右侧
{
inside = (!inside);
}
}
}
return inside;
}
///
/// 当窗体改变时,自动计算大小
///
///
protected override void OnClientSizeChanged(EventArgs e)
{
base.OnClientSizeChanged(e);
InitHexagonMenus();
}
}
///
/// 梯形类
///
internal sealed class Trapezoid
{
public int Index { get; set; }
public Point TopLeft { get; set; }
public Point TopRight { get; set; }
public Point BottomLeft { get; set; }
public Point BottomRight { get; set; }
public Point[] Points
{
get
{
return new Point[] { TopLeft, TopRight, BottomRight, BottomLeft };
}
}
public Trapezoid(Point topLeft, Point topRight, Point bottomLeft, Point bottomRight)
{
TopLeft = topLeft;
TopRight = topRight;
BottomLeft = bottomLeft;
BottomRight = bottomRight;
}
public override string ToString()
{
return $"Trapezoid {{ Index={Index}, TopLeft={TopLeft}, TopRight={TopRight}, BottomLeft={BottomLeft}, BottomRight={BottomRight} }}";
}
}
}
- .NET Core系列之MemoryCache 初识
- 007手机一键Root(安机网一键Root) v3.0 官方最新版 一键ROOT您的Android手机
- 12306密码被盗了怎么办?12306密码外泄解决方法
- 12个字的qq网名
- 150M迷你型无线路由器怎么设置?
- 192.168.1.1打不开怎么办?路由器192.168.1.1打不开的原因以及解决办法
- 2011年电子报合订本 电子报 编辑部 中文 PDF版 [84M]
- 2015年1月15日小米新旗舰发布会现场图文直播
- 2016.3.1vivo Xplay5新品发布会现场视频直播 优酷直播
- 2016华为P9发布会视频直播地址 4月15日华为P9国行发布会直播