none
ProgressBar 實作問題 RRS feed

  • 問題

  • 您好 目前我實作了一個ProgressBar 在System.Windoes.Forms.ProgressBar類別下的Blocks功能方面是沒有問題的但是Marquee模式的效率很差動畫的驅動我是使用Timer來作 頻率大概是100電腦的顯示卡是9600 GT 的狀態下會出現很明顯的重繪白色區域 每2~3秒大概會出現一次但是我發現使用P4等級的舊式主機版內建顯示晶片來跑 出現的頻率也差不多圖畫的顯示方式是已BackgroundImage的方式顯示想請問有沒有比較好的繪圖機制呢?
        public partial class ProgressBar : UserControl
        {
    
            /// <summary>
    
            /// ProgressBar的百分比,Value必須介於0到100之間(含等於)。
    
            /// </summary>
    
            public int Value
    
            {
    
                get { return Percentage_; }
    
                set
    
                {
    
                    if (value <= 100 && value >= 0)
    
                    {
    
                        Percentage_ = value; ReDrawContorl();
    
                    }
    
                }
    
            }
    
            public int MarqueeSpeed { get { return ActionTime.Interval; } set { ActionTime.Interval = value; } }
    
            public int MarqueeSize { get { return MarqueeSize_; } set { MarqueeSize_ = value; } }
    
            public string Mode { get { return Mode_; } set { Mode_ = value; ModeSet(); } }
    
            string Mode_ = ProgressBarModes.Normal_Blocks;
    
            int Percentage_ = 50;
    
            int MarqueeSize_ = 30;
    
            Timer ActionTime = new Timer();
    
            public ProgressBar()
    
            {
    
                InitializeComponent();
    
                ActionTime.Tick += new EventHandler(ActionTime_OnTick);
    
                ActionTime.Interval = 100;
    
            }
    
            private void ModeSet()
    
            {
    
                if (Mode == ProgressBarModes.Normal_Marquee)
    
                {
    
                    ActionTime.Interval = MarqueeSpeed;
    
                    ActionTime.Enabled = true;
    
                }
    
                else
    
                {
    
                    ActionTime.Enabled = false;
    
                }
    
                ReDrawContorl();
    
            }
    
            void ActionTime_OnTick(object sender, EventArgs e)
    
            {
    
                ReDrawContorl();
    
            }
    
    
    
            private void ProgressBar_StyleByOptimist_Load(object sender, EventArgs e)
    
            {
    
                ReDrawContorl();
    
            }
    
    
    
            private void ProgressBar_StyleByOptimist_SizeChanged(object sender, EventArgs e)
    
            {
    
                BP.Dispose();
    
                BP = new Bitmap(this.Width, this.Height);
    
                ReDrawContorl();
    
            }
    
    
    
            int MarqueeIndex = 0;
    
    
    
            Bitmap BP = new Bitmap(1, 1);
    
            private void ReDrawContorl()
    
            {
    
                Graphics GG = null;
    
                if (Mode == ProgressBarModes.Normal_Blocks)
    
                {
    
                    BP.Dispose();
    
                    BP = new Bitmap(this.Width, this.Height);
    
                    GG = Graphics.FromImage(BP);
    
                    int TotalLine = (this.Width - 6) / 3;
    
                    double EveryDrawTimeMin = 100.0 / TotalLine;
    
                    for (int X = 1; X < (double)(Percentage_ / EveryDrawTimeMin) + 1; X++)
    
                    {
    
                        GG.DrawLine(Pens.White, new Point(X * 3, 4), new Point(X * 3, this.Height - 4));
    
                        GG.DrawLine(Pens.White, new Point(X * 3 + 1, 4), new Point(X * 3 + 1, this.Height - 4));
    
                    }
    
                    GG.DrawRectangle(Pens.White, new Rectangle(new Point(0, 0), new Size(this.Width - 1, this.Height - 1)));
    
                }
    
                if (Mode == ProgressBarModes.Normal_Marquee)
    
                {
    
                    BP.Dispose();
    
                    BP = new Bitmap(this.Width, this.Height);
    
                    GG = Graphics.FromImage(BP);
    
                    int TotalLine = (this.Width - 6) / 3;
    
                    double EveryDrawTimeMin = 100.0 / TotalLine;
    
                    int TEMP = MarqueeIndex;
    
                    for (int X = 0; X < (double)(MarqueeSize / EveryDrawTimeMin) + 1; X++)
    
                    {
    
                        if (TEMP * 3 > this.Width)
    
                        {
    
                            TEMP = 0;
    
                        }
    
                        GG.DrawLine(Pens.White, new Point(TEMP * 3, 4), new Point(TEMP * 3, this.Height - 4));
    
                        if (TEMP * 3 + 1 > this.Width)
    
                        {
    
                            TEMP = 0;
    
                        }
    
                        GG.DrawLine(Pens.White, new Point(TEMP * 3 + 1, 4), new Point(TEMP * 3 + 1, this.Height - 4));
    
                        TEMP++;
    
                    }
    
                    MarqueeIndex++;
    
                    if (MarqueeIndex * 3 > this.Width)
    
                        MarqueeIndex = 0;
    
                    GG.DrawRectangle(Pens.White, new Rectangle(new Point(0, 0), new Size(this.Width - 1, this.Height - 1)));
    
                }
    
                if (GG != null)
    
                    GG.Dispose();
    
                this.BackgroundImage = BP;
    
            }
    
        }
    
        public class ProgressBarModes
    
        {
    
            public static String Normal_Blocks { get { return "Normal_Blocks"; } set { } }
    
            public static String Normal_Marquee { get { return "Normal_Marquee"; } set { } }
    
        }
    
    
    2010年1月12日 上午 02:33

解答

所有回覆

  • HI,

    您可以參考這個例子:
    A marquee control in C#(http://www.codeproject.com/KB/miscctrl/csmarquee.aspx)
    2010年1月12日 上午 04:14
  • 確實改用Sleep機制來實作繪圖時機了

    但是我的主要問題是 在快速的BackGroundImage刷新過程中整個控制項會變成白色閃爍

    我希望可以將這個問題解決 希望各位高手提供一點方向


    .Net內建的ProgressBar的頻率似乎也是100 但是他不會有閃爍狀況出現

    想請教那大概是怎麼寫的呢
    2010年1月12日 上午 08:18
  • 分享一個我剛剛測試發現的結果

    我原本使用的是UserControl本身的BackGroundImage來顯示繪製好的Bitmap

    現在我改用PictureBox設定Dock = Fill 並用其BackGroundImage來顯示Bitmap

    這個狀態下閃爍的狀況有些許的降低 應該不是我的錯覺

    可是結果仍然很難讓人滿意= = 麻煩各位高手指點
    2010年1月12日 上午 08:28
  • Hi,

    直接用Control.CreateGraphics取得控制項的Graphics物件來畫會快很多。
    • 已標示為解答 Optimist9266 2010年1月12日 上午 10:30
    2010年1月12日 上午 09:24
  • 非常感謝

    速度確實是快到不行 就連For的繪圖痕跡都會顯示出來

    小弟想請問差別是什麼呢?

    是因為所有該控制項的繪圖作業最終都要送給Control本身的Graphics作執行

    所以我直接下指令給他比較快嗎?
    2010年1月12日 上午 10:33
  • Hi,

    你本來的方法是
    1.New Bitmap
    2.畫在Bitmap
    3.把Bitmap塞到PictureBox

    而改用CreateGraphics是
    1.CreateGraphics
    2.畫在Graphic上

    少了頻繁建立臨時用的Bitmap
    也少畫了一次動作
    在你本來的方法中
    2跟3步都是要畫圖的

    依其功能來看
    也許可以把本來的方法視為
    1.New Bitmap
    2.畫在Bitmap
    3.把Bitmap塞到PictureBox
    4.PictureBox內部使用Paint事件的參數取得Graphic
    5.PictureBox內部把塞進來的Bitmap畫到Graphic

    謙卑學習,持之以恆,才能不斷的Level Up http://www.dotblogs.com.tw/larrynung/
    2010年1月14日 上午 05:10
  • 因為Graphics物件是直接封裝GDI+介面,自然速度差很多...

    如果你不想在for裡面那種畫到一半的都顥示出來的話
    應該保留原本你暫存的Bitmap,等所有的圖都畫到暫存的Bitmap後
    再利用Graphics.DrawImage一次將圖畫上去
    2010年1月14日 上午 06:08
  • 好的 謝謝
    2010年1月15日 上午 10:15