禁用组合框中的特定项目

21

我有一个WinForms应用程序,想知道是否有更优雅的方法禁用ComboBox项,而不必为所有禁用值更改SelectedIndex属性为-1。

我一直在Google搜索,很多解决方案都涉及ASP.Net DropDownList,但是这个链接看起来很有前途。我想我可能需要构建自己的ComboBox控件,但在重新发明轮子之前,我想先在这里询问它是否可行。

更新

这是最终的解决方案,感谢Arif Eqbal:

//Add a Combobox to a form and name it comboBox1
//
    using System;
    using System.Drawing;
    using System.Windows.Forms;

    namespace WindowsFormsApplication6
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
                this.comboBox1.DrawMode = DrawMode.OwnerDrawFixed;
                this.comboBox1.DrawItem += new System.Windows.Forms.DrawItemEventHandler(this.comboBox1_DrawItem);
                this.comboBox1.SelectedIndexChanged += new System.EventHandler(this.comboBox1_SelectedIndexChanged);
            }

            private void Form1_Load(object sender, EventArgs e)
            {
                this.comboBox1.Items.Add("Test1");
                this.comboBox1.Items.Add("Test2");
                this.comboBox1.Items.Add("Test3");
                this.comboBox1.Items.Add("Test4");
                this.comboBox1.Items.Add("Test5");
                this.comboBox1.Items.Add("Test6");
                this.comboBox1.Items.Add("Test7");
            }

            Font myFont = new Font("Aerial", 10, FontStyle.Underline|FontStyle.Regular);
            Font myFont2 = new Font("Aerial", 10, FontStyle.Italic|FontStyle.Strikeout);

            private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
            {
                if (e.Index == 1 || e.Index == 4 || e.Index == 5)//We are disabling item based on Index, you can have your logic here
                {
                    e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), myFont2, Brushes.LightSlateGray, e.Bounds);
                }
                else
                {
                    e.DrawBackground();
                    e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), myFont, Brushes.Black, e.Bounds);
                    e.DrawFocusRectangle();
                }
            }

            void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
            {
                if (comboBox1.SelectedIndex == 1 || comboBox1.SelectedIndex == 4 || comboBox1.SelectedIndex == 5)
                    comboBox1.SelectedIndex = -1;
            }
        }
    }

3
ASP.NET不等同于WinForms,请勿在此处寻找。扩展基本的ComboBox并不是特别困难(通常是为了添加复选框、图标等),但我认为没有这样的标准支持。 - user166390
2
如果您真的想让用户感受到项目被禁用的感觉,那么您提到的链接是正确的选择。您可能希望将文本绘制为灰色,您可能不想显示选定的背景颜色等等,当然用户仍然可以选择该项目,因此您需要处理selectedIndexChanged并将selectedIndex设置为-1。但从视觉上来看,这样做会更具有暗示性。 - Arif Eqbal
3个回答

32

尝试这个...它是否符合您的目的:

我假设您有一个名为ComboBox1的组合框,并且您想禁用第二个项目,即索引为1的项目。

将组合框的DrawMode属性设置为OwnerDrawFixed,然后按下面所示处理这两个事件:

Font myFont = new Font("Aerial", 10, FontStyle.Regular);

private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{        
    if (e.Index == 1) //We are disabling item based on Index, you can have your logic here
    {
        e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), myFont, Brushes.LightGray, e.Bounds);
    }
    else
    {
        e.DrawBackground();
        e.Graphics.DrawString(comboBox1.Items[e.Index].ToString(), myFont, Brushes.Black, e.Bounds);
        e.DrawFocusRectangle();
    }
} 

void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    if (comboBox1.SelectedIndex == 1)
        comboBox1.SelectedIndex = -1;
}

19
这是基于Arif Eqbal的答案改进的:
  • 重用ComboBox中的Font,而不是创建新的(这样如果您在设计器中更改它,就不必更新代码)
  • 重用默认的SystemBrushes(以匹配您的主题;但如果手动更改ComboBox中使用的颜色,则不起作用)
  • 对于禁用的项目,我不得不重新绘制背景,否则每次重新绘制灰色项目时,其颜色会越来越接近黑色
  • 创建一个专门的IsItemDisabled方法以避免复制/粘贴
// Don't forget to change DrawMode, else the DrawItem event won't be called.
// this.comboBox1.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawFixed;

private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
    ComboBox comboBox = (ComboBox)sender;

    if (IsItemDisabled(e.Index))
    {
        // NOTE we must draw the background or else each time we hover over the text it will be redrawn and its color will get darker and darker.
        e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
        e.Graphics.DrawString(comboBox.Items[e.Index].ToString(), comboBox.Font, SystemBrushes.GrayText, e.Bounds);
    }
    else
    {
        e.DrawBackground();

        // Using winwaed's advice for selected items:
        // Set the brush according to whether the item is selected or not
        Brush brush = ( (e.State & DrawItemState.Selected) > 0) ? SystemBrushes.HighlightText : SystemBrushes.ControlText;
        e.Graphics.DrawString(comboBox.Items[e.Index].ToString(), comboBox.Font, brush, e.Bounds);

        e.DrawFocusRectangle();
    }
}

void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
    if (IsItemDisabled(comboBox1.SelectedIndex))
        comboBox1.SelectedIndex = -1;
}

bool IsItemDisabled(int index)
{
    // We are disabling item based on Index, you can have your logic here
    return index % 2 == 1;
}

@cool_php 我已经有一段时间没有使用那段代码了。代码开头的注释不足以说明问题吗?比如使用 OwnerDrawFixed - user276648

5
这里是进一步的修改。上述解决方案存在问题,因为选定的项目不可见,原因在于字体前景和背景选择都很暗。因此,应根据e.State的值设置字体。
    private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
    {
        ComboBox comboBox = (ComboBox)sender;
        if (e.Index >= 0)
        {
            if (IsItemDisabled(e.Index))
            {
                e.Graphics.FillRectangle(SystemBrushes.Window, e.Bounds);
                e.Graphics.DrawString(comboBox.Items[e.Index].ToString(), comboBox.Font, Brushes.LightSlateGray, e.Bounds);
            }
            else
            {
                e.DrawBackground();

                // Set the brush according to whether the item is selected or not
                Brush br = ( (e.State & DrawItemState.Selected) > 0) ? SystemBrushes.HighlightText : SystemBrushes.ControlText;

                e.Graphics.DrawString(comboBox.Items[e.Index].ToString(), comboBox.Font, br, e.Bounds);
                e.DrawFocusRectangle();
            }
        }
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接