Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
Print
(untagged)

Easy ToolStrip - Let your UserControl display beyond the Containers/Form limits (C# and VB.NET)

0.00/5 (No votes)
3 Aug 2016 1  
The easiest way to implement the ToolStripControlHost and ToolStripDropDown classes, in a UserControl, in order to display a Container/Control beyond the limits of its Parent Container and Parent Form, with very few lines of code. Let's uncomplicate ...

Introduction

When I tried (for the first time) to understand in a practical way how one could display a UserControl beyond the limits of any eventual Parent Container and Parent Form, I came across several examples. Most of them very good, some with stunning styles and visual effects, however all complicated, tangled, sometimes dependent on external actions to the UserControl itself.

As I am fan of a simple coding, the aim of this article is to show how you can apply this technique to a "DropDown" UserControl simply by adding very few lines of code in three different "Key Parts" of the code:

  • Key Part 1 - Variables definition
  • Key Part 2 - UserControl loading
  • Key Part 3 - "DropDown" action controller Event

This technique can be applied to Containers (such as Panel, GroupBox, TabControl, etc.) or Controls (such as ListView, ListBox, TreeView, DataGridView, etc.).

Using the Code

In this example we'll build a kind of DropDownList ComboBox (called CbBox), consisting of three Controls:

  1. A Label (called LBox), to display the selected item.
  2. A Button (called bDrop), to Open/Show or Close/Hide the Items List.
  3. A ListView (called _List), without Headers, to store the selectable items.

● The Label and the Button are created by the Designer and are the visible and resizable part of the UserControl.

● The ListView, declared as an Instance Variable, is set up (with only one Column) when the UserControl is loaded, and only at Runtime.

Key Part 1 - Variables definition

Just define 3 variables:

  1. _List (class ListView), to store the list of data items. In VB.NET, Friend WithEvents, in order to handle the ListView event ItemSelectionChanged.
  2. tsHost (class ToolStripControlHost), to host the Control (_List).
  3. tsDrop (class ToolStripDropDown), to display the list of data items stored in the hosted Control (_List).
// Instance variables
private ListView _List;
private ToolStripControlHost tsHost;
private ToolStripDropDown tsDrop = new ToolStripDropDown();

Key Part 2 - UserControl loading

In the UserControl's Load Event, but only at Runtime:

  1. Set up the ListView with the appropriate Properties, no Headers, just one Column (in this example).
  2. In C#, add the ListView's ItemSelectionChanged EventHandler
  3. Attach the ListView to the ToolStripControlHost.
private void CbBox_Load(object sender, System.EventArgs e)
{
    if (this.DesignMode == false)   // Only at Runtime
    {
        _List = new ListView();
        _List.View = View.Details;
        _List.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
        _List.MultiSelect = false;
        _List.FullRowSelect = true;
        _List.HideSelection = false;
        _List.HeaderStyle = ColumnHeaderStyle.None;
        _List.GridLines = true;
        _List.Columns.Add(new ColumnHeader());
        _List.Columns[0].Width = this.Width - 21;
        //
        // Add the ItemSelectionChanged EventHandler
        _List.ItemSelectionChanged += _List_ItemSelectionChanged;
        //
        // Attach the ListView to the ToolStripControlHost
        tsHost = new ToolStripControlHost(_List);
        //
    }
}

Key Part 3 - "DropDown" action controller Event

In the Button (or Label) Click Event:

  • If _IsOpen is false, the ToolStripDropDown is to be shown:
    1. Resize the ListView:
      • Width equals the UserControl's Width.
      • Height is the lowest number of rows between the predefined _MaxItemsDisp and the total data items (or 1 if no data items),
        multiplied by 17 (16 pixels per row plus 1 pixel for the gridline) plus 1 pixel for final line.
    2. Prevent unnecessary lines display (Margin, Padding).
    3. Get the items to display by the ToolStripDropDown, from the ToolStripControlHost (which hosts the ListView).
    4. Show the ToolStripDropDown, relative to the UserControl's visible part (Label & Button), at the specified upper left corner location (horizontal, vertical).
  • If _IsOpen is true, the ToolStripDropDown is to be hidden.
private void bDrop_Click(object sender, EventArgs e)
{
    // Shows or Hides the List, depending on _IsOpen
    // _IsOpen: True->tsDrop is shown, False->tsDrop is hidden
    if (_IsOpen == false)   // If hidden, show it
    {
        _List.Size = new Size(this.Width, Math.Min(_MaxItemsDisp, Math.Max(_List.Items.Count, 1)) * 17 + 1);
        if (tsHost != null)
        {
            tsHost.Margin = new Padding(0);
            tsDrop.Padding = new Padding(0);
            tsDrop.Items.Add(tsHost);
            tsDrop.Show(this, new Point(-1, this.Height - 2));
        }
    }
    else                    // If shown, hide it
    {
        tsDrop.Hide();
    }
    _IsOpen = !_IsOpen;     // Invert value
    bDrop.Focus();
}

Some other features

The UserControl is actually very simple. But beyond the 3 Key Parts shown above, there are a few more pieces of code:

● The ListView's ItemSelectionChanged Event

When the ToolStripDropDown is shown, this Event is needed, to:

  • Save the Selected Item into a variable Item
  • Hide the ToolStripDropDown
  • If the Item really changed:
    • Copy the User's selection to the UserControl's Label Control
    • Raise the UserControl's SelectedItemChanged Event
private void _List_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
{
    string Item = e.Item.Text;
    tsDrop.Hide();
    _IsOpen = false;
    if (LBox.Text != Item)
    {
        LBox.Text = Item;
        if (SelectedItemChanged != null)
        {
            SelectedItemChanged(Item);
        }
    }
}

● The UserControl's Resize Event

When the UserControl is resized, the Height may not decrease below 15, otherwise the text would be cut.

private void CbBox_Resize(object sender, System.EventArgs e)
{
    if (this.Height < 15)
    {
        this.Height = 15;
    }
}

When the Width is resized, the Button's Width is not affected (19 pixels). But when the Height is decreased or increased, the Button's Height changes accordingly:

(Height = 15 pixels)

(Height = 29 pixels)

● The MouseHover and MouseLeave Events

Just a very simple visual effect. When the Mouse passes over the Button Control, the Mouse pointer and the Button's BackColor are changed:

private void bDrop_MouseHover(object sender, EventArgs e)
{
    this.Cursor = Cursors.Hand;
    bDrop.BackColor = Color.Orange;
}

private void bDrop_MouseLeave(object sender, EventArgs e)
{
    this.Cursor = Cursors.Default;
    bDrop.BackColor = Color.Lavender;
}

● The Properties

A few Properties to add some functionality...

  • BackColor - (Read/Write) Gets or Sets the UserControl's Back Color
  • ForeColor - (Read/Write) Gets or Sets the UserControl's Fore Color
  • MaxItemsDisp - (Read/Write) Gets or Sets the Maximum number of Rows to display
  • Text - (Read/Write) Gets or Sets the UserControl's Text (Label Control)
  • Count - (ReadOnly) Gets the total number of Items in the ListView's Item Collection
public override Color BackColor
{
    get { return LBox.BackColor; }
    set { LBox.BackColor = value; }
}

public override Color ForeColor
{
    get { return LBox.ForeColor; }
    set { LBox.ForeColor = value; }
}

public int MaxItemsDisp
{
    get { return _MaxItemsDisp; }
    set { _MaxItemsDisp = value; }
}

public override string Text
{
    get { return LBox.Text; }
    set { LBox.Text = value; }
}

public int Count
{
    get { return _List == null ? 0 : _List.Items.Count; }
}

● The Methods

Only one Method is needed:

  • AddRow - Adds a String to the ListView's ItemCollection.
public void AddRow(string NewItem)
{
    _List.Items.Add(NewItem);
}

● The Events

Only one Event:

  • SelectedItemChanged - Is raised when the ListViews's ItemSelectionChanged Event occurs and the Selected Item really changed.
public event SelectedItemChangedEventHandler SelectedItemChanged;
public delegate void SelectedItemChangedEventHandler(string Item);

The Demo project

Is a simple Windows Forms Application, where all the UserControl's features can be tested.

The UserControl DLL is located at ...\bin\Debug\CbBox.dll

Although I prefer VB.NET for the sake of simplicity and readability (braces and semicolons are a bummer),
I have to recognize that the performance of C# is much higher... DLL file sizes:

  • VB.NET version is 28 KB
  • C# version is 12 KB

  • Items: each one of the groups can be added only once.
  • Other player: Type and Add whatever you want.
  • To Set a Property, first Click the white box and type or choose the desired value. Colors will show a ColorDialog.
  • Event: Whenever the Selected Item changes, a message will be displayed:

History

03.Aug.2016 - First posted.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here