|
One of the biggest problems when designing screens and forms for surveys
and assessments that have a large number of questions is "real estate".
I have a healthcare assessment module I'm completing for a project that
I've put into a tabbed interface. Each "Tab" represents a section of
a very long healthcare assessment, and one of the speciification's requirements
is to have a number of multi-select dropdown - type controls on a single
tab.
The nice thing about a combobox is that it is very compact - it can
occupy basically one "line" of vertical space, and when you click on
the arrow at the right, all the items in it drop down and then when you
are done selecting, it, contracts again. Thus, you can have many of these
on a single form or "Tab".
Unfortunately, the Windows Forms ComboBox class doesn't support multiple
selections. Only the ListBox does. But the problem with the ListBox is
that if you set its height to a single item, you have to use the little
up/down arrows at the right to move through the list of items, and that
is clumsy and unproductive because you only get to see one of the items
at a time. And if you set its height big enough to show all the items,
well -- there goes your real estate.
I quickly figured out that it would be a lot easier to make a ListBox
-- which already supports multiple selections -- behave the way I wanted,
than to try to make a ComboBox support multiple selections.
My solution, I believe, is quite elegant and compact, and serves as
an excellent illustration of the incredible power and flexibility of
the .NET Framework. What I did, in essence, is to create a CustomListBox
control derived from ListBox, and override its OnMouseEnter and OnMouseLeave
events to automatically expand the height to exactly show all the items,
allowing multiple selections, while also automatically sending all other
controls to the back so that they didn't interfere with my "view".
Then, when you are finished selecting and OnMouseLeave, everything is
restored to its original compact, one-line height.
Let's breeze through the code, which I've commented in the appropriate
places, and you will see that this is not at all a complex control to
write:
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
namespace PAB.CustomControls
{
public class CustomListBox : System.Windows.Forms.ListBox
{
private int LastHeight=0;
private int LastParentHeight=0;
private System.ComponentModel.Container components = null;
public CustomListBox()
{
InitializeComponent();
}
protected override void OnMouseEnter(EventArgs e)
{
// store the last height of this control
this.LastHeight =this.Height;
// use the ItemHeight to compute needed height
int tmpHeight=this.GetItemHeight(0);
this.Height=this.Items.Count *(tmpHeight+1);
// if parent is GroupBox or Panel, we also need to
// expand its height temporarily so it will accomodate us!
if(this.Parent is GroupBox || this.Parent is Panel)
{
// store the parent's height so we can put it back
this.LastParentHeight=Parent.Height;
this.Parent.Height=this.Height +tmpHeight;
this.Parent.SendToBack();
this.Parent.Refresh();
}
// all the other guys to the back of the line...
Form frm = this.FindForm();
foreach( Control c in frm.Controls)
{
c.SendToBack();
}
// we go to the front, everybody else behind us
this.BringToFront();
this.Refresh();
base.OnMouseEnter (e);
}
protected override void OnMouseLeave(EventArgs e)
{
// restore original height
this.Height =this.LastHeight ;
// if we did the daddy, fix him too...
if(this.LastParentHeight!=0)
this.Parent.Height=this.LastParentHeight;
base.OnMouseLeave (e);
}
protected override void Dispose( bool disposing )
{
if( disposing )
{
if( components != null )
components.Dispose();
}
base.Dispose( disposing );
}
#region Component Designer generated code
private void InitializeComponent()
{
this.Name = "CustomListBox1";
this.Size = new System.Drawing.Size(150, 24);
}
#endregion
}
}
|
Here is how the final control appears in its original
and Expanded states:

No focus |

On MouseEnter
|
You can download the solution below, which includes both
the control project and a Windows Forms "Test Harness" just like the
above.
Download the Source Code that accompanies this article
|