Tooltip for each items in a combo box

2019-01-17 04:48发布

问题:

I just want to add a tooltip for each item in a combo box. i am using c#.net windows application.

There is no option like

combobox.items[1].tooltip();

Is there any way to add tooltip it ?

回答1:

There are actually a couple reasonable solutions to this question. An MSDN forum has a ComboBox Item highlight event post that contains two possibilities, one from nobugz and one from agrobler. Each of them provides code to subclass a ComboBox that is supposed to handle tool tips on individual items in the ComboBox's dropdown. Agrobler's solution looks more polished, in that he/she even includes some nice illustrations, but unfortunately it is not clear (at least to me) how to populate the crucial ToolTipMember property of the control.

Both of these solutions appear to allow arbitrary tooltips assigned to individual items. A more specific, but more common case, is where you simply want the tooltip to mirror the text of the item, when you know you may have items that are too long to fit the width of the ComboBox. In my own case, I have an instance of a ComboBox that holds complete file paths so it is easy to see where the contents could exceed the ComboBox's width.

Zhi-Xin Ye, in the MSDN forum post Windows Dropdown question, provides a solution that addresses this more specific problem and is much simpler. I reproduce the code here in its entirety. (Note that this code presupposes you have created a Form called Form1 and hooked up the load handler shown, and also added a ComboBox named comboBox1 and a tool tip handler toolTip1.)

private void Form1_Load(object sender, EventArgs e)
{
    this.comboBox1.DrawMode = DrawMode.OwnerDrawFixed;
    this.comboBox1.DrawItem += new DrawItemEventHandler(comboBox1_DrawItem);
}

void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
    string text = this.comboBox1.GetItemText(comboBox1.Items[e.Index]);
    e.DrawBackground();
    using (SolidBrush br = new SolidBrush(e.ForeColor))
    { e.Graphics.DrawString(text, e.Font, br, e.Bounds); }

    if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
    { this.toolTip1.Show(text, comboBox1, e.Bounds.Right, e.Bounds.Bottom); }
    else { this.toolTip1.Hide(comboBox1); }
    e.DrawFocusRectangle();
}

While simple and concise, this code does suffer from one defect (as is pointed out in a reply on the above MSDN thread): as you move the mouse (without clicking) from one dropdown item to the next, only every other one shows a persistent tooltip! The fix is only hinted at by yet another entry on that thread, so I thought it would be useful to provide the full, corrected code here:

private void Form1_Load(object sender, EventArgs e)
{
    comboBox1.DrawMode = DrawMode.OwnerDrawFixed;
    comboBox1.DrawItem += comboBox1_DrawItem;
    comboBox1.DropDownClosed += comboBox1_DropDownClosed;
}

private void comboBox1_DropDownClosed(object sender, EventArgs e)
{
    toolTip1.Hide(comboBox1);
}

private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
{
    if (e.Index < 0) { return; } // added this line thanks to Andrew's comment
    string text = comboBox1.GetItemText(comboBox1.Items[e.Index]);
    e.DrawBackground();
    using (SolidBrush br = new SolidBrush(e.ForeColor))
    { e.Graphics.DrawString(text, e.Font, br, e.Bounds); }
    if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
    { toolTip1.Show(text, comboBox1, e.Bounds.Right, e.Bounds.Bottom); }
    e.DrawFocusRectangle();
}

Besides removing a few redundant portions of code (e.g. the "this" qualifier) the primary difference is moving the toolTip1.Hide call into the DropDownClosed event handler. Taking it out of the DrawItem handler eliminates the defect mentioned above; but then you need to close it when the drop down closes, otherwise the last displayed tooltip will remain onscreen.

2012.07.31 Addendum

Just wanted to mention that I have since created a composite ComboBox that incorporates this tooltip capability so if you use my library you have no code to write at all. Just drag a ComboBoxWithTooltip onto the Visual Studio designer and you are done. Drill down to ComboBoxWithTooltip on my API page or download my open-source C# library to get started. (Note that the patch for the bug Andrew caught will be in release 1.1.04, due out soon.)



回答2:

private void comboBox1_SelectedIndexChanged(object sender, System.EventArgs e)
{
    ToolTip toolTip1 = new ToolTip();
    toolTip1.AutoPopDelay = 0;
    toolTip1.InitialDelay = 0;
    toolTip1.ReshowDelay = 0;
    toolTip1.ShowAlways = true;
    toolTip1.SetToolTip(this.comboBox1, comboBox1.Items[comboBox1.SelectedIndex].ToString()) ;
}


回答3:

My solution:

ToolTip toolTip = new ToolTip() { AutoPopDelay = 0, InitialDelay = 0, ReshowDelay = 0, ShowAlways = true, };
comboBox.DrawMode = DrawMode.OwnerDrawFixed;
comboBox.DrawItem += (s, e) =>
{
    e.DrawBackground();
    string text = comboBox.GetItemText(comboBox.Items[e.Index]);
    using (SolidBrush br = new SolidBrush(e.ForeColor))
        e.Graphics.DrawString(text, e.Font, br, e.Bounds);
    if ((e.State & DrawItemState.Selected) == DrawItemState.Selected && comboBox.DroppedDown)
        toolTip.Show(text, comboBox, e.Bounds.Right, e.Bounds.Bottom + 4);
    e.DrawFocusRectangle();
};
comboBox.DropDownClosed += (s, e) =>
    toolTip.Hide(comboBox);


回答4:

Building upon the solution from Michael Sorens (fixed a few bugs and added features). A few things this does:

  • It will display a preview of a file associated with the drop down (in this case a book title from an XML file, or you can add more descriptions in the tooltip, or display something completely different).
  • It does not display tooltip for the "0" positon in the drop down (I had a placeholder, but you can simply remove the e.index>0 in the second if statement).
  • It does not display the tooltip when the dropdown is CLOSED.

    private void comboBox1_DrawItem(object sender, DrawItemEventArgs e)
    {
        ComboBox comboBox1 = (ComboBox)sender;
        if (e.Index >= 0)
        {//Draws all items in drop down menu
            String text = comboBox1.GetItemText(comboBox1.Items[e.Index]);
            e.DrawBackground();
            using (SolidBrush br = new SolidBrush(e.ForeColor))
            {
                e.Graphics.DrawString(text, e.Font, br, e.Bounds);
            }
    
            if ((e.State & DrawItemState.Selected) == DrawItemState.Selected && e.Index > 0 && comboBox1.DroppedDown)
            {//Only draws tooltip when item 1+ are highlighted.  I had a "--" placeholder in the 0 position
                try
                {
                    XmlDocument doc;
                    XmlNode testNode;
                    doc = new XmlDocument();
                    String testXMLDoc = String.Format(@"{0}\{1}.xml", filePath, fileName);//global variables
                    String toolTip = "---Preview of File---";
                    doc.Load(testXMLDoc);
                    testNode = doc.SelectSingleNode("/Books");
                    if (testNode.HasChildNodes)
                    {
                        XmlNodeList nodeList = testNode.SelectNodes("Book");
                        foreach (XmlNode xmlNode in nodeList)
                        {
                            toolTip += "\r\n" + xmlNode.SelectSingleNode("Title").InnerXml;
                        }
                    }
                    this.toolTipHelp.Show(toolTip, comboBox1, e.Bounds.Right, e.Bounds.Bottom);
                }
                catch (Exception tp)
                {
                    Debug.WriteLine("Error in comboBox1 tooltip: " + tp);
                }
            }
            else
            {
                this.toolTipHelp.Hide(comboBox1);
            }
        }
        else
        {
            this.toolTipHelp.Hide(comboBox1);
        }
        e.DrawFocusRectangle();
    }
    


回答5:

You will need to create your own UserControl.

Having a ToolTip for each item in a combobox is an unusual requirement; perhaps you could use a 2 column combobox instead?



回答6:

If you are loading from a datasource, get the data into datatable and set the same to combobox. My datatable has three columns ID,NAME,DEFINITION. Below is my code :

InputQuery = "select * from ds_static_frequency";
        TempTable = UseFunc.GetData(InputQuery);

        cmbxUpdateFrequency.DataSource = TempTable;
        cmbxUpdateFrequency.DataTextField = "NAME";
        cmbxUpdateFrequency.DataValueField = "ID";
        cmbxUpdateFrequency.DataBind();

        foreach (DataRow dr in TempTable.Rows)
        {                
            int CurrentRow = Convert.ToInt32(dr["ID"].ToString());
            cmbxUpdateFrequency.Items[CurrentRow - 1].ToolTip = dr["Definition"].ToString();               
        }    


回答7:

My solution:

public class ToolTipComboBox: ComboBox
{
    #region Fields

    private ToolTip toolTip;
    private bool _tooltipVisible;
    private bool _dropDownOpen;
    #endregion

    #region Types

    [StructLayout(LayoutKind.Sequential)]
    // ReSharper disable once InconsistentNaming
    public struct COMBOBOXINFO
    {
        public Int32 cbSize;
        public RECT rcItem;
        public RECT rcButton;
        public ComboBoxButtonState buttonState;
        public IntPtr hwndCombo;
        public IntPtr hwndEdit;
        public IntPtr hwndList;
    }

    public enum ComboBoxButtonState
    {
        // ReSharper disable once UnusedMember.Global
        StateSystemNone = 0,
        // ReSharper disable once UnusedMember.Global
        StateSystemInvisible = 0x00008000,
        // ReSharper disable once UnusedMember.Global
        StateSystemPressed = 0x00000008
    }

    [DllImport("user32.dll")]
    public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
    [DllImport("user32.dll", SetLastError = true)]
    public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);

    #endregion

    #region Properties

    private IntPtr HwndCombo
    {
        get
        {
            COMBOBOXINFO pcbi = new COMBOBOXINFO();
            pcbi.cbSize = Marshal.SizeOf(pcbi);
            GetComboBoxInfo(Handle, ref pcbi);
            return pcbi.hwndCombo;
        }
    }

    private IntPtr HwndDropDown
    {
        get
        {
            COMBOBOXINFO pcbi = new COMBOBOXINFO();
            pcbi.cbSize = Marshal.SizeOf(pcbi);
            GetComboBoxInfo(Handle, ref pcbi);
            return pcbi.hwndList;
        }
    }

    [Browsable(false)]
    public new DrawMode DrawMode
    {
        get { return base.DrawMode; }
        set { base.DrawMode = value; }
    }

    #endregion

    #region ctor

    public ToolTipComboBox()
    {
        toolTip = new ToolTip
        {
            UseAnimation = false,
            UseFading = false
        };

        base.DrawMode = DrawMode.OwnerDrawFixed;
        DrawItem += OnDrawItem;
        DropDownClosed += OnDropDownClosed;
        DropDown += OnDropDown;
        MouseLeave += OnMouseLeave;
    }

    #endregion

    #region Methods

    private void OnDropDown(object sender, EventArgs e)
    {
        _dropDownOpen = true;
    }

    private void OnMouseLeave(object sender, EventArgs e)
    {
        ResetToolTip();
    }

    private void ShowToolTip(string text, int x, int y)
    {
        toolTip.Show(text, this, x, y);
        _tooltipVisible = true;
    }

    private void OnDrawItem(object sender, DrawItemEventArgs e)
    {
        ComboBox cbo = sender as ComboBox;
        if (e.Index == -1) return;

        // ReSharper disable once PossibleNullReferenceException
        string text = cbo.GetItemText(cbo.Items[e.Index]);
        e.DrawBackground();

        if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
        {
            TextRenderer.DrawText(e.Graphics, text, e.Font, e.Bounds.Location, SystemColors.Window);

            if (_dropDownOpen)
            {
                Size szText = TextRenderer.MeasureText(text, cbo.Font);
                if (szText.Width > cbo.Width - SystemInformation.VerticalScrollBarWidth && !_tooltipVisible)
                {
                    RECT rcDropDown;
                    GetWindowRect(HwndDropDown, out rcDropDown);

                    RECT rcCombo;
                    GetWindowRect(HwndCombo, out rcCombo);

                    if (rcCombo.Top > rcDropDown.Top)
                    {
                        ShowToolTip(text, e.Bounds.X, e.Bounds.Y - rcDropDown.Rect.Height - cbo.ItemHeight - 5);
                    }
                    else
                    {
                        ShowToolTip(text, e.Bounds.X, e.Bounds.Y + cbo.ItemHeight - cbo.ItemHeight);
                    }
                }
            }
        }
        else
        {
            ResetToolTip();
            TextRenderer.DrawText(e.Graphics, text, e.Font, e.Bounds.Location, cbo.ForeColor);
        }

        e.DrawFocusRectangle();
    }

    private void OnDropDownClosed(object sender, EventArgs e)
    {
        _dropDownOpen = false;
        ResetToolTip();
    }

    private void ResetToolTip()
    {
        if (_tooltipVisible)
        {
            // ReSharper disable once AssignNullToNotNullAttribute
            toolTip.SetToolTip(this, null);
            _tooltipVisible = false;
        }
    }

    #endregion
}


回答8:

Below is c# code to show tool tip on item of combo box whose width is greater than width of combo box control. Tool tip will be shown once user hover on such combo box:

 this.combo_box1.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
 this.combo_box1.DrawMode = DrawMode.OwnerDrawFixed;
 this.combo_box1.DrawItem += new DrawItemEventHandler(combo_box1_DrawItem);
 this.combo_box1.DropDownClosed += new EventHandler(combo_box1_DropDownClosed);
 this.combo_box1.MouseLeave += new EventHandler(combo_box1_Leave);

 void combo_box1_DrawItem(object sender, DrawItemEventArgs e)
        {
            if (e.Index < 0) { return; }
            string text = combo_box1.GetItemText(combo_box1.Items[e.Index]);
            e.DrawBackground();
            using (SolidBrush br = new SolidBrush(e.ForeColor))
            {
                e.Graphics.DrawString(text, e.Font, br, e.Bounds);
            }

            if ((e.State & DrawItemState.Selected) == DrawItemState.Selected && combo_box1.DroppedDown)
            {
                if (TextRenderer.MeasureText(text, combo_box1.Font).Width > combo_box1.Width)
                {
                    toolTip1.Show(text, combo_box1, e.Bounds.Right, e.Bounds.Bottom);
                }
                else
                {
                    toolTip1.Hide(combo_box1);
                }
            }
            e.DrawFocusRectangle();
        }

        private void combo_box1_DropDownClosed(object sender, EventArgs e)
        {
            toolTip1.Hide(combo_box1);
        }

        private void combo_box1_Leave(object sender, EventArgs e)
        {
            toolTip1.Hide(combo_box1);
        }

        private void combo_box1_MouseHover(object sender, EventArgs e)
        {
            if (!combo_box1.DroppedDown && TextRenderer.MeasureText(combo_box1.SelectedItem.ToString(), combo_box1.Font).Width > combo_box1.Width)
            {
                toolTip1.Show(combo_box1.SelectedItem.ToString(), combo_box1, combo_box1.Location.X, combo_box1.Location.Y);
            }
        }

Here is the link for more details - http://newapputil.blogspot.in/2016/12/display-tooltip-for-combo-box-item-cnet.html



回答9:

With WPF use a ComboBox.ItemTemplate

<ComboBox               
    ItemsSource="{Binding Path=ComboBoxItemViewModels}"
    SelectedValue="{Binding SelectedComboBoxItem, 
    SelectedValuePath="Name"                
>
  <ComboBox.ItemTemplate>
    <DataTemplate>
      <TextBlock Text="{Binding Path=Name}" ToolTip="{Binding Path=Description}"/>
    </DataTemplate>
  </ComboBox.ItemTemplate>
</ComboBox>