• Crumb -面包屑状的嵌套按钮


    Image 1

    介绍

    这么长时间以来,我一直在尝试寻找一个类似于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;

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

    Image 2

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

    设置这么多设置会带来很多可能性:一个面包屑可能会有/可能没有孩子;可能/可能没有复选框;可能/可能没有图像;可能/可能没有文本。为此,有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<crumbclickeventargs> childClick = 
         new EventHandler<crumbclickeventargs>(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,  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

  • 相关阅读:
    Mosquitto搭建Android推送服务(一)MQTT简介
    Quartz定时任务简单实例
    Oracle基础知识(一)、简介与安装
    [2013-08-01]window.open
    C#中DataTable与泛型集合互转(支持泛型集合中对象包含枚举)
    C#代码安装Windows服务(控制台应用集成Windows服务)
    Node+Socket实现聊天室
    web前端架构
    Laravel-admin form 表单是增加或者修改
    Laravel-admin 消息提醒、播放音频、点击跳转
  • 原文地址:https://www.cnblogs.com/Dincat/p/13488370.html
Copyright © 2020-2023  润新知