views:

87

answers:

1

I wrote a custom Control (an auto-complete TextBox (below)) in which a ContextMenuStrip is programmatically added to the form.

My problem is that when the control generates a list longer than the height of it's parent container (Panel, GroupBox, etc) the bottom section of ContextMenuStrip is hidden.

I have tried calling .BringToFront() but can't find any way to overcome this behaviour.

Any help would be greatly appriciated, also feel free to steal the control :)

fig 1.

/// <summary>
/// TextBox which can auto complete words found in a table column
/// Just set DataSource and DataListField and start typing - WD
/// </summary>
public class AutoComplete : TextBox
{
    public DataTable DataSource { get; set; }
    public string DataListField { get; set; }
    private ContextMenuStrip SuggestionList = new ContextMenuStrip();

    public AutoComplete()
    {
        this.LostFocus += new EventHandler(AutoComplete_LostFocus);
        KeyUp += new KeyEventHandler(AutoComplete_KeyUp);
        SuggestionList.ItemClicked += new ToolStripItemClickedEventHandler(SuggestionList_ItemClicked);
    }

    void AutoComplete_LostFocus(object sender, EventArgs e)
    {
        if (!SuggestionList.Focused)
        {
            SuggestionList.Visible = false;
        }
    }

    void SuggestionList_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
    {
        this.Text = e.ClickedItem.Text;
        SuggestionList.Visible = false; 
        this.Focus();
        SuggestionList.Visible = false;
    }

    void AutoComplete_KeyUp(object sender, KeyEventArgs e)
    {
        if (null != DataSource && DataSource.Rows.Count > 0 && null != DataListField)
        {
            if (e.KeyCode != Keys.Enter)
            {
                if (SuggestionList.Items.Count > 0 && e.KeyCode == Keys.Down)
                {
                    SuggestionList.Focus();
                    SuggestionList.Items[0].Select();
                    SuggestionList.BringToFront();
                }
                else if (this.Text.Length > 0)
                {
                    SuggestionList.Items.Clear();

                    DataRow[] drSuggestionList = DataSource.Select("[" + DataListField + "] LIKE '" + this.Text + "%'");

                    foreach (DataRow dr in drSuggestionList)
                    {
                        SuggestionList.Items.Add(dr[DataListField].ToString());
                    }

                    SuggestionList.TopLevel = false;
                    SuggestionList.Visible = true;
                    SuggestionList.Top = (this.Top + this.Height);
                    SuggestionList.Left = this.Left;
                    this.Parent.Controls.Add(SuggestionList);
                    SuggestionList.BringToFront();
                }
            }
        }
    }

}
+1  A: 

It's because you turned it into a child control by setting its TopLevel property to false and adding it to the parent's Control collection. Replace this:

                SuggestionList.TopLevel = false;
                SuggestionList.Visible = true;
                SuggestionList.Top = (this.Top + this.Height);
                SuggestionList.Left = this.Left;
                this.Parent.Controls.Add(SuggestionList);
                SuggestionList.BringToFront();

with this:

      SuggestionList.Show(this.Parent.PointToScreen(new Point(this.Left, this.Bottom)));

Beware that the CMS will overlap the text box if it is too tall.

Hans Passant
Thanks, this does solve the issue I raised, however it also interupts the users typing and I can't seem to use this.Focus() after it to get the cursor back in the textbox :?
WillDud
You are right, that's a show-stopper problem. CMS will automatically close when it loses focus. You are aware that TextBox already provides the AutoCompleteSource property?
Hans Passant
Yea, this is my workaroud for allowing wildcards like * and % and a bit of a practice exercise, cheers for the help :)
WillDud