首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

C# 自訂 ListBox 控制項:OwnerDraw 實作圖示與多色文字

     編輯:關於C語言

動機

標準 ListBox 僅支援單一字型/顏色,無法顯示圖示。專案需求:檔案傳輸清單顯示圖示 (檔案類型) 並依狀態 (傳輸中/完成/錯誤) 以不同顏色標示。

架構設計

  1. ListBoxExItem:單一項目資料模型 (文字、圖示索引、前景色)。
  2. ListBoxExItemCollection / SelectedListBoxExItemCollection:包裝基底 ObjectCollection/SelectedObjectCollection,提供強型別 Add/Insert/Remove 並同步 UI。
  3. ListBoxEx:繼承 ListBox,設定 DrawMode = OwnerDrawFixed,覆寫 OnDrawItem 繪製圖示與彩色文字。

核心類別實作

1. 項目模型


public class ListBoxExItem
{
    public string Text { get; set; }
    public int ImageIndex { get; set; } = -1;
    public Color ForeColor { get; set; } = SystemColors.WindowText;

    private ListBoxEx _parent;
    internal ListBoxEx Parent
    {
        get => _parent;
        set { _parent = value; }
    }

    // 顏色變更時觸發重繪
    public Color ForeColor
    {
        get => _foreColor;
        set { _foreColor = value; _parent?.Invalidate(); }
    }
    private Color _foreColor = SystemColors.WindowText;

    public override string ToString() => Text; // 基底 ListBox 以 ToString() 顯示 (若非 OwnerDraw)
}

2. 強型別集合 (包裝模式)


public class ListBoxExItemCollection : IList, ICollection, IEnumerable
{
    private readonly ListBox.ObjectCollection _inner;
    public ListBoxExItemCollection(ListBox.ObjectCollection inner) => _inner = inner;

    public int Add(ListBoxExItem item) => _inner.Add(item);
    public void Insert(int index, ListBoxExItem item) => _inner.Insert(index, item);
    public void Remove(ListBoxExItem item) => _inner.Remove(item);
    public ListBoxExItem this[int index] => (ListBoxExItem)_inner[index];
    // ... 實作 IList/ICollection/IEnumerable 介面其餘成員 (委派給 _inner) ...
}

public class SelectedListBoxExItemCollection : IList, ICollection, IEnumerable
{
    private readonly ListBox.SelectedObjectCollection _inner;
    public SelectedListBoxExItemCollection(ListBox.SelectedObjectCollection inner) => _inner = inner;
    // 同樣委派實作,索引子回傳 ListBoxExItem
}

3. ListBoxEx 控制項


public class ListBoxEx : ListBox
{
    private ListBoxExItemCollection _items;
    private ImageList _imageList;

    public ListBoxEx()
    {
        DrawMode = DrawMode.OwnerDrawFixed; // 固定項目高度,由我們繪製
        _items = new ListBoxExItemCollection(base.Items);
    }

    // 覆寫 Items 屬性回傳強型別集合
    public new ListBoxExItemCollection Items => _items;

    public new ListBoxExItem SelectedItem => base.SelectedItem as ListBoxExItem;

    public new SelectedListBoxExItemCollection SelectedItems =>
        new SelectedListBoxExItemCollection(base.SelectedItems);

    // 圖示清單屬性
    public ImageList ImageList
    {
        get => _imageList;
        set { _imageList = value; Invalidate(); }
    }

    // 核心繪製邏輯
    protected override void OnDrawItem(DrawItemEventArgs e)
    {
        e.DrawBackground();
        e.DrawFocusRectangle();

        if (e.Index < 0 || e.Index >= Items.Count) { base.OnDrawItem(e); return; }

        var item = Items[e.Index];
        Rectangle bounds = e.Bounds;
        int xOffset = 0;

        // 1. 繪製圖示 (若有 ImageList 且 ImageIndex 有效)
        if (_imageList != null && item.ImageIndex >= 0 && item.ImageIndex < _imageList.Images.Count)
        {
            int imgSize = bounds.Height; // 正方形圖示
            _imageList.Draw(e.Graphics, bounds.Left, bounds.Top, imgSize, imgSize, item.ImageIndex);
            xOffset += imgSize;
        }

        // 2. 繪製文字 (使用項目指定的 ForeColor)
        using (var brush = new SolidBrush(item.ForeColor))
        {
            e.Graphics.DrawString(item.Text, e.Font, brush, bounds.Left + xOffset, bounds.Top);
        }

        base.OnDrawItem(e);
    }
}

使用範例


// 表單載入
var imgList = new ImageList { ImageSize = new Size(16,16) };
imgList.Images.Add(Properties.Resources.FileIcon);     // 索引 0
imgList.Images.Add(Properties.Resources.FolderIcon);   // 索引 1
imgList.Images.Add(Properties.Resources.ErrorIcon);    // 索引 2
listBoxEx1.ImageList = imgList;

// 新增項目
listBoxEx1.Items.Add(new ListBoxExItem { Text = "Document.pdf", ImageIndex = 0, ForeColor = Color.Black });
listBoxEx1.Items.Add(new ListBoxExItem { Text = "Photos", ImageIndex = 1, ForeColor = Color.Blue });
var errItem = new ListBoxExItem { Text = "Failed.zip", ImageIndex = 2, ForeColor = Color.Red };
listBoxEx1.Items.Add(errItem);

// 執行緒中更新狀態 (需 Invoke)
errItem.ForeColor = Color.Green; // 自動觸發 Invalidate 重繪
errItem.Text = "Failed.zip (Retry OK)";

關鍵實作細節

  • DrawMode.OwnerDrawFixed:項目高度固定 (等於 ItemHeight),OnDrawItem 依序繪製每個可見項目。
  • ImageList.Draw 參數:Graphics, x, y, width, height, index 可精確控制圖示大小位置。
  • SolidBrush 以 using 釋放,避免 GDI 物件洩漏。
  • 集合包裝模式:不複製資料,直接操作基底 ObjectCollection,確保 UI 同步、效能最佳。
  • 項目屬性變更 (如 ForeColor) 呼叫 Parent.Invalidate() 觸發重繪,實現即時視覺更新。

擴充方向

  1. 支援 DrawMode.OwnerDrawVariable 實作可變項目高度 (覆寫 OnMeasureItem)。
  2. 加入 BackColor、Font、Tag 等屬性於 ListBoxExItem。
  3. 實作拖放排序、複選框、進度條等複合 UI。
  4. 將繪製邏輯抽離為 IListBoxExRenderer 介面,支援主題切換。

結語

透過 OwnerDrawFixed、強型別項目模型、集合包裝與 ImageList,ListBoxEx 在保留標準 ListBox 所有功能 (選取、鍵盤導航、資料繫結) 的前提下,輕鬆實現圖示與多色文字,且設計工具支援完整,可直接拖曳使用。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved