首页 > 解决方案 > 出现滚动条时如何正确应用修改后的 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 窗体编程很长时间了,所以我为此道歉,我显然做错了什么,但不知道是什么。

标签: c#winforms

解决方案


解决方案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);
            }
        }
    }

这看起来像这样

在此处输入图像描述


推荐阅读