如何为ListBox制作透明背景?
How to make transparent background to ListBox?
我有一个 c# WinForms 应用程序,背景中有一张图片。我将一个列表框放在 window 上,我想将 BackColor
更改为对我的列表框透明。但是ListBox不支持透明BackColor
。我能为它做什么?
你可以做的应该是这样的:
比如说,我们要创建一个名为TansparentListBox
的透明列表框。所以我们需要从ListBox
控件中派生出一个新的class,设置一些控件样式使其双缓冲,防止绘制背景,说一下我们自己来绘制:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.ComponentModel;
[ToolboxBitmap(typeof(ListBox)),
DesignerCategory("")]
public class TransparentListBox : ListBox
{
public TransparentListBox() : base()
{
SetStyle(
ControlStyles.Opaque |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer, true);
DrawMode = DrawMode.OwnerDrawFixed;
}
//...
现在我们需要覆盖 OnPaint
和 OnDrawItem
事件来进行绘制。 DrawThemeParentBackground 函数需要复制父级的背景,只是我们控件的区域。另外我们还有一个新成员,SelectionBackColor
属性,选中项的背景颜色:
//...
public Color SelectionBackColor { get; set; } = Color.DarkOrange;
[DllImport("uxtheme", ExactSpelling = true)]
private extern static int DrawThemeParentBackground(
IntPtr hWnd,
IntPtr hdc,
ref Rectangle pRect
);
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
var rec = ClientRectangle;
IntPtr hdc = g.GetHdc();
DrawThemeParentBackground(this.Handle, hdc, ref rec);
g.ReleaseHdc(hdc);
using (Region reg = new Region(e.ClipRectangle))
{
if (Items.Count > 0)
{
for (int i = 0; i < Items.Count; i++)
{
rec = GetItemRectangle(i);
if (e.ClipRectangle.IntersectsWith(rec))
{
if ((SelectionMode == SelectionMode.One && SelectedIndex == i) ||
(SelectionMode == SelectionMode.MultiSimple && SelectedIndices.Contains(i)) ||
(SelectionMode == SelectionMode.MultiExtended && SelectedIndices.Contains(i)))
OnDrawItem(new DrawItemEventArgs(g, Font, rec, i, DrawItemState.Selected, ForeColor, BackColor));
else
OnDrawItem(new DrawItemEventArgs(g, Font, rec, i, DrawItemState.Default, ForeColor, BackColor));
reg.Complement(rec);
}
}
}
}
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (e.Index < 0) return;
var rec = e.Bounds;
var g = e.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
using (SolidBrush sb = new SolidBrush(SelectionBackColor))
g.FillRectangle(sb, rec);
using (SolidBrush sb = new SolidBrush(ForeColor))
using (StringFormat sf = new StringFormat { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center })
g.DrawString(GetItemText(Items[e.Index]), Font, sb, rec, sf);
}
//...
困难的部分已经完成。最后,绘图应该在某些事件发生时刷新,否则会视觉混乱:
//...
protected override void OnSelectedIndexChanged(EventArgs e)
{
base.OnSelectedIndexChanged(e);
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
Invalidate();
}
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
Invalidate();
}
//...
此外,同时使用垂直和水平滚动条时需要刷新。这样做可以通过覆盖 WndProc
来实现,因为 ListBox 控件没有任何滚动事件。
//...
private const int WM_KILLFOCUS = 0x8;
private const int WM_VSCROLL = 0x115;
private const int WM_HSCROLL = 0x114;
protected override void WndProc(ref Message m)
{
if (m.Msg != WM_KILLFOCUS &&
(m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL))
Invalidate();
base.WndProc(ref m);
}
}
就是这样。拼图,重建并尝试。
我有一个 c# WinForms 应用程序,背景中有一张图片。我将一个列表框放在 window 上,我想将 BackColor
更改为对我的列表框透明。但是ListBox不支持透明BackColor
。我能为它做什么?
你可以做的应该是这样的:
比如说,我们要创建一个名为TansparentListBox
的透明列表框。所以我们需要从ListBox
控件中派生出一个新的class,设置一些控件样式使其双缓冲,防止绘制背景,说一下我们自己来绘制:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.ComponentModel;
[ToolboxBitmap(typeof(ListBox)),
DesignerCategory("")]
public class TransparentListBox : ListBox
{
public TransparentListBox() : base()
{
SetStyle(
ControlStyles.Opaque |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.ResizeRedraw |
ControlStyles.UserPaint |
ControlStyles.OptimizedDoubleBuffer, true);
DrawMode = DrawMode.OwnerDrawFixed;
}
//...
现在我们需要覆盖 OnPaint
和 OnDrawItem
事件来进行绘制。 DrawThemeParentBackground 函数需要复制父级的背景,只是我们控件的区域。另外我们还有一个新成员,SelectionBackColor
属性,选中项的背景颜色:
//...
public Color SelectionBackColor { get; set; } = Color.DarkOrange;
[DllImport("uxtheme", ExactSpelling = true)]
private extern static int DrawThemeParentBackground(
IntPtr hWnd,
IntPtr hdc,
ref Rectangle pRect
);
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
var rec = ClientRectangle;
IntPtr hdc = g.GetHdc();
DrawThemeParentBackground(this.Handle, hdc, ref rec);
g.ReleaseHdc(hdc);
using (Region reg = new Region(e.ClipRectangle))
{
if (Items.Count > 0)
{
for (int i = 0; i < Items.Count; i++)
{
rec = GetItemRectangle(i);
if (e.ClipRectangle.IntersectsWith(rec))
{
if ((SelectionMode == SelectionMode.One && SelectedIndex == i) ||
(SelectionMode == SelectionMode.MultiSimple && SelectedIndices.Contains(i)) ||
(SelectionMode == SelectionMode.MultiExtended && SelectedIndices.Contains(i)))
OnDrawItem(new DrawItemEventArgs(g, Font, rec, i, DrawItemState.Selected, ForeColor, BackColor));
else
OnDrawItem(new DrawItemEventArgs(g, Font, rec, i, DrawItemState.Default, ForeColor, BackColor));
reg.Complement(rec);
}
}
}
}
}
protected override void OnDrawItem(DrawItemEventArgs e)
{
if (e.Index < 0) return;
var rec = e.Bounds;
var g = e.Graphics;
g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
using (SolidBrush sb = new SolidBrush(SelectionBackColor))
g.FillRectangle(sb, rec);
using (SolidBrush sb = new SolidBrush(ForeColor))
using (StringFormat sf = new StringFormat { Alignment = StringAlignment.Near, LineAlignment = StringAlignment.Center })
g.DrawString(GetItemText(Items[e.Index]), Font, sb, rec, sf);
}
//...
困难的部分已经完成。最后,绘图应该在某些事件发生时刷新,否则会视觉混乱:
//...
protected override void OnSelectedIndexChanged(EventArgs e)
{
base.OnSelectedIndexChanged(e);
Invalidate();
}
protected override void OnMouseDown(MouseEventArgs e)
{
base.OnMouseDown(e);
Invalidate();
}
protected override void OnMouseWheel(MouseEventArgs e)
{
base.OnMouseWheel(e);
Invalidate();
}
//...
此外,同时使用垂直和水平滚动条时需要刷新。这样做可以通过覆盖 WndProc
来实现,因为 ListBox 控件没有任何滚动事件。
//...
private const int WM_KILLFOCUS = 0x8;
private const int WM_VSCROLL = 0x115;
private const int WM_HSCROLL = 0x114;
protected override void WndProc(ref Message m)
{
if (m.Msg != WM_KILLFOCUS &&
(m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL))
Invalidate();
base.WndProc(ref m);
}
}
就是这样。拼图,重建并尝试。