c# - 出现滚动条时如何正确应用修改后的 C# WinForm ListBox 边框颜色
问题描述
我被要求对 C# WinForms 应用程序进行一些现代化工作,我正在使用 VS2019 和 C#.Net 4.7.2。
项目所有者想要更改最初使用的所有旧版 Windows 控件的边框颜色。最初的想法是 - 使用 MetroIT 框架 - 将原始 Windows 控件保留在窗体设计器中,但通过定义扩展 MetroIT 等效项但更改设计器 InitializeComponent() 方法中声明的类类型的新类来覆盖它们。
这种方法并不能真正用作“直接”替代品,因为 MetroIT 控件往往是 Control 的子类,而现有代码期望旧的 Windows 属性/方法可用。
因此,我尝试重写 OnPaint 方法。这对 CheckBox 和 RadioButton 来说效果很好,但我现在正在努力让它为 ListBox 工作。以下是我所掌握的;正如我将解释的那样,这当然是不正确的,但感觉有点接近?
public class MyListBox : ListBox
{
public MyListBox()
{
SetStyle(ControlStyles.UserPaint, true);
this.DrawMode = DrawMode.OwnerDrawVariable;
BorderStyle = BorderStyle.None;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Brush textBrush = new SolidBrush(Color.Black);
float y = 1;
foreach (String strItem in Items)
{
e.Graphics.DrawString(strItem, DefaultFont, textBrush, 1, y);
y += DefaultFont.Height;
}
Rectangle borderRectangle = this.ClientRectangle;
ControlPaint.DrawBorder(e.Graphics, borderRectangle, Color.Blue, ButtonBorderStyle.Solid);
}
}
最初,ListBox 中没有数据,控件被正确绘制。
但是,一旦出现滚动条,基本的 Windows 代码就会将滚动条绘制在我的边框顶部而不是在它的内部(而未修改的 ListBox 会在顶部、底部和右侧重新绘制边框滚动条):
有没有办法更改我的代码,使边框围绕滚动条的边缘绘制而不是排除它?
我的代码错误的另一种方式是,一旦我开始滚动,边框绘制代码就会将自身应用于某些 ListBox 项:
有没有办法可以完成这个,还是因为无法修改基本滚动条而浪费我的时间?
谢谢
编辑1:
根据@GuidoG 的建议:
是的,要清楚,我真的只想将边框更改为蓝色。很高兴离开列表框项目,因为它们通常会被绘制而没有任何边框。
所以,为了实现这一点,我已经删除了你建议的代码来做 DrawItem,现在我只有绘制边框的代码 - 但显然我做错了什么,因为列表框现在已经恢复到看起来像标准的黑色边框。
所以这就是我现在所拥有的:
在我的 Dialog.Designer.cs InitializeComponent() 中:
this.FieldsListBox = new System.Windows.Forms.ListBox();
this.FieldsListBox.FormattingEnabled = true;
this.FieldsListBox.Location = new System.Drawing.Point(7, 98);
this.FieldsListBox.Name = "FieldsListBox";
this.FieldsListBox.Size = new System.Drawing.Size(188, 121);
this.FieldsListBox.Sorted = true;
this.FieldsListBox.TabIndex = 11;
this.FieldsListBox.SelectedIndexChanged += new System.EventHandler(this.FieldsListBox_SelectedIndexChanged);
在我的 Dialog.cs 表单声明中:
public SelectByAttributesDialog()
{
InitializeComponent();
BlueThemAll(this);
}
private void BlueThemAll(Control parent)
{
foreach (Control item in parent.Controls)
{
Blue(item);
if (item.HasChildren)
BlueThemAll(item);
}
}
private void Blue(Control control)
{
Debug.WriteLine("Blue: " + control.Name.ToString());
Rectangle r = new Rectangle(control.Left - 1, control.Top - 1, control.Width + 1, control.Height + 1);
using (Graphics g = control.Parent.CreateGraphics())
{
using (Pen selPen = new Pen(Color.Blue))
{
g.DrawRectangle(selPen, r);
}
}
}
我知道该对话框中的所有控件都调用了 Blue(),因为 Debug.WriteLine() 正在输出每个控件名称,但没有将边框更改为蓝色(当然也不是列表框)。
我已经离开 Windows 窗体编程很长时间了,所以我为此道歉,我显然做错了什么,但不知道是什么。
解决方案
解决方案1:让表单在所有内容周围绘制蓝色边框:
这意味着您不必对任何控件进行子类化,但是您可以在窗体上放置一些代码,这些代码将为您在每个控件周围绘制边框。
这是一个对我有用的例子
// method that draws a blue border around a control
private void Blue(Control control)
{
Rectangle r = new Rectangle(control.Left - 1, control.Top - 1, control.Width + 1, control.Height + 1);
using (Graphics g = control.Parent.CreateGraphics())
{
using (Pen selPen = new Pen(Color.Blue))
{
g.DrawRectangle(selPen, r);
}
}
}
// recursive method that finds all controls and call the method Blue on each found control
private void BlueThemAll(Control parent)
{
foreach (Control item in parent.Controls)
{
Blue(item);
if (item.HasChildren)
BlueThemAll(item);
}
}
在哪里称呼这个?
// draw the blue borders when the form is resized
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
panel3.Invalidate(); // on panel3 there is a control that moves position because it is anchored to the bottom
Update();
BlueThemAll(this);
}
// draw the borders when the form is moved, this is needed when the form moved off the screen and back
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
BlueThemAll(this);
}
// draw the borders for the first time
protected override void OnShown(EventArgs e)
{
base.OnShown(e);
BlueThemAll(this);
}
如果您还希望列表框的每个项目周围有蓝色边框:
要在列表框中的每个项目周围获得一些边框,请使用DrawItem
事件而不是绘制事件,也许这个链接会有所帮助。
我修改了代码以使其绘制蓝色边框
listBox1.DrawMode = DrawMode.OwnerDrawFixed;
listBox1.DrawItem += ListBox1_DrawItem;
private void ListBox1_DrawItem(object sender, DrawItemEventArgs e)
{
try
{
e.DrawBackground();
Brush myBrush = new SolidBrush(e.ForeColor);
Rectangle r = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width - 1, e.Bounds.Height);
using (Pen selPen = new Pen(Color.Blue))
{
e.Graphics.DrawRectangle(selPen, r);
}
e.Graphics.DrawString(((ListBox)sender).Items[e.Index].ToString(), e.Font, myBrush, e.Bounds, StringFormat.GenericDefault);
e.DrawFocusRectangle();
}
catch { }
}
它看起来像这样
解决方案 2:对列表框进行子类化:
如果您在子类列表框中需要它,那么您可以这样做。
但是,这仅在列表框周围至少有一个像素空间时才有效,因为要绕过滚动条,您必须在列表框的父级上绘制,而不是在列表框本身上。滚动条将始终在您在那里绘制的任何内容上绘制自己。
public class myListBox : ListBox
{
public myListBox(): base()
{
this.DrawMode = DrawMode.OwnerDrawFixed;
BorderStyle = BorderStyle.None;
this.DrawItem += MyListBox_DrawItem;
}
private void MyListBox_DrawItem(object sender, DrawItemEventArgs e)
{
e.DrawBackground();
Brush myBrush = new SolidBrush(e.ForeColor);
e.Graphics.DrawString(this.Items[e.Index].ToString(), e.Font, myBrush, e.Bounds, StringFormat.GenericDefault);
e.DrawFocusRectangle();
Rectangle r = new Rectangle(this.Left - 1, this.Top - 1, this.Width + 2, this.Height + 2);
using (Graphics g = this.Parent.CreateGraphics())
{
ControlPaint.DrawBorder(g, r, Color.Blue, ButtonBorderStyle.Solid);
}
}
}
这看起来像这样
推荐阅读
- python - 在字符串中的小写字母序列之前插入一个单词
- graphql - 流体Img组件在Link组件下时正在收缩
- react-native - 如何在应用程序中添加 react-native expo 用户登录
- ios - Xcode 12 错误:多个命令产生 Charts.framework
- python - 只运行一次 Airflow Dag
- c - 将 32 位地址分成几部分
- powershell - 用于在调度程序中搜索特定任务并删除此任务的脚本
- python - 休息 API Python 请求:响应 [400]
- flutter - PageView 容器不会限制最大宽度或高度抖动
- azure - Microsoft MIP JAVA SDK 示例错误:“无法识别的异常:profile_add_engine_async”