ListBox 更改特定项目的前景色
ListBox Change a Specific Item ForeColor
我正在努力为列表框创建自定义功能。
我需要我的 ListBox 来更改特定(“标记”)项目的前景色。
不要与所选项目混淆。
所需功能:
假设 ListBox 集合包含多个文件名。
当我双击一个项目时;该项目索引和对象存储在两个变量(索引和对象)中。
然后,这些变量将用于设置项目前景色(当未选择列表框项目时)。
Remaining Items 和 Item Rectangle 应使用默认属性绘制
(在这种情况下,它们有自己的颜色属性以允许进一步定制)。
我的问题:
- 加载时不绘画
- 字符串是用“奇怪的字符”绘制的
- 选择项目时;我正在绘制“已标记”项目。
我真的很困惑。 MSDN 文档不是很清楚如何实现这一点;也不是 DrawItem 事件发生的方式和时间。
我一直在考虑几种选择;但是删除了所有内容并返回到当前代码以尝试理解逻辑和行为。
第一次尝试代码(原始问题代码):
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Custom_Controls.Controls
{
internal class MyPlaylist : ListBox
{
public MyPlaylist()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
// Enable ListBox Customized Design
DrawMode = DrawMode.OwnerDrawVariable;
//SelectionMode = SelectionMode.MultiExtended;
}
#region <Custom Properties>
private int markedIndex = -1;
public int MarkedIndex
{
get { return markedIndex; }
set { markedIndex = value; Invalidate(); }
}
private object markedItem = string.Empty;
public object MarkedItem
{
get { return markedItem; }
set
{
markedItem = value;
Invalidate();
}
}
private Color markedItemForeColor = Color.Red;
public Color MarkedItemForeColor
{
get { return markedItemForeColor; }
set { markedItemForeColor = value; Invalidate(); }
}
private Color markedItemBackColor = Color.DimGray;
public Color MarkedItemBackColor
{
get { return markedItemBackColor; }
set { markedItemBackColor = value; Invalidate(); }
}
private Color selectionBackColor = Color.DeepSkyBlue;
public Color SelectionBackColor
{
get { return selectionBackColor; }
set { selectionBackColor = value; Invalidate(); }
}
private Color selectionForeColor = Color.White;
public Color SelectionForeColor
{
get { return selectionForeColor; }
set { selectionForeColor = value; Invalidate(); }
}
#endregion
protected override void OnDrawItem(DrawItemEventArgs e) // When Selected?
{
e.DrawBackground();
e.DrawFocusRectangle();
//// Improve Graphic Quality and Pixel Precision
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var defaultForeBrush = new SolidBrush(Color.White))
using (var markForeBrush = new SolidBrush(markedItemForeColor))
{
// Iterate over all the items
for (int i = 0; i < Items.Count; i++)
{
var item = Items[i];
// Draw "Marked" Item
if (i == markedIndex)
{
e.Graphics.DrawString(Items[markedIndex].ToString(), e.Font, markForeBrush, e.Bounds, StringFormat.GenericDefault);
}
// Draw Remaining Items
else
{
e.Graphics.DrawString(item.ToString(), e.Font, markForeBrush, e.Bounds, StringFormat.GenericDefault);
}
// Draw Selection Rectangle
// ...
}
}
}
#region <Overriden Events>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
}
protected override void OnDoubleClick(EventArgs e)
{
base.OnDoubleClick(e);
SetMarkedItem();
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
SetMarkedItem();
}
#region <Methods>
private void SetMarkedItem()
{
markedIndex = SelectedIndex;
markedItem = SelectedItem;
}
#endregion
}
}
我第二次尝试使用 Jimi 的帮助(当前代码)
变化:
- 我评论了 Pinvoke LB_ Enums 和 WndProc,因为我无法使其工作。
- 为了保持简单:我删除了三元运算符(但是我喜欢 Jimi 的代码用它们交替颜色的方式)。
- SetMarker() 已还原为以前的版本。标记的项目从来没有那样绘制过。
- 自定义属性没有为画笔提供它们的价值;因此他们被暂时删除了。
当前问题:
WndProc 确实需要重新实现以清除绘图(标记项);并可能重绘控件,以便尽快更新标记。
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Custom_Controls.Controls
{
internal class MyPlaylist : ListBox
{
#region <Constructor>
public MyPlaylist()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
DrawMode = DrawMode.OwnerDrawVariable;
BackColor = Color.FromArgb(255, 25, 25, 25);
ForeColor = Color.White;
BorderStyle = BorderStyle.FixedSingle;
}
#endregion
#region <Fields>
//private const int LB_RESETCONTENT = 0x0184;
//private const int LB_DELETESTRING = 0x0182;
//TextFormatFlags flags = TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.LeftAndRightPadding | TextFormatFlags.VerticalCenter;
#endregion
#region <Custom Properties>
// Tip: Always Verify that the new Values are Different from the Old Ones
private int markedIndex = -1;
public int MarkedIndex
{
get { return markedIndex; }
set
{
if (value != markedIndex)
{
markedIndex = value;
Invalidate();
}
}
}
// Read-only: just return the marked Item, set it using the Index only
public object MarkedItem
{
get { return Items[markedIndex]; }
}
#endregion
#region <Overriden Events>
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (Items.Count == 0) return;
// Draw Selection:
if (e.State.HasFlag(DrawItemState.Focus) || e.State.HasFlag(DrawItemState.Selected))
{
using (var brush = new SolidBrush(Color.FromArgb(255, 52, 52, 52)))
{
// Background Rectangle
e.Graphics.FillRectangle(brush, e.Bounds);
// Item Text : Marked Item
if (e.Index == markedIndex)
{
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, Color.Red, flags);
}
// Other Items (Except Marked)
else
{
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, Color.White, flags);
}
}
}
// Draw Unselected:
else
{
using (var brush = new SolidBrush(BackColor))
using (var markedBrush = new SolidBrush(Color.Khaki))
{
e.Graphics.FillRectangle(brush, e.Bounds);
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, Color.White, flags);
}
// Draw (Unselected) Marked Item
if (markedIndex > -1 && e.Index == markedIndex)
{
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, Color.Red, flags);
}
}
e.DrawFocusRectangle();
base.OnDrawItem(e);
}
// Set the Height of the Item (Width: only if needed).
// This is the Standard Value (Modify as required)
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
if (Items.Count > 0)
{
e.ItemHeight = Font.Height + 4; // 4 = Text vs Item Rectangle Margin
}
base.OnMeasureItem(e);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
}
protected override void OnDoubleClick(EventArgs e)
{
base.OnDoubleClick(e);
SetMarkedItem();
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
if (e.Button == MouseButtons.Left)
{
SetMarkedItem();
}
}
#endregion
#region <Methods>
/// <summary>
/// WndProc is Overridden in order to Intercept the LB_RESETCONTENT (sent when the ObjectCollection is cleared);<br/>
/// and the LB_DELETESTRING (sent when an Item is removed).
/// This is done to reset the marked Item when the list is cleared or the marked Item is removed (otherwise an Item will remain marked when it shouldn't).
/// </summary>
/// <param name="m"></param>
//protected override void WndProc(ref Message m)
//{
// switch (m.Msg)
// {
// // List Cleared
// case LB_RESETCONTENT:
// markedIndex = -1;
// break;
// // Item Deleted
// case LB_DELETESTRING:
// if (markedIndex == m.WParam.ToInt32())
// {
// markedIndex = -1;
// }
// break;
// }
//}
private void SetMarkedItem() // Current Block
{
markedIndex = SelectedIndex;
}
// Previous Code Line (By Jimmy; using Ternary Operator) <----------------------------------------
//private void SetMarkedItem() => MarkedIndex = markedIndex == SelectedIndex ? -1 : SelectedIndex;
#endregion
}
}
有用的相关内容
How to add multiline Text to a ListBox item
此示例 class 包含使列表作为标准列表框工作所需的调整,但具有问题中描述的 增强功能 。
另请参阅代码中的注释。
- TextRenderer.DrawText() 替换
Graphics.DrawString()
:这将为呈现的列表项提供更 自然的 方面。无需使用 anti-aliasing.
- OnMeasureItem 也被覆盖,为项目提供自定义高度(可选的宽度 - 当严格要求时)。它设置为
ListBox.Font.Height + 4
(相当标准);根据需要修改。
OnDrawItem()
已更正以处理自定义选择颜色和标记项目的颜色。请注意,此方法对每个 Item 调用一次,因此您不必每次都循环整个集合,只需将当前 Item 涂上正确的颜色即可。
SetMarkedItem()
被修改为切换标记项目的状态,以防你 double-click 它两次。
WndProc
被覆盖为 intercept LB_RESETCONTENT
(在清除 ObjectCollection
时发送)和 LB_DELETESTRING
(在清除 ObjectCollection
时发送一个项目被删除)。这样做是为了在清除列表或删除标记的项目时重置标记的项目(否则项目将在不应该标记的情况下保持标记)。
- 设置 属性 值时,在调用
Invalidate()
(或任何其他方法 - 或 属性 之前,请始终验证新值不等于旧值setter) 无缘无故。
- 一些小改动,见代码。
public class MyPlaylist : ListBox {
private const int LB_DELETESTRING = 0x0182;
private const int LB_RESETCONTENT = 0x0184;
TextFormatFlags flags = TextFormatFlags.PreserveGraphicsClipping |
TextFormatFlags.LeftAndRightPadding |
TextFormatFlags.VerticalCenter;
public MyPlaylist()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
DrawMode = DrawMode.OwnerDrawVariable;
}
protected override void WndProc(ref Message m)
{
switch (m.Msg) {
// List cleared
case LB_RESETCONTENT:
markedIndex = -1;
break;
// Item deleted
case LB_DELETESTRING:
if (markedIndex == m.WParam.ToInt32()) {
markedIndex = -1;
}
break;
}
base.WndProc(ref m);
}
private int markedIndex = -1;
public int MarkedIndex {
get => markedIndex;
set {
if (value != markedIndex) {
markedIndex = value;
Invalidate();
}
}
}
// Read-only: just return the marked Item, set it using the Index only
public object MarkedItem {
get => Items[markedIndex];
}
// Always verify that the new value is different from the old one
private Color markedItemForeColor = Color.Orange;
public Color MarkedItemForeColor {
get => markedItemForeColor;
set {
if (value != markedItemForeColor) {
markedItemForeColor = value;
Invalidate();
}
}
}
private Color markedItemBackColor = Color.DimGray;
public Color MarkedItemBackColor {
get => markedItemBackColor;
set {
if (value != markedItemBackColor) {
markedItemBackColor = value;
Invalidate();
}
}
}
private Color selectionBackColor = Color.DeepSkyBlue;
public Color SelectionBackColor {
get => selectionBackColor;
set {
if (value != selectionBackColor) {
selectionBackColor = value;
Invalidate();
}
}
}
private Color selectionForeColor = Color.White;
public Color SelectionForeColor {
get => selectionForeColor;
set {
if (value != selectionForeColor) {
selectionForeColor = value;
Invalidate();
}
}
}
// Use TextRenderer to draw the Items - no anti-aliasing needed
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (Items.Count == 0) return;
if (e.State.HasFlag(DrawItemState.Focus) || e.State.HasFlag(DrawItemState.Selected)) {
using (var brush = new SolidBrush(selectionBackColor)) {
e.Graphics.FillRectangle(brush, e.Bounds);
}
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, selectionForeColor, flags);
}
else {
var color = markedIndex != -1 && markedIndex == e.Index ? markedItemBackColor : BackColor;
using (var brush = new SolidBrush(color)) {
e.Graphics.FillRectangle(brush, e.Bounds);
}
var foreColor = markedIndex == e.Index ? markedItemForeColor : ForeColor;
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, foreColor, flags);
}
e.DrawFocusRectangle();
base.OnDrawItem(e);
}
// Set the Height (the Width only if needed) of the Item
// This is the standard value, modify as required
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
if (Items.Count > 0) {
e.ItemHeight = Font.Height + 4;
}
base.OnMeasureItem(e);
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) {
SetMarkedItem();
}
base.OnMouseDoubleClick(e);
}
private void SetMarkedItem() => MarkedIndex = markedIndex == SelectedIndex ? -1 : SelectedIndex;
}
我正在努力为列表框创建自定义功能。 我需要我的 ListBox 来更改特定(“标记”)项目的前景色。 不要与所选项目混淆。
所需功能:
假设 ListBox 集合包含多个文件名。
当我双击一个项目时;该项目索引和对象存储在两个变量(索引和对象)中。
然后,这些变量将用于设置项目前景色(当未选择列表框项目时)。
Remaining Items 和 Item Rectangle 应使用默认属性绘制
(在这种情况下,它们有自己的颜色属性以允许进一步定制)。
我的问题:
- 加载时不绘画
- 字符串是用“奇怪的字符”绘制的
- 选择项目时;我正在绘制“已标记”项目。
我真的很困惑。 MSDN 文档不是很清楚如何实现这一点;也不是 DrawItem 事件发生的方式和时间。
我一直在考虑几种选择;但是删除了所有内容并返回到当前代码以尝试理解逻辑和行为。
第一次尝试代码(原始问题代码):
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Custom_Controls.Controls
{
internal class MyPlaylist : ListBox
{
public MyPlaylist()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
// Enable ListBox Customized Design
DrawMode = DrawMode.OwnerDrawVariable;
//SelectionMode = SelectionMode.MultiExtended;
}
#region <Custom Properties>
private int markedIndex = -1;
public int MarkedIndex
{
get { return markedIndex; }
set { markedIndex = value; Invalidate(); }
}
private object markedItem = string.Empty;
public object MarkedItem
{
get { return markedItem; }
set
{
markedItem = value;
Invalidate();
}
}
private Color markedItemForeColor = Color.Red;
public Color MarkedItemForeColor
{
get { return markedItemForeColor; }
set { markedItemForeColor = value; Invalidate(); }
}
private Color markedItemBackColor = Color.DimGray;
public Color MarkedItemBackColor
{
get { return markedItemBackColor; }
set { markedItemBackColor = value; Invalidate(); }
}
private Color selectionBackColor = Color.DeepSkyBlue;
public Color SelectionBackColor
{
get { return selectionBackColor; }
set { selectionBackColor = value; Invalidate(); }
}
private Color selectionForeColor = Color.White;
public Color SelectionForeColor
{
get { return selectionForeColor; }
set { selectionForeColor = value; Invalidate(); }
}
#endregion
protected override void OnDrawItem(DrawItemEventArgs e) // When Selected?
{
e.DrawBackground();
e.DrawFocusRectangle();
//// Improve Graphic Quality and Pixel Precision
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var defaultForeBrush = new SolidBrush(Color.White))
using (var markForeBrush = new SolidBrush(markedItemForeColor))
{
// Iterate over all the items
for (int i = 0; i < Items.Count; i++)
{
var item = Items[i];
// Draw "Marked" Item
if (i == markedIndex)
{
e.Graphics.DrawString(Items[markedIndex].ToString(), e.Font, markForeBrush, e.Bounds, StringFormat.GenericDefault);
}
// Draw Remaining Items
else
{
e.Graphics.DrawString(item.ToString(), e.Font, markForeBrush, e.Bounds, StringFormat.GenericDefault);
}
// Draw Selection Rectangle
// ...
}
}
}
#region <Overriden Events>
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
}
protected override void OnDoubleClick(EventArgs e)
{
base.OnDoubleClick(e);
SetMarkedItem();
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
SetMarkedItem();
}
#region <Methods>
private void SetMarkedItem()
{
markedIndex = SelectedIndex;
markedItem = SelectedItem;
}
#endregion
}
}
我第二次尝试使用 Jimi 的帮助(当前代码)
变化:
- 我评论了 Pinvoke LB_ Enums 和 WndProc,因为我无法使其工作。
- 为了保持简单:我删除了三元运算符(但是我喜欢 Jimi 的代码用它们交替颜色的方式)。
- SetMarker() 已还原为以前的版本。标记的项目从来没有那样绘制过。
- 自定义属性没有为画笔提供它们的价值;因此他们被暂时删除了。
当前问题: WndProc 确实需要重新实现以清除绘图(标记项);并可能重绘控件,以便尽快更新标记。
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Custom_Controls.Controls
{
internal class MyPlaylist : ListBox
{
#region <Constructor>
public MyPlaylist()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
DrawMode = DrawMode.OwnerDrawVariable;
BackColor = Color.FromArgb(255, 25, 25, 25);
ForeColor = Color.White;
BorderStyle = BorderStyle.FixedSingle;
}
#endregion
#region <Fields>
//private const int LB_RESETCONTENT = 0x0184;
//private const int LB_DELETESTRING = 0x0182;
//TextFormatFlags flags = TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.LeftAndRightPadding | TextFormatFlags.VerticalCenter;
#endregion
#region <Custom Properties>
// Tip: Always Verify that the new Values are Different from the Old Ones
private int markedIndex = -1;
public int MarkedIndex
{
get { return markedIndex; }
set
{
if (value != markedIndex)
{
markedIndex = value;
Invalidate();
}
}
}
// Read-only: just return the marked Item, set it using the Index only
public object MarkedItem
{
get { return Items[markedIndex]; }
}
#endregion
#region <Overriden Events>
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (Items.Count == 0) return;
// Draw Selection:
if (e.State.HasFlag(DrawItemState.Focus) || e.State.HasFlag(DrawItemState.Selected))
{
using (var brush = new SolidBrush(Color.FromArgb(255, 52, 52, 52)))
{
// Background Rectangle
e.Graphics.FillRectangle(brush, e.Bounds);
// Item Text : Marked Item
if (e.Index == markedIndex)
{
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, Color.Red, flags);
}
// Other Items (Except Marked)
else
{
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, Color.White, flags);
}
}
}
// Draw Unselected:
else
{
using (var brush = new SolidBrush(BackColor))
using (var markedBrush = new SolidBrush(Color.Khaki))
{
e.Graphics.FillRectangle(brush, e.Bounds);
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, Color.White, flags);
}
// Draw (Unselected) Marked Item
if (markedIndex > -1 && e.Index == markedIndex)
{
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, Color.Red, flags);
}
}
e.DrawFocusRectangle();
base.OnDrawItem(e);
}
// Set the Height of the Item (Width: only if needed).
// This is the Standard Value (Modify as required)
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
if (Items.Count > 0)
{
e.ItemHeight = Font.Height + 4; // 4 = Text vs Item Rectangle Margin
}
base.OnMeasureItem(e);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
}
protected override void OnDoubleClick(EventArgs e)
{
base.OnDoubleClick(e);
SetMarkedItem();
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
base.OnMouseDoubleClick(e);
if (e.Button == MouseButtons.Left)
{
SetMarkedItem();
}
}
#endregion
#region <Methods>
/// <summary>
/// WndProc is Overridden in order to Intercept the LB_RESETCONTENT (sent when the ObjectCollection is cleared);<br/>
/// and the LB_DELETESTRING (sent when an Item is removed).
/// This is done to reset the marked Item when the list is cleared or the marked Item is removed (otherwise an Item will remain marked when it shouldn't).
/// </summary>
/// <param name="m"></param>
//protected override void WndProc(ref Message m)
//{
// switch (m.Msg)
// {
// // List Cleared
// case LB_RESETCONTENT:
// markedIndex = -1;
// break;
// // Item Deleted
// case LB_DELETESTRING:
// if (markedIndex == m.WParam.ToInt32())
// {
// markedIndex = -1;
// }
// break;
// }
//}
private void SetMarkedItem() // Current Block
{
markedIndex = SelectedIndex;
}
// Previous Code Line (By Jimmy; using Ternary Operator) <----------------------------------------
//private void SetMarkedItem() => MarkedIndex = markedIndex == SelectedIndex ? -1 : SelectedIndex;
#endregion
}
}
有用的相关内容
How to add multiline Text to a ListBox item
此示例 class 包含使列表作为标准列表框工作所需的调整,但具有问题中描述的 增强功能 。
另请参阅代码中的注释。
- TextRenderer.DrawText() 替换
Graphics.DrawString()
:这将为呈现的列表项提供更 自然的 方面。无需使用 anti-aliasing. - OnMeasureItem 也被覆盖,为项目提供自定义高度(可选的宽度 - 当严格要求时)。它设置为
ListBox.Font.Height + 4
(相当标准);根据需要修改。 OnDrawItem()
已更正以处理自定义选择颜色和标记项目的颜色。请注意,此方法对每个 Item 调用一次,因此您不必每次都循环整个集合,只需将当前 Item 涂上正确的颜色即可。SetMarkedItem()
被修改为切换标记项目的状态,以防你 double-click 它两次。WndProc
被覆盖为 interceptLB_RESETCONTENT
(在清除ObjectCollection
时发送)和LB_DELETESTRING
(在清除ObjectCollection
时发送一个项目被删除)。这样做是为了在清除列表或删除标记的项目时重置标记的项目(否则项目将在不应该标记的情况下保持标记)。- 设置 属性 值时,在调用
Invalidate()
(或任何其他方法 - 或 属性 之前,请始终验证新值不等于旧值setter) 无缘无故。 - 一些小改动,见代码。
public class MyPlaylist : ListBox {
private const int LB_DELETESTRING = 0x0182;
private const int LB_RESETCONTENT = 0x0184;
TextFormatFlags flags = TextFormatFlags.PreserveGraphicsClipping |
TextFormatFlags.LeftAndRightPadding |
TextFormatFlags.VerticalCenter;
public MyPlaylist()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
DrawMode = DrawMode.OwnerDrawVariable;
}
protected override void WndProc(ref Message m)
{
switch (m.Msg) {
// List cleared
case LB_RESETCONTENT:
markedIndex = -1;
break;
// Item deleted
case LB_DELETESTRING:
if (markedIndex == m.WParam.ToInt32()) {
markedIndex = -1;
}
break;
}
base.WndProc(ref m);
}
private int markedIndex = -1;
public int MarkedIndex {
get => markedIndex;
set {
if (value != markedIndex) {
markedIndex = value;
Invalidate();
}
}
}
// Read-only: just return the marked Item, set it using the Index only
public object MarkedItem {
get => Items[markedIndex];
}
// Always verify that the new value is different from the old one
private Color markedItemForeColor = Color.Orange;
public Color MarkedItemForeColor {
get => markedItemForeColor;
set {
if (value != markedItemForeColor) {
markedItemForeColor = value;
Invalidate();
}
}
}
private Color markedItemBackColor = Color.DimGray;
public Color MarkedItemBackColor {
get => markedItemBackColor;
set {
if (value != markedItemBackColor) {
markedItemBackColor = value;
Invalidate();
}
}
}
private Color selectionBackColor = Color.DeepSkyBlue;
public Color SelectionBackColor {
get => selectionBackColor;
set {
if (value != selectionBackColor) {
selectionBackColor = value;
Invalidate();
}
}
}
private Color selectionForeColor = Color.White;
public Color SelectionForeColor {
get => selectionForeColor;
set {
if (value != selectionForeColor) {
selectionForeColor = value;
Invalidate();
}
}
}
// Use TextRenderer to draw the Items - no anti-aliasing needed
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (Items.Count == 0) return;
if (e.State.HasFlag(DrawItemState.Focus) || e.State.HasFlag(DrawItemState.Selected)) {
using (var brush = new SolidBrush(selectionBackColor)) {
e.Graphics.FillRectangle(brush, e.Bounds);
}
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, selectionForeColor, flags);
}
else {
var color = markedIndex != -1 && markedIndex == e.Index ? markedItemBackColor : BackColor;
using (var brush = new SolidBrush(color)) {
e.Graphics.FillRectangle(brush, e.Bounds);
}
var foreColor = markedIndex == e.Index ? markedItemForeColor : ForeColor;
TextRenderer.DrawText(e.Graphics, GetItemText(Items[e.Index]), Font, e.Bounds, foreColor, flags);
}
e.DrawFocusRectangle();
base.OnDrawItem(e);
}
// Set the Height (the Width only if needed) of the Item
// This is the standard value, modify as required
protected override void OnMeasureItem(MeasureItemEventArgs e)
{
if (Items.Count > 0) {
e.ItemHeight = Font.Height + 4;
}
base.OnMeasureItem(e);
}
protected override void OnMouseDoubleClick(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left) {
SetMarkedItem();
}
base.OnMouseDoubleClick(e);
}
private void SetMarkedItem() => MarkedIndex = markedIndex == SelectedIndex ? -1 : SelectedIndex;
}