自定义 TabPage 选项卡,例如 Microsoft Dynamics CRM 2015 中的流程箭头
Customize TabPage tabs like Process Flow Arrows in Microsoft Dynamics CRM 2015
我希望 Windows 表单应用程序中的 TabControl 看起来像这样:
https://technet.microsoft.com/en-us/library/dn531164.aspx
我有一个具有流程的应用程序,每个 TabPage 都是一个流程阶段,我想用这些漂亮的箭头来表示。我知道 OnPaint 和 System.Drawing,但我无法使这些选项卡看起来像样。
我尝试处理 TabControl.DrawItem 事件并绘制箭头,但我对外观不满意。
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
Rectangle rect = e.Bounds;
int offset = 10;
Point p1 = e.Bounds.Location;
Point p2 = new Point(e.Bounds.X + e.Bounds.Width - offset, e.Bounds.Y);
Point p3 = new Point(e.Bounds.X + e.Bounds.Width, e.Bounds.Y + (e.Bounds.Height / 2));
Point p4 = new Point(p2.X, e.Bounds.Bottom);
Point p5 = new Point(e.Bounds.X, e.Bounds.Y+e.Bounds.Height);
Point p6 = new Point(e.Bounds.X + offset, p3.Y);
GraphicsPath path = new GraphicsPath();
path.AddLine(p1, p2);
path.AddLine(p2, p3);
path.AddLine(p3, p4);
path.AddLine(p4, p5);
path.AddLine(p5, p6);
path.AddLine(p6, p1);
e.Graphics.FillPath(Brushes.Black, path);
};
是否有任何其他方法可以使这项工作如所描述的那样?
有时我会发疯并接受挑战只是为了好玩:-)
结果如下:
我用 Panel tp
覆盖了 TabControl tabControl1
。 (请选择更好的名字!)请注意,这必须发生得相当晚,否则 TabControl
会弹出到顶部。
我在 Shown
活动中叫他们点餐:
private void Form1_Shown(object sender, EventArgs e)
{
tp.BringToFront();
tabControl1.SendToBack();
}
您可能可以在设计器中创建 Panel
来避免这些动作..
除了 Panel
之外,我还需要一个 List<GraphicsPath>
用于绘图和精确命中测试..:[=27=]
Panel tp = null;
List<GraphicsPath> tabAreas = new List<GraphicsPath>();
您可以调用此函数来准备 Panel
和 List
:
void makeTabPanel(TabControl tab)
{
tp = new Panel();
tp.Size = new Size(tab.Width, tab.ItemSize.Height);
tp.Paint += tp_Paint;
tp.MouseClick += tp_MouseClick;
tp.Location = tab.Location;
tab.Parent.Controls.Add(tp);
int tabs = tabControl1.TabPages.Count;
float w = tabControl1.Width / tabs;
float h = tp.Size.Height;
float y0 = 0; float y1 = h / 2f; float y2 = h;
float d = 5; // <--- this is the gap
float e = 8; // <- this is the extrusion
float w1 = w - d;
tabAreas = new List<GraphicsPath>();
for (int t = 0; t < tabs; t++)
{
int t1 = t + 1;
float e1 = t == 0 ? 0 : e; // corrections for start and end..
float e2 = t == tabs - 1 ? 0 : e;
float e3 = t == tabs - 1 ? d : 0;
List<PointF> points = new List<PointF>();
points.Add(new PointF(t * w, y0));
points.Add(new PointF(t1 * w - d + e3, y0));
points.Add(new PointF(t1 * w -d + e2 + e3, y1));
points.Add(new PointF(t1 * w- d + e3, y2));
points.Add(new PointF(t * w, y2));
points.Add(new PointF(t * w + e1, y1));
GraphicsPath gp = new GraphicsPath(FillMode.Alternate);
gp.AddPolygon(points.ToArray());
tabAreas.Add(gp);
}
}
之后的实际事件就相当简单了:
void tp_MouseClick(object sender, MouseEventArgs e)
{
for (int t = 0; t < tabAreas.Count; t++)
{
if (tabAreas[t].IsVisible(e.Location) )
{ tabControl1.SelectedIndex = t; break;}
}
tp.Invalidate();
}
void tp_Paint(object sender, PaintEventArgs e)
{
StringFormat fmt = new StringFormat()
{ Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.Clear(Color.White); // **
float w = tp.Width / tabAreas.Count;
Size sz = new System.Drawing.Size( (int)w, e.ClipRectangle.Height);
for (int t = 0; t < tabAreas.Count; t++)
{
Rectangle rect = new Rectangle((int)(t * w ), 0, sz.Width, sz.Height);
bool selected = tabControl1.SelectedIndex == t ;
Brush brush = selected ? Brushes.DarkGoldenrod : Brushes.DarkGray; // **
e.Graphics.FillPath(brush, tabAreas[t]);
e.Graphics.DrawString(tabControl1.TabPages[t].Text,
tabControl1.Font, Brushes.White, rect, fmt);
}
}
在这里选择你的颜色 (**)
更新:按照 Lars 的建议使用 TextRenderer 也同样有效。 (至少 ;-)
TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter ;
TextRenderer.DrawText(e.Graphics, tabControl1.TabPages[t].Text,
tabControl1.Font, rect, Color.White, flags);
我希望 Windows 表单应用程序中的 TabControl 看起来像这样: https://technet.microsoft.com/en-us/library/dn531164.aspx
我有一个具有流程的应用程序,每个 TabPage 都是一个流程阶段,我想用这些漂亮的箭头来表示。我知道 OnPaint 和 System.Drawing,但我无法使这些选项卡看起来像样。
我尝试处理 TabControl.DrawItem 事件并绘制箭头,但我对外观不满意。
private void tabControl1_DrawItem(object sender, DrawItemEventArgs e)
{
Rectangle rect = e.Bounds;
int offset = 10;
Point p1 = e.Bounds.Location;
Point p2 = new Point(e.Bounds.X + e.Bounds.Width - offset, e.Bounds.Y);
Point p3 = new Point(e.Bounds.X + e.Bounds.Width, e.Bounds.Y + (e.Bounds.Height / 2));
Point p4 = new Point(p2.X, e.Bounds.Bottom);
Point p5 = new Point(e.Bounds.X, e.Bounds.Y+e.Bounds.Height);
Point p6 = new Point(e.Bounds.X + offset, p3.Y);
GraphicsPath path = new GraphicsPath();
path.AddLine(p1, p2);
path.AddLine(p2, p3);
path.AddLine(p3, p4);
path.AddLine(p4, p5);
path.AddLine(p5, p6);
path.AddLine(p6, p1);
e.Graphics.FillPath(Brushes.Black, path);
};
是否有任何其他方法可以使这项工作如所描述的那样?
有时我会发疯并接受挑战只是为了好玩:-)
结果如下:
我用 Panel tp
覆盖了 TabControl tabControl1
。 (请选择更好的名字!)请注意,这必须发生得相当晚,否则 TabControl
会弹出到顶部。
我在 Shown
活动中叫他们点餐:
private void Form1_Shown(object sender, EventArgs e)
{
tp.BringToFront();
tabControl1.SendToBack();
}
您可能可以在设计器中创建 Panel
来避免这些动作..
除了 Panel
之外,我还需要一个 List<GraphicsPath>
用于绘图和精确命中测试..:[=27=]
Panel tp = null;
List<GraphicsPath> tabAreas = new List<GraphicsPath>();
您可以调用此函数来准备 Panel
和 List
:
void makeTabPanel(TabControl tab)
{
tp = new Panel();
tp.Size = new Size(tab.Width, tab.ItemSize.Height);
tp.Paint += tp_Paint;
tp.MouseClick += tp_MouseClick;
tp.Location = tab.Location;
tab.Parent.Controls.Add(tp);
int tabs = tabControl1.TabPages.Count;
float w = tabControl1.Width / tabs;
float h = tp.Size.Height;
float y0 = 0; float y1 = h / 2f; float y2 = h;
float d = 5; // <--- this is the gap
float e = 8; // <- this is the extrusion
float w1 = w - d;
tabAreas = new List<GraphicsPath>();
for (int t = 0; t < tabs; t++)
{
int t1 = t + 1;
float e1 = t == 0 ? 0 : e; // corrections for start and end..
float e2 = t == tabs - 1 ? 0 : e;
float e3 = t == tabs - 1 ? d : 0;
List<PointF> points = new List<PointF>();
points.Add(new PointF(t * w, y0));
points.Add(new PointF(t1 * w - d + e3, y0));
points.Add(new PointF(t1 * w -d + e2 + e3, y1));
points.Add(new PointF(t1 * w- d + e3, y2));
points.Add(new PointF(t * w, y2));
points.Add(new PointF(t * w + e1, y1));
GraphicsPath gp = new GraphicsPath(FillMode.Alternate);
gp.AddPolygon(points.ToArray());
tabAreas.Add(gp);
}
}
之后的实际事件就相当简单了:
void tp_MouseClick(object sender, MouseEventArgs e)
{
for (int t = 0; t < tabAreas.Count; t++)
{
if (tabAreas[t].IsVisible(e.Location) )
{ tabControl1.SelectedIndex = t; break;}
}
tp.Invalidate();
}
void tp_Paint(object sender, PaintEventArgs e)
{
StringFormat fmt = new StringFormat()
{ Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center };
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.Clear(Color.White); // **
float w = tp.Width / tabAreas.Count;
Size sz = new System.Drawing.Size( (int)w, e.ClipRectangle.Height);
for (int t = 0; t < tabAreas.Count; t++)
{
Rectangle rect = new Rectangle((int)(t * w ), 0, sz.Width, sz.Height);
bool selected = tabControl1.SelectedIndex == t ;
Brush brush = selected ? Brushes.DarkGoldenrod : Brushes.DarkGray; // **
e.Graphics.FillPath(brush, tabAreas[t]);
e.Graphics.DrawString(tabControl1.TabPages[t].Text,
tabControl1.Font, Brushes.White, rect, fmt);
}
}
在这里选择你的颜色 (**)
更新:按照 Lars 的建议使用 TextRenderer 也同样有效。 (至少 ;-)
TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter ;
TextRenderer.DrawText(e.Graphics, tabControl1.TabPages[t].Text,
tabControl1.Font, rect, Color.White, flags);