C# WinForm中带文字的进度条

时间:2022-09-11 13:14:56
MFC转WinForm的新手请教:
给ProgressBar添加文字,但不会重载OnPaint方法,就用了Graphics.DrawString()。

        private void button2_Click(object sender, EventArgs e)
        {
            progressBar1.Value = Math.Min(progressBar1.Value+10, 100);
            Graphics dc = progressBar1.CreateGraphics();
            Font font = new Font("宋体", 10);
            Brush br = Brushes.Black;
            Rectangle rec = progressBar1.ClientRectangle;
            StringFormat drawFormat = new StringFormat();
            drawFormat.Alignment = StringAlignment.Center;
            drawFormat.LineAlignment = StringAlignment.Center;
            dc.DrawString(string.Format("{0}%", progressBar1.Value), font, br, rec, drawFormat);
        }

但文字会一闪而逝。我猜是:ProgressBar.Value改变后,进度条没有立即重绘;Value的改变仅仅相当于是PostMessage到消息队列。之后Graphics.DrawString()绘制出文字。再之后进度条重绘,原文字被刷没了。
我想到的解决办法是,控件在重绘完成后是不是会产生一个事件,关联此事件到一个委托方法,在委托方法中Graphics.DrawString()就行了。
求大神提供方法,先行谢过了!
另外,ProgressBar.Value改变,进度条的变化怎么是连续且有延迟的?比如从0直接变成100,能明显看到进度条从空白慢悠悠地涨满,用时0.5秒左右。我想让这个改变一蹴而就,何解?
实在不行,有大神提供个现成的派生控件?再行谢过!

8 个解决方案

#2


该回复于2014-08-08 11:24:29被管理员删除

#3


引用 1 楼 bdmh 的回复:
http://lzy3169421.blog.163.com/blog/static/11354527720097710196795/

谢谢版主的回复,问题解决!
我在原文的基础上做了一些修改,可以显示任意字符,只要对Text属性赋值即可。如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace SimpleWindowsFormsControlLibrary
{
    [ToolboxItem(true)]
    public partial class TextProgressBar : System.Windows.Forms.ProgressBar
    {
        [System.Runtime.InteropServices.DllImport("user32.dll ")]
        static extern IntPtr GetWindowDC(IntPtr hWnd);
        [System.Runtime.InteropServices.DllImport("user32.dll ")]
        static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
        // 添加文字相关的私有字段
        private string text = "";
        private Color textColor = Color.Black;
        private Font font = new System.Drawing.Font("SimSun ", 9);

        // 重写Text属性
        [BrowsableAttribute(true)]
        [BindableAttribute(true)]
        public override string Text
        {
            get { return text; }
            set { text = value; this.Invalidate(); }
        }

        // 文字颜色
        public System.Drawing.Color TextColor
        {
            get { return textColor; }
            set { textColor = value; this.Invalidate(); }
        }

        // 重写Font属性
        [BrowsableAttribute(false)]
        public override Font Font
        {
            get { return font; }
            set { font = value; this.Invalidate(); }
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            if (m.Msg == 0xf || m.Msg == 0x133)
            {
                //拦截系统消息,获得当前控件进程以便重绘。  
                //一些控件(如TextBox、Button等)是由系统进程绘制,重载OnPaint方法将不起作用.  
                //所有这里并没有使用重载OnPaint方法绘制TextBox边框。  
                //MSDN:重写   OnPaint   将禁止修改所有控件的外观。  
                //那些由   Windows   完成其所有绘图的控件(例如   Textbox)从不调用它们的   OnPaint   方法,  
                //因此将永远不会使用自定义代码。请参见您要修改的特定控件的文档,  
                //查看   OnPaint   方法是否可用。如果某个控件未将   OnPaint   作为成员方法列出,  
                //则您无法通过重写此方法改变其外观。  
                //MSDN:要了解可用的   Message.Msg、Message.LParam   和   Message.WParam   值,  
                //请参考位于   MSDN   Library   中的   Platform   SDK   文档参考。可在   Platform   SDK(“Core   SDK”一节)  
                //下载中包含的   windows.h   头文件中找到实际常数值,该文件也可在   MSDN   上找到。  

                IntPtr hDC = GetWindowDC(m.HWnd);
                if (hDC.ToInt32() == 0)
                {
                    return;
                }

                //base.OnPaint(e);

                System.Drawing.Graphics g = Graphics.FromHdc(hDC);
                SolidBrush brush = new SolidBrush(textColor);

                SizeF size = g.MeasureString(text, font);
                float x = (this.Width - size.Width) / 2;
                float y = (this.Height - size.Height) / 2;
                g.DrawString(Text, font, brush, x, y);
                // 下面的显示方法与上面的方法有相同的效果
                //StringFormat format = new StringFormat();
                //format.Alignment = StringAlignment.Center;
                //format.LineAlignment = StringAlignment.Center;
                //g.DrawString(text, font, brush, this.ClientRectangle, format);

                //返回结果
                m.Result = IntPtr.Zero;
                //释放
                ReleaseDC(m.HWnd, hDC);
            }
        }
    }

}

#4


这可真是编程悲剧。你打算每次加一个功能,都破坏一次 WndProc ?

#5


重新修改了一下,让Font属性可见:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace SimpleWindowsFormsControlLibrary
{
    [ToolboxItem(true)]
    public partial class TextProgressBar : System.Windows.Forms.ProgressBar
    {
        [System.Runtime.InteropServices.DllImport("user32.dll ")]
        static extern IntPtr GetWindowDC(IntPtr hWnd);
        [System.Runtime.InteropServices.DllImport("user32.dll ")]
        static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
        // 添加文字相关的私有字段
        private string text = "";
        private Color textColor = Color.Black;
        private Font font = new Font("SimSun", 9F);

        // 重写Text属性
        [BrowsableAttribute(true)]
        [BindableAttribute(true)]
        public override string Text
        {
            get { return text; }
            set { text = value; this.Invalidate(); }
        }

        // 文字颜色
        public Color TextColor
        {
            get { return textColor; }
            set { textColor = value; this.Invalidate(); }
        }

        // 重写Font属性
        [BrowsableAttribute(true)]
        public override Font Font
        {
            get { return font; }
            set { font = value; this.Invalidate(); }
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            if (m.Msg == 0xf || m.Msg == 0x133)
            {
                //拦截系统消息,获得当前控件进程以便重绘。  
                //一些控件(如TextBox、Button等)是由系统进程绘制,重载OnPaint方法将不起作用.  
                //所有这里并没有使用重载OnPaint方法绘制TextBox边框。  
                //MSDN:重写   OnPaint   将禁止修改所有控件的外观。  
                //那些由   Windows   完成其所有绘图的控件(例如   Textbox)从不调用它们的   OnPaint   方法,  
                //因此将永远不会使用自定义代码。请参见您要修改的特定控件的文档,  
                //查看   OnPaint   方法是否可用。如果某个控件未将   OnPaint   作为成员方法列出,  
                //则您无法通过重写此方法改变其外观。  
                //MSDN:要了解可用的   Message.Msg、Message.LParam   和   Message.WParam   值,  
                //请参考位于   MSDN   Library   中的   Platform   SDK   文档参考。可在   Platform   SDK(“Core   SDK”一节)  
                //下载中包含的   windows.h   头文件中找到实际常数值,该文件也可在   MSDN   上找到。  

                IntPtr hDC = GetWindowDC(m.HWnd);
                if (hDC.ToInt32() == 0)
                {
                    return;
                }

                //base.OnPaint(e);

                System.Drawing.Graphics g = Graphics.FromHdc(hDC);
                SolidBrush brush = new SolidBrush(textColor);

                SizeF size = g.MeasureString(text, font);
                float x = (this.Width - size.Width) / 2;
                float y = (this.Height - size.Height) / 2;
                g.DrawString(Text, font, brush, x, y);
                // 下面的显示方法与上面的方法有相同的效果
                //StringFormat format = new StringFormat();
                //format.Alignment = StringAlignment.Center;
                //format.LineAlignment = StringAlignment.Center;
                //g.DrawString(text, font, brush, this.ClientRectangle, format);

                //返回结果
                m.Result = IntPtr.Zero;
                //释放
                ReleaseDC(m.HWnd, hDC);
            }
        }
    }

}

#6


对于着重于实际业务系统开发的人,我建议学点高级的 WPF 下的 XAML 酷设计,拒绝“玩儿” GDI 等东西。

#7


引用 4 楼 sp1234 的回复:
这可真是编程悲剧。你打算每次加一个功能,都破坏一次 WndProc ?

请问应该怎么改比较好?我刚学.net没几天。

#8


引用 6 楼 sp1234 的回复:
对于着重于实际业务系统开发的人,我建议学点高级的 WPF 下的 XAML 酷设计,拒绝“玩儿” GDI 等东西。

我是业余的,不是程序员。上学时学的C语言,后自学MFC和VB用来做些小工具。真要高级点的,我自学了Qt的一点皮毛。
想请教您一下,在WinForm里,这个问题如何解决比较合理?

#1


#2


该回复于2014-08-08 11:24:29被管理员删除

#3


引用 1 楼 bdmh 的回复:
http://lzy3169421.blog.163.com/blog/static/11354527720097710196795/

谢谢版主的回复,问题解决!
我在原文的基础上做了一些修改,可以显示任意字符,只要对Text属性赋值即可。如下:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace SimpleWindowsFormsControlLibrary
{
    [ToolboxItem(true)]
    public partial class TextProgressBar : System.Windows.Forms.ProgressBar
    {
        [System.Runtime.InteropServices.DllImport("user32.dll ")]
        static extern IntPtr GetWindowDC(IntPtr hWnd);
        [System.Runtime.InteropServices.DllImport("user32.dll ")]
        static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
        // 添加文字相关的私有字段
        private string text = "";
        private Color textColor = Color.Black;
        private Font font = new System.Drawing.Font("SimSun ", 9);

        // 重写Text属性
        [BrowsableAttribute(true)]
        [BindableAttribute(true)]
        public override string Text
        {
            get { return text; }
            set { text = value; this.Invalidate(); }
        }

        // 文字颜色
        public System.Drawing.Color TextColor
        {
            get { return textColor; }
            set { textColor = value; this.Invalidate(); }
        }

        // 重写Font属性
        [BrowsableAttribute(false)]
        public override Font Font
        {
            get { return font; }
            set { font = value; this.Invalidate(); }
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            if (m.Msg == 0xf || m.Msg == 0x133)
            {
                //拦截系统消息,获得当前控件进程以便重绘。  
                //一些控件(如TextBox、Button等)是由系统进程绘制,重载OnPaint方法将不起作用.  
                //所有这里并没有使用重载OnPaint方法绘制TextBox边框。  
                //MSDN:重写   OnPaint   将禁止修改所有控件的外观。  
                //那些由   Windows   完成其所有绘图的控件(例如   Textbox)从不调用它们的   OnPaint   方法,  
                //因此将永远不会使用自定义代码。请参见您要修改的特定控件的文档,  
                //查看   OnPaint   方法是否可用。如果某个控件未将   OnPaint   作为成员方法列出,  
                //则您无法通过重写此方法改变其外观。  
                //MSDN:要了解可用的   Message.Msg、Message.LParam   和   Message.WParam   值,  
                //请参考位于   MSDN   Library   中的   Platform   SDK   文档参考。可在   Platform   SDK(“Core   SDK”一节)  
                //下载中包含的   windows.h   头文件中找到实际常数值,该文件也可在   MSDN   上找到。  

                IntPtr hDC = GetWindowDC(m.HWnd);
                if (hDC.ToInt32() == 0)
                {
                    return;
                }

                //base.OnPaint(e);

                System.Drawing.Graphics g = Graphics.FromHdc(hDC);
                SolidBrush brush = new SolidBrush(textColor);

                SizeF size = g.MeasureString(text, font);
                float x = (this.Width - size.Width) / 2;
                float y = (this.Height - size.Height) / 2;
                g.DrawString(Text, font, brush, x, y);
                // 下面的显示方法与上面的方法有相同的效果
                //StringFormat format = new StringFormat();
                //format.Alignment = StringAlignment.Center;
                //format.LineAlignment = StringAlignment.Center;
                //g.DrawString(text, font, brush, this.ClientRectangle, format);

                //返回结果
                m.Result = IntPtr.Zero;
                //释放
                ReleaseDC(m.HWnd, hDC);
            }
        }
    }

}

#4


这可真是编程悲剧。你打算每次加一个功能,都破坏一次 WndProc ?

#5


重新修改了一下,让Font属性可见:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;

namespace SimpleWindowsFormsControlLibrary
{
    [ToolboxItem(true)]
    public partial class TextProgressBar : System.Windows.Forms.ProgressBar
    {
        [System.Runtime.InteropServices.DllImport("user32.dll ")]
        static extern IntPtr GetWindowDC(IntPtr hWnd);
        [System.Runtime.InteropServices.DllImport("user32.dll ")]
        static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
        // 添加文字相关的私有字段
        private string text = "";
        private Color textColor = Color.Black;
        private Font font = new Font("SimSun", 9F);

        // 重写Text属性
        [BrowsableAttribute(true)]
        [BindableAttribute(true)]
        public override string Text
        {
            get { return text; }
            set { text = value; this.Invalidate(); }
        }

        // 文字颜色
        public Color TextColor
        {
            get { return textColor; }
            set { textColor = value; this.Invalidate(); }
        }

        // 重写Font属性
        [BrowsableAttribute(true)]
        public override Font Font
        {
            get { return font; }
            set { font = value; this.Invalidate(); }
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            if (m.Msg == 0xf || m.Msg == 0x133)
            {
                //拦截系统消息,获得当前控件进程以便重绘。  
                //一些控件(如TextBox、Button等)是由系统进程绘制,重载OnPaint方法将不起作用.  
                //所有这里并没有使用重载OnPaint方法绘制TextBox边框。  
                //MSDN:重写   OnPaint   将禁止修改所有控件的外观。  
                //那些由   Windows   完成其所有绘图的控件(例如   Textbox)从不调用它们的   OnPaint   方法,  
                //因此将永远不会使用自定义代码。请参见您要修改的特定控件的文档,  
                //查看   OnPaint   方法是否可用。如果某个控件未将   OnPaint   作为成员方法列出,  
                //则您无法通过重写此方法改变其外观。  
                //MSDN:要了解可用的   Message.Msg、Message.LParam   和   Message.WParam   值,  
                //请参考位于   MSDN   Library   中的   Platform   SDK   文档参考。可在   Platform   SDK(“Core   SDK”一节)  
                //下载中包含的   windows.h   头文件中找到实际常数值,该文件也可在   MSDN   上找到。  

                IntPtr hDC = GetWindowDC(m.HWnd);
                if (hDC.ToInt32() == 0)
                {
                    return;
                }

                //base.OnPaint(e);

                System.Drawing.Graphics g = Graphics.FromHdc(hDC);
                SolidBrush brush = new SolidBrush(textColor);

                SizeF size = g.MeasureString(text, font);
                float x = (this.Width - size.Width) / 2;
                float y = (this.Height - size.Height) / 2;
                g.DrawString(Text, font, brush, x, y);
                // 下面的显示方法与上面的方法有相同的效果
                //StringFormat format = new StringFormat();
                //format.Alignment = StringAlignment.Center;
                //format.LineAlignment = StringAlignment.Center;
                //g.DrawString(text, font, brush, this.ClientRectangle, format);

                //返回结果
                m.Result = IntPtr.Zero;
                //释放
                ReleaseDC(m.HWnd, hDC);
            }
        }
    }

}

#6


对于着重于实际业务系统开发的人,我建议学点高级的 WPF 下的 XAML 酷设计,拒绝“玩儿” GDI 等东西。

#7


引用 4 楼 sp1234 的回复:
这可真是编程悲剧。你打算每次加一个功能,都破坏一次 WndProc ?

请问应该怎么改比较好?我刚学.net没几天。

#8


引用 6 楼 sp1234 的回复:
对于着重于实际业务系统开发的人,我建议学点高级的 WPF 下的 XAML 酷设计,拒绝“玩儿” GDI 等东西。

我是业余的,不是程序员。上学时学的C语言,后自学MFC和VB用来做些小工具。真要高级点的,我自学了Qt的一点皮毛。
想请教您一下,在WinForm里,这个问题如何解决比较合理?