---
# System prepended metadata

title: VS自訂有百分比(%)的ProgressBar元件
tags: [控制項, C#, Visual Studio]

---

# VS自訂有百分比(%)的ProgressBar元件
工作剛好需要用到「顯示百分比(%)的ProgressBar」，但VS原生ProgressBar沒有這個功能。
好險網路上已經有大佬教學，這邊簡單紀錄一下作法，以及可能遇到的問題解法。

>[!Tip] 關鍵步驟  
>#### 1. 繼承(Inheritance) : 繼承原控制項  
>#### 2. 覆寫(Override) : 覆寫OnPaint事件  
>#### 3. 繪製(DrawString) : 繪製文字

## ✔程式碼
```csharp=
public class ProgressBarWithText : ProgressBar
{
    public ProgressBarWithText() {
        SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
    }

    protected override void OnPaint(PaintEventArgs e) {
        Rectangle rect = ClientRectangle;
        Graphics g = e.Graphics;

        ProgressBarRenderer.DrawHorizontalBar(g, rect);
        rect.Inflate(-3, -3);
        if (Value > 0) {
            var clip = new Rectangle(rect.X, rect.Y, (int)((float)Value / Maximum * rect.Width), rect.Height);
            ProgressBarRenderer.DrawHorizontalChunks(g, clip);
        }

        string text = string.Format("{0}%", Value * 100 / Maximum); ;
        using (var font = new Font("Tahoma", 8, FontStyle.Bold)) {
            SizeF sz = g.MeasureString(text, font);
            var location = new PointF(rect.Width / 2 - sz.Width / 2, rect.Height / 2 - sz.Height / 2 + 2);
            g.DrawString(text, font, Brushes.Black, location);
        }
    }
}
```

### ✅程式說明
- 第1行，`public class ProgressBarWithText : ProgressBar`
    - 定義類別，並繼承內建的`ProgressBar`類別，這樣才可沿用原有功能。
    - 這裡定義的類別名稱，會在元件製作好後，顯示在工具箱。
- 第3~5行，在`ProgressBarWithText`的建構式，使用`SetStyle()`設定行為權限。
    - `SetStyle()`方法，用於設定開發者「修改控制項的繪製和行為」。
    - `ControlStyles.UserPaint`，表示會使用此類別定義的繪製事件`OnPaint`，而不是父類別`ProgressBar`的`OnPaint`。
    - `ControlStyles.AllPaintingInWmPaint`，減少繪製圖案時的閃爍。
    - `ControlStyles.OptimizedDoubleBuffer`，設定DoubleBuffer雙重緩衝。
    - 最後的`true`，代表開啟前面提到的行為或權限。
- 第7~24行，覆寫`OnPaint`事件。
    - 第22行`DrawString()`是關鍵，用於在元件的`Graphic`上繪製文字。
    - `DrawString(字串, 字型, 文字顏色, 繪製位置);`
- 專案編譯後，元件就會出現在工具箱。
![image](https://hackmd.io/_uploads/H11LwV39ke.png) &emsp; ![image](https://hackmd.io/_uploads/ByTQFr3ckx.png)

### 📌其它說明
- `ProgressBarWithText`類別要寫在哪?
    - 因為我只是做一個小工具，所以是直接寫在`MainForm`的`.cs`檔，省時省力。
    - 如果是大型專案，或是要製作給其他人使用，通常:
        - VS有專門撰寫元件的方案，完成後會產生控制項組件，可以在VS裡加入該組件。
        - 寫成DLL來引用。
- `SetStyle()`不加`AllPaintingInWmPaint`和`OptimizedDoubleBuffer`會怎樣?
    - 不知道，大神怎麼寫，我就怎麼~~抄~~寫。
    - 因為我拿掉也感覺不出差異，如果有人知道也可以跟我說🥺。

## 可能遇到的問題
如果跟我一樣直接寫在`MainForm`，某天偶然開啟設計頁面，出現這個錯誤。

`找不到類型'XXXXX.ProgressBarWithText'。請確認已經參考包含此類型的組件。`
![image](https://hackmd.io/_uploads/S1vdoV25yl.png)

❌ **不是製作的元件壞掉了**

⭕ **原因是Visual Studio本身是一個32位元的應用程式。**
![image](https://hackmd.io/_uploads/SkfoHSn9Je.png)
- 所以Visual Studio工具箱，也只能讀取使用`x86`編譯產生的元件。
- 如果將`MainForm`切換到`x64`編譯，因為`ProgressBarWithText`剛好寫在`MainForm`，所以也一起被編譯成`x64`了。
- 這樣開啟設計頁面，因為VS讀不到`x64`編譯的元件，所以就跳出錯誤。
(不一定編譯完立刻跳錯，有可能重開專案後才會跳錯)
- 但實際上專案還是可以正常編譯，也可以正常顯示/使用元件。

✅ **解決辦法**
如果是小工具，想偷懶寫在`MainForm`。
- 若沒有位元要求，就直接將專案切換成`AnyCPU`即可。
- 若必須編譯成`x64`，那就在設計頁面時，先切換到`x86`編譯，等要繼續寫程式再切回`x64`環境。

## 參考資料
[C#下实现在ProgressBar控件上添加百分比](https://blog.csdn.net/zhuimengshizhe87/article/details/20640157)
[WinForm重绘进度条ProgressBar显示百分比进度](http://www.windite.com/article/details/j0nobe12)
[X64下编译UserControl问题：未能加载工具箱项UserControl，将从工具箱中删除](https://blog.csdn.net/ghhong1988/article/details/106021251)