googo / 控件 / C#下自定义控件的制作 二

分享

   

C#下自定义控件的制作 二

2011-07-18  googo
  • 分类:C# Programming | 2009-02-26

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明

    经过第一讲我想一些朋友应该是云里雾里,到底这些是什么,那么这部分就是高我们么如何使用我们定义的这些属性和如何产生效果,这一部分也是 很具有数学知识的,比如贝塞尔曲线以路径方法的绘制,这个都是参照MSDN给的例子模拟的,然后很多懂Fireworks和Photoshop的朋友就会 很快的掌握程序的进程....至少我猜是这样的...

    通常我们的按钮是矩形的,这样的形状是不是很老套呢?那么我这里就绘制一个圆角矩形吧...有喜欢的朋友可以设计一个圆形的Button,实际上真的就是完全一样的方法...

    #region 绘制圆角矩形
            private GraphicsPath RoundRect(RectangleF r, float r1, float r2, float r3, float r4)
            {
                float x = r.X, y = r.Y, w = r.Width, h = r.Height;
                GraphicsPath rr = new GraphicsPath();
                rr.AddBezier(x, y + r1, x, y, x + r1, y, x + r1, y);
                rr.AddLine(x + r1, y, x + w - r2, y);
                rr.AddBezier(x + w - r2, y, x + w, y, x + w, y + r2, x + w, y + r2);
                rr.AddLine(x + w, y + r2, x + w, y + h - r3);
                rr.AddBezier(x + w, y + h - r3, x + w, y + h, x + w - r3, y + h, x + w - r3, y + h);
                rr.AddLine(x + w - r3, y + h, x + r4, y + h);
                rr.AddBezier(x + r4, y + h, x, y + h, x, y + h - r4, x, y + h - r4);
                rr.AddLine(x, y + h - r4, x, y + r1);
                return rr;
            }
            //为GraphicsPath类自定义一个函数名字为RoundRect,里面记录了利用GraphicsPath类的方法存储的线条模型
            #endregion

    这里所说的绘制就是在绘制路径,很熟悉吧,路径,要是高级的Photoshop或者Fireworks的玩家可以很轻松的找到这个词,不错,我也是个Photoshop和Fireworks玩家。

    要不要绘制写特效呢?自然要绘制...比如Photoshop中常用的Stroke(描边),我在这里分出了两个OuterStroke和InnerStroke,瞧瞧代码?好的...

    private void DrawOuterStroke(Graphics g)
            {
                if (this.ButtonStyle == Style.Flat && this.mButtonState == State.None) { return; }
                Rectangle r = this.ClientRectangle;
                r.Width -= 1; r.Height -= 1;
                using (GraphicsPath rr = RoundRect(r, CornerRadius, CornerRadius, CornerRadius, CornerRadius))
                {
                    //这里,rr就存储了相关的线条路径
                    using (Pen p = new Pen(this.ButtonColor))
                    {
                        g.DrawPath(p, rr);
                    }
                    //绘制这条路径...
                }
                //DrawPath函数利用Pen对象绘制相关的路径...,DrawPath为Graphics类的一个函数,接受2个参数,一个是Pen,另一个是GraphicsPath位置提供的一个对象
                //这里这个对象是由用户自定义的函数创造出来的一段路径...实际上,要使用DrawPath函数必须要提供一个路径....要不怎么叫Path呢?
            }

            private void DrawInnerStroke(Graphics g)
            {
                if (this.ButtonStyle == Style.Flat && this.mButtonState == State.None) { return; }
                Rectangle r = this.ClientRectangle;
                r.X++; r.Y++;
                r.Width -= 3; r.Height -= 3;
                using (GraphicsPath rr = RoundRect(r, CornerRadius, CornerRadius, CornerRadius, CornerRadius))
                {
                    using (Pen p = new Pen(this.HighlightColor))
                    {
                        g.DrawPath(p, rr);
                    }
                }
                //减少3个像素之后再次绘制第二个描边,也就是内层的描边...利用的颜色即是我们定义的高光颜色
            }

    是不是感觉在用Photoshop或者Fireworks制作一个Button,或者一个图标??是的,我们继续一些特效吧...

    private void DrawBackground(Graphics g)
            {
                if (this.ButtonStyle == Style.Flat && this.mButtonState == State.None) { return; }
               
                int alpha = (mButtonState == State.Pressed) ? 204 : 127;
                Rectangle r = this.ClientRectangle;
                r.Width--; r.Height--;
                using (GraphicsPath rr = RoundRect(r, CornerRadius, CornerRadius, CornerRadius, CornerRadius))
                {
                    using (SolidBrush sb = new SolidBrush(this.BaseColor))
                    {
                        g.FillPath(sb, rr);
                    }
                    SetClip(g); //这个函数是当我们设置背景图的时候,将背景图限制在我们绘制的RoundRectangle的范围内,这个是我们自定义的一个函数,具体是什么?留个悬念,放到第三讲去...
                    //不至于超出范围到整个客户区去...
                    if (this.BackImage != null)
                    {
                        g.DrawImage(this.BackImage, new Rectangle((this.Width / 2) - (this.BackImage.Width / 2),
                            (this.Height / 2) - (this.BackImage.Height / 2), this.BackImage.Width, this.BackImage.Height));
                        //这里并未对图像进行缩放,只是简单的加载,过大的图片会显示到图片外边去,然后会被裁掉...
                    }
                    g.ResetClip();//由于在SetClip中设置了操作剪辑,影响了操作剪辑原来的位置,因此要对修改的剪辑进行重置,就是删除该剪辑...(SetClip设置的)
                    //重置我们的编辑区到无穷大...以防止修改后的编辑区影响操作...(一般添加这样的语句以保证不出现错误,这是个好习惯)
                    using (SolidBrush sb = new SolidBrush(Color.FromArgb(alpha, this.ButtonColor)))
                    {
                        g.FillPath(sb, rr);
                        //利用ButtonColor填充Button体...(这个Button体就是我们在函数初新建的,并不是为了绘制图形而修改过的...修改过的路径在ResetClip已经被重置了...
                    }
                }
            }

            //利用命名空间System.Drawing.Drawing2D中的LinearGradientBrush类帮助绘制高光....
            private void DrawHighlight(Graphics g)
            {
                if (this.ButtonStyle == Style.Flat && this.mButtonState == State.None) { return; }

                int alpha = (mButtonState == State.Pressed) ? 60 : 150;
                Rectangle rect = new Rectangle(0, 0, this.Width, this.Height/2);
                using (GraphicsPath r = RoundRect(rect, CornerRadius, CornerRadius, 0, 0))
                {
                    using (LinearGradientBrush lg = new LinearGradientBrush(r.GetBounds(),
                                                Color.FromArgb(alpha, this.HighlightColor),
                                                Color.FromArgb(20, this.HighlightColor),
                                                LinearGradientMode.Vertical))
                    {
                        g.FillPath(lg, r);
                    }
                }
            }
            //绘制高光...这里有一个问题就是绘制的高光会产生一个界限,这个界限十分清楚,效果不是很好...
            //这主要是因为FillRectangle会产生对边界的依赖
            //如果不想要这个边界,可以将最后的渐变色设置接近透明(alpha 趋近 0,然后将整个渲染区域设置为全部的界面
            //但是怎样产生逼真的高光效果需要对数据进行精确的设置...

            private void DrawGlow(Graphics g)
            {
                if (this.mButtonState == State.Pressed) { return; }//如果按下按钮,就不绘制,这样会影响用户的体验,当然,这只是个Demo
                //如果需要,也可以对按下后的效果进行定义...
                SetClip(g);
                using (GraphicsPath glow = new GraphicsPath())
                {
                    glow.AddEllipse(-5, this.Height / 2 - 10, this.Width + 11, this.Height + 11);
                    using (PathGradientBrush gl = new PathGradientBrush(glow))
                    {
                        gl.CenterColor = Color.FromArgb(mGlowAlpha, this.GlowColor);//这里的mGlowAlpha不是初始化0值,我们可以参见
                        //mFadeIn_Tick函数中会有明确的定义...
                        gl.SurroundColors = new Color[] { Color.FromArgb(0, this.GlowColor) };
                        //这里及使用透明色进行填充我们的椭圆的外围
                        g.FillPath(gl, glow);
                        //这里的FillPath的笔刷gl的绘制则是按照一定的比例(系统自定)对椭圆中心进行渐变的绘制,渐变效果以椭圆中心变化到椭圆外围...
                    }
                }
                g.ResetClip();
                //设置了路径在使用完应该即时清除,这是个好习惯...
            }

    hoho,不错吧...一层一层的代码相当于那些图形软件里的填充、渐变命令,不错,如果一层一层的堆叠,一个按钮的外观是不是已经出来了?应该是的...

    Button还是需要有字的,所以,绘制一些字体吧....

    private void DrawText(Graphics g)
            {
                StringFormat sf = this.StringFormatAlignment(this.TextAlign);
                Rectangle r = new Rectangle(8, 8, this.Width - 17, this.Height - 17);
                g.DrawString(this.ButtonText, this.Font, new SolidBrush(this.ForeColor), r, sf);
                //g.DrawString中的参数解释:
                //ButtonText使我们这里设置的属性,Font是从Control中继承的,ForeColor也是继承的,第四个参数规定了字符绘制的位置和大小的矩形框,最后是绘制的效果是怎样的(居中、左对齐还是右对齐)
            }
            //为了规定我们自己的对其方式,我们在这里创建一个函数来表示,这个函数为了满足DrawString的要求,我们设置为StringFomat类型
            public StringFormat StringFormatAlignment(ContentAlignment textalign)//ContentAlignment是一个枚举类型的对象
            {
                StringFormat sf = new StringFormat();
                switch (textalign)
                {
                    case ContentAlignment.TopLeft:
                    case ContentAlignment.MiddleLeft:
                    case ContentAlignment.BottomLeft:
                        sf.Alignment = StringAlignment.Near;
                        break;
                    case ContentAlignment.TopCenter:
                    case ContentAlignment.MiddleCenter:
                    case ContentAlignment.BottomCenter:
                        sf.Alignment = StringAlignment.Center;
                        break;
                    case ContentAlignment.TopRight:
                    case ContentAlignment.MiddleRight:
                    case ContentAlignment.BottomRight:
                        sf.Alignment = StringAlignment.Far;
                        break;
                }
                return sf;
            }

     


    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。

    来自: googo > 《控件》

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多
    喜欢该文的人也喜欢 更多

    ×
    ×

    ¥.00

    微信或支付宝扫码支付:

    开通即同意《个图VIP服务协议》

    全部>>