最近试用msn8的时候,感觉其界面很清爽的样子,因此想自己绘制。首先是绘制一个自定义的Panel。基本思路是给Panel加上边框,然后里面区域的颜色依据边框颜色进行计算,然后绘制内部区域。
内部区域绘制时,为了模拟水晶效果,将整个panel分为上下两个部分,分别用渐变填充。
效果是实现了,但是不是很理想,尤其是闪烁的问题,过于严重,根本无法接受。代码如下,请各位指点。
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;namespace Bineon.SystemFramework.Windows.Forms.MSNStyle
{
    public class PanelEx : Panel
    {
        private bool m_LeftBorder = true;        public PanelEx()
        {
            base.ResizeRedraw = true;
            //base.DoubleBuffered = true;
        }
        public bool LeftBorder
        {
            get { return m_LeftBorder; }
            set { m_LeftBorder = value; }
        }
        private bool m_RightBorder = true;        public bool RightBorder
        {
            get { return m_RightBorder; }
            set { m_RightBorder = value; }
        }
        private bool m_TopBorder = true;        public bool TopBorder
        {
            get { return m_TopBorder; }
            set { m_TopBorder = value; }
        }
        private bool m_BottomBorder = true;        public bool BottomBorder
        {
            get { return m_BottomBorder; }
            set { m_BottomBorder = value; }
        }        private int m_OnePart = 70;        public int OnePart
        {
            get { return m_OnePart; }
            set 
            { 
                m_OnePart = value > 100 ? 100 : value;
                m_OnePart = value < 1 ? 1 : value;
            }
        }        private Color m_BorderColor = Color.Silver;       //正常状态下的边框颜色        public Color BorderColor
        {
            get { return m_BorderColor; }
            set 
            {
                m_BorderColor = value; 
            }
        }        public new BorderStyle BorderStyle
        {
            get { return BorderStyle.None; }
            set { base.BorderStyle = BorderStyle.None; }
        }        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);
            if (!this.m_Clarity)
            {
                Color color = this.BorderColor;
                Color start = Color.FromArgb(this.m_StartAlpha, color);
                Color end = Color.FromArgb(this.m_EndAlpha, color);
                this.PaintBorder(color);
                this.PaintRegion(start, end);
            }
        }        /// <summary>
        /// 绘制边框
        /// </summary>
        /// <param name="e"></param>
        protected virtual void PaintBorder(Color color)
        {
            Pen pen = new Pen(color);
            Graphics g = this.CreateGraphics();
            g.SmoothingMode = SmoothingMode.AntiAlias;
            Rectangle border = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
            Point[] points = this.GetPoints(border);
            if (this.LeftBorder)
            {
                g.DrawLine(pen, points[0], points[3]);
            }
            if (this.TopBorder)
            {
                g.DrawLine(pen, points[0], points[1]);
            }
            if (this.RightBorder)
            {
                g.DrawLine(pen, points[1], points[2]);
            }
            if (this.BottomBorder)
            {
                g.DrawLine(pen, points[2], points[3]);
            }
        }        /// <summary>
        /// 绘制填充区域
        /// </summary>
        protected virtual void PaintRegion(Color start, Color end, float angle)
        {
            Graphics g = this.CreateGraphics();
            g.SmoothingMode = SmoothingMode.AntiAlias;
            Brush brush;            brush = new LinearGradientBrush(this.ClientRectangle, end, start, angle);
            RectangleF top = new RectangleF(1, 1, this.Width - 3, (this.Height - 2) * this.OnePart / 100);
            g.FillRectangle(brush, top);            brush = new LinearGradientBrush(this.ClientRectangle, start, end, angle);
            RectangleF down = new RectangleF(top.Left, top.Bottom, (float)(this.Width - 3), (float)(this.Height - top.Height - 3));
            g.FillRectangle(brush, down);
        }        protected virtual void PaintRegion(Color start, Color end)
        {
            float angle = 90F;
            PaintRegion(start, end, angle);
        }        /// <summary>
        /// 获取矩形顶点数组
        /// </summary>
        /// <param name="rect"></param>
        /// <returns></returns>
        protected Point[] GetPoints(Rectangle rect)
        {
            return new Point[]
            {
                new Point(rect.Left, rect.Top),     //左上顶点
                new Point(rect.Right, rect.Top),    //右上顶点
                new Point(rect.Right, rect.Bottom), //右下顶点
                new Point(rect.Left, rect.Bottom)   //坐下顶点
            };
        }        private bool m_Clarity = false;        public bool Clarity
        {
            get { return m_Clarity; }
            set { m_Clarity = value; }
        }        private byte m_StartAlpha = 40;
        private byte m_EndAlpha = 30;
        private Color m_StartColor;
        private Color m_EndColor;    }
}

解决方案 »

  1.   

    楼上的能说清楚点吗?绘完再显示如何实现?一种想法是添加一个bitmap对象,完全在bitmap上绘制,然后绘制好整个图片后,再绘制到panel中。这种方法我没有测试,不知道是否可行,因为我感觉这种办法即使能解决问题,也不是良好的方法
      

  2.   

    试试
    public PanelEx()
    {
    this.SetStyle(ControlStyles.UserPaint,true);
    this.SetStyle(ControlStyles.DoubleBuffer,true);
    }
      

  3.   

    如果是.NET2.0的话用DoubleBuffer
    -------------------------------
    尝试过,但是结果不能正确绘制图像。于是注释了该语句。试试
    public PanelEx()
    {
    this.SetStyle(ControlStyles.UserPaint,true);
    this.SetStyle(ControlStyles.DoubleBuffer,true);
    }
    ---------------------
    测试过,不行。也不知道为什么to 但是不是很理想,尤其是闪烁的问题,过于严重,根本无法接受出现闪烁,是一直闪烁,还是做某些操作时候闪烁。
    -------------------------
    调整大小时闪烁。被其他窗体盖住了,移开其他窗体时重绘,闪烁,。
      

  4.   

    把如下三个函数
    protected virtual void PaintBorder(Color color)
    protected virtual void PaintRegion(Color start, Color end, float angle)
    protected virtual void PaintRegion(Color start, Color end)
    用这三个来替换
    protected virtual void PaintBorder(ref Graphics g, Color color)
    protected virtual void PaintRegion( ref Graphics g, Color start, Color end, float angle)
    protected virtual void PaintRegion(ref Graphics g, Color start, Color end)同时要删除创建Graphics对象的部分,在OnPaint事件中如下调用。
    Graphics g = e.Graphics;
    PaintBorder( ref g, color);
    PaintRegion( ref g, start, end);
      

  5.   

    替换以后问题依旧。不过你的指点是很必要的,我看到教程里面说CreateGraphics获取的g最好不要用于绘图。
    但是,怎么解决闪烁问题呢?全部源代码在这里,能有人帮助测试一下吗?
      

  6.   

    很奇怪,我测试了一下你的代码,根本就没画出来,你的代码需要设置哪些参数,除了
    private Color m_StartColor;
    private Color m_EndColor;
    这两个变量外
      

  7.   

    启用doublebuffer就不能绘制了。准确的说,启用doublebuffer只有,调整窗体大小的时候可以看到闪烁的绘制。但是窗体调整大小完毕以后则恢复为默认样式了。
      

  8.   

    这个是简洁点的代码,但是依然还是闪烁。
    m_OnePart表示将Panel区域分块,上面一块占用的百分比。程序将Panel分两块进行绘制。这在PaintRegion中实现。
    另外,区域中的颜色由Border的颜色计算其Alpha来获取。
            private byte m_StartAlpha = 40;
            private byte m_EndAlpha = 30;
    用上面两个变量定义了起始和结束分量。
    public class PanelEx : Panel
        {
            public PanelEx()
            {
                base.ResizeRedraw = true;
            }        private int m_OnePart = 70;        public int OnePart
            {
                get { return m_OnePart; }
                set
                {
                    m_OnePart = value > 100 ? 100 : value;
                    m_OnePart = value < 1 ? 1 : value;
                }
            }        private Color m_BorderColor = Color.Silver;       //正常状态下的边框颜色        public Color BorderColor
            {
                get { return m_BorderColor; }
                set
                {
                    this.m_StartColor = Color.FromArgb(this.m_StartAlpha, value);
                    this.m_EndColor = Color.FromArgb(this.m_EndAlpha, value);
                    m_BorderColor = value;
                }
            }        public new BorderStyle BorderStyle
            {
                get { return BorderStyle.None; }
                set { base.BorderStyle = BorderStyle.None; }
            }        protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
                Graphics g = e.Graphics;
                Color color = this.BorderColor;
                Color start = Color.FromArgb(this.m_StartAlpha, color);
                Color end = Color.FromArgb(this.m_EndAlpha, color);
                this.PaintBorder(ref g, color);
                this.PaintRegion(ref g, start, end);        }        /// <summary>
            /// 绘制边框
            /// </summary>
            /// <res>创建人员(日期): Bineon(060428 12:57)</res>
            /// <param name="e"></param>
            protected virtual void PaintBorder(ref Graphics g, Color color)
            {
                Pen pen = new Pen(color);
                g.SmoothingMode = SmoothingMode.HighQuality;
                Rectangle border = new Rectangle(0, 0, this.Width - 1, this.Height - 1);
                Point[] points = this.GetPoints(border);
                g.DrawRectangle(pen, border);
            }        /// <summary>
            /// 绘制填充区域
            /// </summary>
            /// <res>创建人员(日期): Bineon(060428 16:55)</res>
            protected virtual void PaintRegion(ref Graphics g, Color start, Color end, float angle)
            {
                g.SmoothingMode = SmoothingMode.HighQuality;
                Brush brush;            brush = new LinearGradientBrush(this.ClientRectangle, end, start, angle);
                RectangleF top = new RectangleF(1, 1, this.Width - 3, (this.Height - 2) * this.OnePart / 100);
                g.FillRectangle(brush, top);            brush = new LinearGradientBrush(this.ClientRectangle, start, end, angle);
                RectangleF down = new RectangleF(top.Left, top.Bottom, (float)(this.Width - 3), (float)(this.Height - top.Height - 3));
                g.FillRectangle(brush, down);
            }        protected virtual void PaintRegion(ref Graphics g, Color start, Color end)
            {
                float angle = 90F;
                PaintRegion(ref g, start, end, angle);
            }        /// <summary>
            /// 获取矩形顶点数组
            /// </summary>
            /// <param name="rect"></param>
            /// <returns></returns>
            protected Point[] GetPoints(Rectangle rect)
            {
                return new Point[]
                {
                    new Point(rect.Left, rect.Top),     //左上顶点
                    new Point(rect.Right, rect.Top),    //右上顶点
                    new Point(rect.Right, rect.Bottom), //右下顶点
                    new Point(rect.Left, rect.Bottom)   //坐下顶点
                };
            }        private byte m_StartAlpha = 40;
            private byte m_EndAlpha = 30;
            private Color m_StartColor;
            private Color m_EndColor;    }
      

  9.   

    在OnPaint事件中,开头增加一句this.SuspendLayout(),最后增加一句this.ResumeLayout()试试。没装vs2005,这东西太吃资源了。
      

  10.   

    Color start = Color.Black;//Color.FromArgb(this.m_StartAlpha, color);
                Color end = Color.Blue;//Color.FromArgb(this.m_EndAlpha, color);
    修改为这样的代码后就没有问题了。估计是这里的原因。
      

  11.   

    没看你的代码。当拖动窗口的时候,应该将背景“成块”地二进制复制,而不是重新按照业务逻辑绘制。当放大窗口size的时候,也不应该按照业务逻辑重新绘制。特别是选择“渐变”,可能就是在设计时期没有清醒认识到“不能重绘只能复制”这个原则,所以是需求描述到设计的转换中的描述错误。
      

  12.   

    Color start = Color.Black;//Color.FromArgb(this.m_StartAlpha, color);
                Color end = Color.Blue;//Color.FromArgb(this.m_EndAlpha, color);
    修改为这样的代码后就没有问题了。估计是这里的原因。
    楼上两位能解释一下吗?如果用FromArgb方法则闪烁,直接定义为已知颜色则不闪烁。
      

  13.   

    这是由于Color.Black和Color.Blue这两种颜色的alpha值为0。
    你出现闪烁的原因,是由于alpha值不为0造成。
    但是很奇怪,我在vs2003中没有任何问题,不管alpha值是否为0都可以。
      

  14.   

    楼上的大哥你能把03下的弄成dll发送到我邮箱吗?
    bineon (#)  hotmail.com