我正在努力為串列框創建自定義功能。我需要我的 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
}
}
有用的相關內容
串列框:Pinvoke LB_(列舉)
如何將多行文本添加到串列框項
uj5u.com熱心網友回復:
此示例類包含使 List 作為標準 ListBox 作業所需的調整,但具有問題中描述的增強功能。
另請參閱代碼中的注釋。
- TextRenderer.DrawText()替換
Graphics.DrawString():這將為呈現的串列項提供更自然的方面。無需使用抗鋸齒。 - OnMeasureItem也被覆寫,為專案提供自定義高度(可選的寬度 - 當嚴格要求時)。它設定為
ListBox.Font.Height 4(相當標準);根據需要進行修改。 OnDrawItem()已更正以處理自定義選擇顏色和標記專案的顏色。請注意,此方法每個 Item 呼叫一次,因此您不必每次都回圈整個集合,只需用正確的顏色繪制當前 Item。SetMarkedItem()修改為切換標記專案的狀態,以防您雙擊它兩次。WndProc被覆寫以攔截LB_RESETCONTENT(在ObjectCollection清除時發送)和LB_DELETESTRING(在洗掉專案時發送)。這樣做是為了在清除串列或洗掉標記的專案時重置標記的專案(否則專案將在不應該標記時保持標記)。Invalidate()設定屬性值時,請始終在無緣無故呼叫(或任何其他方法 - 或屬性設定器)之前驗證新值不等于舊值。- 一些小的改動,見代碼。
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;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/475070.html
