On a project I'm working on, I use some ComboBoxes that act as a breadcrumb control for a Wizard that I wrote, so the user can jump throughout the wizard to different steps easily and understand where they are. Unfortunately, the ComboBoxes are dynamically filled from an admin section and can be anywhere from a few characters to a few hundred characters. This would make the ComboBoxes very wide. Unfortunately, the ComboBox doesn't automatically change the DropDownWidth Property based off of the items in the ComboBox. I went ahead and wrote a Method to do that (This could be added into an inherited ComboBox to be used over and over too if you wanted) and thought I'd share:
VB
Private Sub FindWidthForDropDown(ByVal ComboBox As ComboBox)
Dim g As Graphics = ComboBox.CreateGraphics()
Dim IsDatabound As Boolean = Not ComboBox.DataSource Is Nothing AndAlso ComboBox.DisplayMember <> ""
Dim WidestWidth As Integer = ComboBox.DropDownWidth
Dim ValueToMeasure As String
Dim CurrentWidth As Integer For i As Integer = 0 To ComboBox.Items.Count - 1
If IsDatabound Then
ValueToMeasure = DirectCast(DirectCast(ComboBox.Items(i), DataRowView)(ComboBox.DisplayMember), String)
Else
ValueToMeasure = DirectCast(ComboBox.Items(i), String)
End If
CurrentWidth = CType(g.MeasureString(ValueToMeasure, ComboBox.Font).Width, Integer)
If CurrentWidth > WidestWidth Then WidestWidth = CurrentWidth
Next
ComboBox.DropDownWidth = WidestWidth
g.Dispose()
End Sub
C#
private void findWidthForDropDown(ComboBox comboBox)
{
bool isDatabound = comboBox.DataSource != null && comboBox.DisplayMember != null && comboBox.DisplayMember != "";
int widestWidth = comboBox.DropDownWidth;
string valueToMeasure;
int currentWidth; using (Graphics g = comboBox.CreateGraphics())
{
for (int i = 0; i < comboBox.Items.Count; i++)
{
if (isDatabound)
valueToMeasure = (string)((DataRowView)comboBox.Items[i])[comboBox.DisplayMember];
else
valueToMeasure = (string)ComboBox.Items[i];
currentWidth = (int)g.MeasureString(valueToMeasure, comboBox.Font).Width;
if (currentWidth > widestWidth) {widestWidth = currentWidth;}
}
}
comboBox.DropDownWidth = widestWidth;
}
Simple, but hopefully helpful. Just pass in the ComboBox and watch it's DropDownWidth change.
UPDATE: Check out the comments for some additional things to add to this method suggested by commenter(s).
Man oh man I have been out of it. A lot has been going on in my life and it's been over a month since I last blogged. I've been really busy with work and have been doing some writing. I am also now engaged and will be getting married May 28th 2005! Now, no more excuses for why I haven't been blogging and let's get to it! ;)
I saw this very cool control on Code Project and thought I'd share. It's a 3 state TreeView Control meaning when you check the CheckBox next to a TreeNode it checks all the children and also when unchecking/checking children, if not all children are checked, but some are, the parents back up the chain will display as checked, but grayed out. There were a few things I needed to change to make this control really the way I wanted it to work though and I thought I'd share:
Visual Styles Awareness
First problem right off the bat is that the control doesn't support visual styles at all. There are a couple different angles we have to look at to get it to really work correctly. We need to make sure that the current system and it's current theming state allows for visual styles. then, if it does, check to see if the application we're currently running in has them turned on.
The first bit of code we need to add are some Win32 API calls (The project was already in C# so I just went with it. Where I mention credits below, Cory Smith has a neat VB version).
[StructLayout(LayoutKind.Sequential)]
public struct DLLVersionInfo
{
public int cbSize;
public int dwMajorVersion;
public int dwMinorVersion;
public int dwBuildNumber;
public int dwPlatformID;
} [DllImport("user32.dll", CharSet=CharSet.Auto)]
private static extern int SendMessage(IntPtr hWnd, TreeViewMessages msg, int wParam, ref TV_HITTESTINFO lParam);
[DllImport("UxTheme.dll", CharSet=CharSet.Auto)]
private static extern bool IsAppThemed();
[DllImport("UxTheme.dll", CharSet=CharSet.Auto)]
private static extern bool IsThemeActive();
[DllImport("comctl32.dll", CharSet=CharSet.Auto)]
private static extern int DllGetVersion(ref DLLVersionInfo version);
Here's the code needed to wrap it up into a nice little Method.
private bool VisualStylesEnabled()
{
OperatingSystem os = Environment.OSVersion;
bool isAppropriateOS = os.Platform == PlatformID.Win32NT && ((os.Version.Major == 5 && os.Version.Minor >= 1) || os.Version.Major > 5);
bool osFeatureThemesPresent = false;
bool osThemeDLLAvailable = false; if (isAppropriateOS)
{
Version osThemeVersion = OSFeature.Feature.GetVersionPresent(OSFeature.Themes);
osFeatureThemesPresent = osThemeVersion != null;
DLLVersionInfo dllVersion = new DLLVersionInfo();
dllVersion.cbSize = Marshal.SizeOf(typeof(DLLVersionInfo));
int temp = DllGetVersion(ref dllVersion);
osThemeDLLAvailable = dllVersion.dwMajorVersion >= 6;
}
return isAppropriateOS && osFeatureThemesPresent && osThemeDLLAvailable && IsAppThemed() && IsThemeActive();
}
Raghavendra Prabhu has a good explanation of each thing to check for in the credits below. With all this in place, we can actually add the visual styles to the control. Add a new ImageList to the Component called "m_TriStateImagesXP" (for the sake of following the existing naming convention) and add the following images to it:
Index 0
Index 1
Index 2
Now modify the code of the constructor to look like this:
public TriStateTreeView()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent(); if (VisualStylesEnabled())
ImageList = m_TriStateImagesXP;
else
ImageList = m_TriStateImages;
ImageIndex = (int)CheckState.Unchecked;
SelectedImageIndex = (int)CheckState.Unchecked;
}
Not bad, but luckily, Visual Styles is a lot better integrated in Whidbey.
Credits here and here when I was adding this functionality.
AfterCheck Event
The AfterCheck Event is one often use in a TreeView and unfortunately doesn't fire off in the provided code. When something doesn't work how you want it to, change it! In my mind, I would only want the AfterCheck Event to fire once for the actual TreeNode that I checked, but you could easily have it fire off for every CheckBox affected as well or possibly make a new Event like AfterInherentCheck or something like that. To add in AfterCheck, add a call to OnAfterCheck in the ChangeNodeState Method, passing in the TreeViewEventArgs. The resulting code looks like this:
private void ChangeNodeState(TreeNode node)
{
CheckState newState;
BeginUpdate();
if (node.ImageIndex == (int)CheckState.Unchecked || node.ImageIndex < 0)
newState = CheckState.Checked;
else
newState = CheckState.Unchecked;
CheckNode(node, newState);
ChangeParent(node.Parent);
OnAfterCheck(new TreeViewEventArgs(node));
EndUpdate();
} Cleanup Designer
The last thing to do is to take out properties from the designer that are no longer appropriate. Just add the following code to hide them:
[Browsable(
false)]
public new bool CheckBoxes
{
get { return base.CheckBoxes; }
set { base.CheckBoxes = value; }
} [Browsable(
false)]
public new int ImageIndex
{
get { return base.ImageIndex; }
set { base.ImageIndex = value; }
} [Browsable(
false)]
public new ImageList ImageList
{
get { return base.ImageList; }
set { base.ImageList = value; }
} [Browsable(
false)]
public new int SelectedImageIndex
{
get { return base.SelectedImageIndex; }
set { base.SelectedImageIndex = value; }
} The end result is something pretty similar to the Visual Studio Tree View control that is aware of Visual Styles.
UPDATE:
Here are some screen shots at different states:
| No Theming | With Theming No Visual Styles | With Theming And Visual Styles |
 |  |  |