我正在为列表框创建自定义功能。我需要我的列表框来更改特定(“标记”)项的前景色。不要与选定项混淆。
所需功能:假设ListBox集合包含多个文件名。
双击项目时;项索引和对象被存储到两个变量(索引和对象)中。
然后,这些变量将用于设置项的ForeColor(当列表框项为Unselected时)。
其余项目和项目矩形应使用默认属性绘制(在这种情况下,它们有自己的颜色属性,以允许进一步自定义)。
我的问题:
- 负载时未涂漆
- 用“奇怪的字符”绘制字符串
- 选择项目时;我在画“标记”项目。
我真的很困惑。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
}
}
有用的相关内容
ListBox : Pinvoke LB_(Enums)
How to add multiline Text to a ListBox item
1条答案
按热度按时间h7wcgrx31#
这个示例类包含使List作为标准ListBox工作所需的调整,但具有问题中描述的 * 增强 *。
另请参阅程式码中的注解。
Graphics.DrawString()
:这会给呈现的列表项一个更自然的外观。2不需要使用抗锯齿。ListBox.Font.Height + 4
(非常标准);根据需要进行修改。OnDrawItem()
已更正为处理自定义Selection颜色和已标记Item的颜色。请注意,此方法针对每个Item调用一次,因此您不必每次都循环整个集合,只需使用正确的颜色绘制当前Item即可。SetMarkedItem()
已修改为切换已标记项目的状态,以防您双击它两次。WndProc
被重写为 interceptLB_RESETCONTENT
(在清除ObjectCollection
时发送)和LB_DELETESTRING
(在移除项目时发送)。这样做是为了在清除列表或移除标记的项目时重置标记的项目(否则项目将在不应标记时保持标记)。Invalidate()
(或任何其他方法-或属性setter)之前,请始终验证新值不等于旧值。