標準 ListBox 僅支援單一字型/顏色,無法顯示圖示。專案需求:檔案傳輸清單顯示圖示 (檔案類型) 並依狀態 (傳輸中/完成/錯誤) 以不同顏色標示。
ObjectCollection/SelectedObjectCollection,提供強型別 Add/Insert/Remove 並同步 UI。ListBox,設定 DrawMode = OwnerDrawFixed,覆寫 OnDrawItem 繪製圖示與彩色文字。
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)
}
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
}
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() 觸發重繪,實現即時視覺更新。DrawMode.OwnerDrawVariable 實作可變項目高度 (覆寫 OnMeasureItem)。BackColor、Font、Tag 等屬性於 ListBoxExItem。IListBoxExRenderer 介面,支援主題切換。透過 OwnerDrawFixed、強型別項目模型、集合包裝與 ImageList,ListBoxEx 在保留標準 ListBox 所有功能 (選取、鍵盤導航、資料繫結) 的前提下,輕鬆實現圖示與多色文字,且設計工具支援完整,可直接拖曳使用。