Crumb -面包屑状的嵌套按钮

下载source code and demo project, and an executable in the debug folder - 70.3 KB

介绍

这么长时间以来,我一直在尝试寻找一个类似于Ubuntu软件中心的breadcrumb控件,但我就是找不到,所以我决定自己去做一个。所以,在几个小时的工作之后,这就是它!

背景

这张图片显示了我的想法是从哪里来的。

使用的代码

这个类继承了System.Windows.Forms。控件类,而不是来自System.Windows.Forms。按钮,但行为和大多数功能是相同的。System.Windows.Forms.Vercas。Crumb类具有以下特性:

NameDescription显示在按钮上的文本。按钮上显示的图像。文本对齐按钮上文本的对齐方式。图像在按钮上的对齐方式。textimagealign确定图像和文本是否都将按照文本的对齐方式进行对齐,并并排显示。checkbox确定是否应该在按钮上绘制复选框以显示其已选中的属性。否则,按钮将显示有焦点或无焦点。确定单击按钮时是否会更改其选中属性。checked确定按钮是集中显示还是选中了按钮上显示的复选框。获取巢中碎屑的索引。孩子:在水流之后的碎屑。

嵌套行为允许最多检查一个碎屑。在示例表单中,有一个对CrumbClick默认事件的订阅,该事件将始终在嵌套中保留选中的crumb。顺便说一下,截图中蓝色的按钮是选中的碎屑。

我还必须补充一点,背景图像被绘制了多次,以实现背景,因为,正如下面指出的,它没有正常拉伸,右边是透明的。

现在是技术部分…具体来说,就是绘制按钮。嗯,因为我缺乏绘画技巧,我决定使用图像。

隐藏,复制代码static Image Left_Edge = Properties.Resources.crumb_left_end;
static Image Body = Properties.Resources.crumb_body;
static Image Right_Edge = Properties.Resources.crumb_right_end;
static Image Right_Triangle = Properties.Resources.crumb_right_point;

static Image Selected_Left_Edge = Properties.Resources.selected_crumb_left_end;
static Image Selected_Body = Properties.Resources.selected_crumb_body;
static Image Selected_Right_Edge = Properties.Resources.selected_crumb_right_end;
static Image Selected_Right_Triangle = Properties.Resources.selected_crumb_right_point;

static Image Hovered_Left_Edge = Properties.Resources.hovered_crumb_left_end;
static Image Hovered_Body = Properties.Resources.hovered_crumb_body;
static Image Hovered_Right_Edge = Properties.Resources.hovered_crumb_right_end;
static Image Hovered_Right_Triangle = Properties.Resources.hovered_crumb_right_point;

static Image Clicked_Left_Edge = Properties.Resources.clicked_crumb_left_end;
static Image Clicked_Body = Properties.Resources.clicked_crumb_body;
static Image Clicked_Right_Edge = Properties.Resources.clicked_crumb_right_end;
static Image Clicked_Right_Triangle = Properties.Resources.clicked_crumb_right_point;

放置这些静态变量是为了让您可以给它们另一个值。例如,如果您将图像放在外部文件中,这将非常有用。顺便说一下,这些是图片:

它们被用来占用(磁盘上)最小的空间。正如我所说,我必须绘制填充/身体图像多次,因为拉伸它会在上面放置一个透明渐变。

设置这么多设置会带来很多可能性:一个面包屑可能会有/可能没有孩子;可能/可能没有复选框;可能/可能没有图像;可能/可能没有文本。为此,有DefaultSize覆盖属性:

隐藏,复制代码protected override Size DefaultSize
{
get
{
var w = (c == null ? (this.Controls.Count == 0 ? 3 : 15) :
Math.Max(15, c.Width)) + (this.CheckBox ? 24 : 0) +
(this.img != null ? img.Width : 0) +
(!string.IsNullOrEmpty(this.Text) ?
TextRenderer.MeasureText(this.Text, this.Font).Width : 0) +
(this.Parent is Crumb ? 13 : 0);
return new Size(this.Controls.Count > 0 ? w :
Math.Max(w, this.Width), 24);
}
}

你可能已经注意到了,我把所有的可能性都考虑进去了。最重要的是,如果crumb有一个子节点,它将自动调整大小,因为我有一些bug。文本的字体也很重要。文本被测量,大小也根据它计算。

控件中有一个名为CrumbClick的事件。此事件通过嵌套的碎屑重定向自身。所以任何点击的碎屑会启动所有碎屑的事件。订阅第一个crumb事件将实际上帮助您跟踪所有的crumb。该事件是按标准的,并具有一个EventArgs类称为CrumbClickEventArgs,它有以下属性:

索引嵌套中被单击的碎屑的索引。点击了SenderThe crumb。在被点击之前是否检查了crumb。获取或设置事件后碎屑的检查状态。如果ChecksOnClick为真,则没有任何效果。ChecksOnClickGets或设置crumb是否应该在单击时切换其检查状态。如果这个属性为真,那么不管您在事件参数中说什么,crumb的检查状态总是会转移到另一个状态。

此事件通过订阅子节点的CrumbClick事件传递给父节点。事件的代码是这样的:

隐藏,复制代码EventHandler childClick =
new EventHandler(c_Click);
static void c_Click(object sender, CrumbClickEventArgs e)
{
if ((sender as Crumb).Parent is Crumb)
{ ((sender as Crumb).Parent as Crumb).OnCrumbClick(e); }
}

事件被重新调用,但这次是在父屑上。保留事件参数。

我还提到过,巢里只有一个碎屑可以被检查。这应该是模拟一个选择系统。

隐藏,复制代码public Boolean Checked
{
get
{
return chk;
}
set
{
if (!nocc)
{
nocc = true;

        Crumb cr = this.Child;
        while (cr != null) { cr.Checked = false; cr = cr.Child; }
        cr = this.Parent as Crumb;
        while (cr != null && cr is Crumb)
            { cr.Checked = false; cr = cr.Parent as Crumb; }

        nocc = false;
    }
    chk = value;

    Refresh();
}

}

nocc是一个静态布尔值,用来声明其他的碎屑不应该更新它们在巢中的同伴,因此会避免溢出异常。

现在,绘图部分:

隐藏,缩小,复制代码public static float dc(PaintEventArgs e, Color foreColor, float x = 0f,
string text = “”, Image img = null, bool clicked = false,
bool hovered = false, bool chk = false, bool chkbox = false,
float width = 0f, Font font = null, bool tai = true,
ContentAlignment ta = ContentAlignment.MiddleCenter,
ContentAlignment ia = ContentAlignment.MiddleLeft,
bool pt = false, bool ch = true)
{
if (font == null) { font = SystemFonts.MessageBoxFont; }
width = Math.Max((ch ? 15 : 3) + (chkbox ? 24 : 0) +
(img != null ? img.Width : 0) + (!string.IsNullOrEmpty(text) ?
TextRenderer.MeasureText(text, font).Width : 0) + (pt ? 13 : 0), width);

if (clicked)
{
    e.Graphics.DrawImage(Crumb.Clicked_Left_Edge, x, 0);
    for (int i = (int)x + Crumb.Clicked_Left_Edge.Width; i <= 
                  x + width - (ch ? Crumb.Clicked_Right_Triangle : 
                  Crumb.Clicked_Right_Edge).Width; i++)
        e.Graphics.DrawImage(Crumb.Clicked_Body, i, 0);
    e.Graphics.DrawImage(ch ? Crumb.Clicked_Right_Triangle : 
      Crumb.Clicked_Right_Edge, x + width - (ch ? 
      Crumb.Clicked_Right_Triangle : Crumb.Clicked_Right_Edge).Width, 0);
}
else if (hovered)
{
    e.Graphics.DrawImage(Crumb.Hovered_Left_Edge, x, 0);
    for (int i = (int)x + Crumb.Hovered_Left_Edge.Width; i <= 
               x + width - (ch ? Crumb.Hovered_Right_Triangle : 
               Crumb.Hovered_Right_Edge).Width; i++)
        e.Graphics.DrawImage(Crumb.Hovered_Body, i, 0);
    e.Graphics.DrawImage((ch ? Crumb.Hovered_Right_Triangle : 
       Crumb.Hovered_Right_Edge), x + width - (ch ? 
       Crumb.Hovered_Right_Triangle : Crumb.Hovered_Right_Edge).Width, 0);
}
else if (chk && !chkbox)
{
    e.Graphics.DrawImage(Crumb.Selected_Left_Edge, x, 0);
    for (int i = (int)x + Crumb.Selected_Left_Edge.Width; i <= 
                  x + width - (ch ? Crumb.Selected_Right_Triangle : 
                  Crumb.Selected_Right_Edge).Width; i++)
        e.Graphics.DrawImage(Crumb.Selected_Body, i, 0);
    e.Graphics.DrawImage((ch ? Crumb.Selected_Right_Triangle : 
               Crumb.Selected_Right_Edge), x + width - (ch ? 
               Crumb.Selected_Right_Triangle : 
               Crumb.Selected_Right_Edge).Width, 0);
}
else
{
    e.Graphics.DrawImage(Crumb.Left_Edge, x, 0);
    for (int i = (int)x + Crumb.Left_Edge.Width; i <= x + width - 
               (ch ? Crumb.Right_Triangle : Crumb.Right_Edge).Width; i++)
        e.Graphics.DrawImage(Crumb.Body, i, 0);
    e.Graphics.DrawImage((ch ? Crumb.Right_Triangle : Crumb.Right_Edge), 
         x + width - (ch ? Crumb.Right_Triangle : Crumb.Right_Edge).Width, 0);
}

if (chkbox)
{
    var st = chk ? (clicked ? 
      System.Windows.Forms.VisualStyles.CheckBoxState.CheckedPressed : 
      System.Windows.Forms.VisualStyles.CheckBoxState.CheckedNormal) : 
      (clicked ? System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedPressed : 
      System.Windows.Forms.VisualStyles.CheckBoxState.UncheckedNormal);

    var sz = CheckBoxRenderer.GetGlyphSize(e.Graphics, st);

    CheckBoxRenderer.DrawCheckBox(e.Graphics, new Point((int)(x + 
       (pt ? 13 : 0) + (24 - sz.Height) / 2), (24 - sz.Height) / 2), st);
}

if (tai)
{
    dit(e, foreColor, x + (pt ? 13 : 0), ta, text, font, chkbox, width, ia, img);
}
else
{
    di(e, x, img, ia, chkbox, width);
    dt(e, foreColor, x + (pt ? 13 : 0), ta, text, font, chkbox, width);
}

return width;

}

这里我绘制按钮的组件图像,然后是文本/图像。不要哭,没有那么难。它只是需要做很多检查,以确保一切都很酷。在最后一行中,有三种不同的方法:dit(绘制图像文本)、di(绘制图像)和dt(绘制文本)。它们都是静态方法,需要很多变量来决定在哪里绘制。以下是守则:

隐藏,缩小,复制代码private static void dt(PaintEventArgs e, Color foreColor, float x = 0f,
ContentAlignment txta = ContentAlignment.MiddleCenter,
string text = “”, Font font = null,
bool chkbox = false, float width = 0f)
{
if (!string.IsNullOrEmpty(text))
{
PointF p = new PointF();

    var s = e.Graphics.MeasureString(text, font);

    switch (txta)
    {
        case ContentAlignment.BottomCenter:
            p = new PointF(x + ((chkbox ? (width - 24) : 
                    width) - 15 - s.Width) / 2, 21 - s.Height);
            break;
        case ContentAlignment.BottomLeft:
            p = new PointF(x + (chkbox ? 24 : 3), 21 - s.Height);
            break;
        case ContentAlignment.BottomRight:
            p = new PointF(x + ((chkbox ? (width - 24) : 
                           width) - 15) - s.Width, 21 - s.Height);
            break;

        case ContentAlignment.MiddleCenter:
            p = new PointF(x + ((chkbox ? (width - 24) : 
                           width) - 15 - s.Width) / 2, (24 - s.Height) / 2);
            break;
        case ContentAlignment.MiddleLeft:
            p = new PointF(x + (chkbox ? 24 : 3), (24 - s.Height) / 2);
            break;
        case ContentAlignment.MiddleRight:
            p = new PointF(x + ((chkbox ? (width - 24) : 
                           width) - 15) - s.Width, (24 - s.Height) / 2);
            break;

        case ContentAlignment.TopCenter:
            p = new PointF(x + ((chkbox ? (width - 24) : 
                           width) - 15 - s.Width) / 2, 3);
            break;
        case ContentAlignment.TopLeft:
            p = new PointF(x + (chkbox ? 24 : 3), 3);
            break;
        case ContentAlignment.TopRight:
            p = new PointF(x + ((chkbox ? 
                (width - 24) : width) - 15) - s.Width, 3);
            break;
    }

    using (Brush b = new SolidBrush(foreColor))
        e.Graphics.DrawString(text, font, b, p);
}

}

这里我根据TextAlign属性绘制文本:

隐藏,缩小,复制代码private static void di(PaintEventArgs e, float x = 0f, Image img = null,
ContentAlignment imga = ContentAlignment.MiddleLeft,
bool chkbox = false, float width = 0f)
{
if (img != null)
{
PointF p = new Point();

    switch (imga)
    {
        case ContentAlignment.BottomCenter:
            p = new PointF(x + ((chkbox ? (width - 24) : 
                    width) - 15 - img.Width) / 2, 21 - img.Height);
            break;
        case ContentAlignment.BottomLeft:
            p = new PointF(x + (chkbox ? 24 : 3), 21 - img.Height);
            break;
        case ContentAlignment.BottomRight:
            p = new PointF(x + ((chkbox ? (width - 24) : 
                    width) - 15) - img.Width, 21 - img.Height);
            break;

        case ContentAlignment.MiddleCenter:
            p = new PointF(x + ((chkbox ? (width - 24) : 
                    width) - 15 - img.Width) / 2, (24 - img.Height) / 2);
            break;
        case ContentAlignment.MiddleLeft:
            p = new PointF(x + (chkbox ? 24 : 3), (24 - img.Height) / 2);
            break;
        case ContentAlignment.MiddleRight:
            p = new PointF(x + ((chkbox ? (width - 24) : 
                    width) - 15) - img.Width, (24 - img.Height) / 2);
            break;

        case ContentAlignment.TopCenter:
            p = new PointF(x + ((chkbox ? (width - 24) : 
                           width) - 15 - img.Width) / 2, 3);
            break;
        case ContentAlignment.TopLeft:
            p = new PointF(x + (chkbox ? 24 : 3), 3);
            break;
        case ContentAlignment.TopRight:
            p = new PointF(x + ((chkbox ? (width - 24) : 
                           width) - 15) - img.Width, 3);
            break;
    }

    e.Graphics.DrawImage(img, p);
}

}

这里我根据ImageAlign属性绘制图像:

隐藏,缩小,复制代码private static void dit(PaintEventArgs e, Color foreColor, float x = 0f,
ContentAlignment txta = ContentAlignment.MiddleCenter,
string text = “”, Font font = null, bool chkbox = false,
float width = 0f,
ContentAlignment imga = ContentAlignment.MiddleLeft, Image img = null)
{
if (!string.IsNullOrEmpty(text))
{
if (img != null)
{
if (!string.IsNullOrEmpty(text))
{
float w = 0, h = 0, ht = 0;

            var s = e.Graphics.MeasureString(text, font);

            switch (txta)
            {
                case ContentAlignment.BottomCenter:
                    w = ((chkbox ? (width - 24) : width) - 15 - 
                          s.Width - img.Width) / 2; h = 21 - 
                          img.Height; ht = 21 - s.Height;
                    break;
                case ContentAlignment.BottomLeft:
                    w = chkbox ? 24 : 3; h = 21 - img.Height; ht = 21 - s.Height;
                    break;
                case ContentAlignment.BottomRight:
                    w = ((chkbox ? (width - 24) : width) - 15) - 
                          s.Width - img.Width; h = 21 - 
                          img.Height; ht = 21 - s.Height;
                    break;

                case ContentAlignment.MiddleCenter:
                    w = ((chkbox ? (width - 24) : width) - 15 - 
                          s.Width - img.Width) / 2; h = (24 - img.Height) / 2; 
                          ht = (24 - s.Height) / 2;
                    break;
                case ContentAlignment.MiddleLeft:
                    w = chkbox ? 24 : 3; h = (24 - img.Height) / 2; 
                                 ht = (24 - s.Height) / 2;
                    break;
                case ContentAlignment.MiddleRight:
                    w = ((chkbox ? (width - 24) : width) - 15) - s.Width - 
                          img.Width; h = (24 - img.Height) / 2; 
                          ht = (24 - s.Height) / 2;
                    break;

                case ContentAlignment.TopCenter:
                    w = ((chkbox ? (width - 24) : width) - 15 - 
                          s.Width - img.Width) / 2; h = ht = 3;
                    break;
                case ContentAlignment.TopLeft:
                    w = chkbox ? 24 : 3; h = ht = 3;
                    break;
                case ContentAlignment.TopRight:
                    w = ((chkbox ? (width - 24) : width) - 15) - 
                          s.Width - img.Width; h = ht = 3;
                    break;
            }

            w += x;

            e.Graphics.DrawImage(img, w, h);

            using (Brush b = new SolidBrush(foreColor))
                e.Graphics.DrawString(text, font, b, w + img.Width, ht);
        }
    }
    else
    {
        dt(e, foreColor, x, txta, text, font, chkbox, width);
    }
}
else
{
    di(e, x, img, imga, chkbox, width);
}

}

这里我根据TextAlign属性绘制图像和文本。同样,这个混乱被用在控件的OnPaint(…)方法上:

隐藏,复制代码protected override void OnPaint(PaintEventArgs e)
{
Crumb.dc(e, this.ForeColor, 0, Text, this.img, this.clicked,
this.hovered, this.chk, this.chkbox, this.c == null ?
this.Width : (this.Width - this.c.Width), this.Font,
this.tai, this.txta, this.imga, this.Parent is Crumb,
this.Controls.Count > 0);
base.OnPaint(e);
}

这里我传递了dc绘制面包屑所需的参数。

同样,dit方法只在TextImageAlign属性设置为true时使用。

在绘制按钮(不是复选框、文本或图像)时,有4个(或3个)可能的选择(取决于是否有复选框):

ClickedHoveredChecked/Selected(如果没有复选框)(如果有,复选框将显示已选中/未选中)正常

当嵌套一个屑,尖的(右)边的父被画在子上面。这可以在子组件的绘制事件的订阅中找到。的代码是:

隐藏,复制代码PaintEventHandler childPaint = new PaintEventHandler(c_Paint);
//Really, this is required for unsubscribing from the event.
//Does it sound right to unsubscribe a NEW DELEGATE from an event?

static void c_Paint(object sender, PaintEventArgs e)
{
var c = sender as Crumb;

if (c.Parent != null && c.Parent is Crumb)
{
    var p = c.Parent as Crumb;
    dc(e, Color.Black, -25f, width: 38f, hovered: 
       p.hovered, clicked: p.clicked, chk: p.chk, chkbox: p.chkbox);
}

}

我真的在寻找一种更快的绘图方式,因为,在这个例子中,我在选择面包屑时得到了一些剪切。

的兴趣点

我已经学会了那个图形。在拉伸糟透了。

历史

11/28/2010 -首次发布。12/1/2010 -更新的文章与代码讨论。

请告诉我你发现的bug !此外,如果有些事情似乎不太好,在低评价之前问我!

					本文转载于:http://www.diyabc.com/frontweb/news14567.html
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值