If you're not a programmer and/or not familiar with MS Com classes and/or a frog, then you probably won't find this interesting in the least.. so by all means, skip along.
Microsoft has these wonderful little controls they call ComboBoxes. ComboBoxes are used for selecting an item from a list - say "CA" out of a list of state abbreviations. MS didn't necessarily invent them but they certainly ripped them off and while that works for MS quite a bit, not everything stolen is worth stealing. These boxes are really not very nice looking but what's worse is that their 3-dimensional border style eats up valuable screen space.
Example of a 3D ComboBox
Most MS controls can be set to be "Fixed Single" or "Fixed 3D" - fixed single being the preferred option as a single line (one pixel in width) is painted around the control as opposed to a 3-dimensional border of maybe 3 or 4 pixels. Unfortunately, in all of MS's wisdom, they not only limited the ComboBox's border style to "Fixed 3D", they force the ComboBox to be 21 pixels high - wasting even more potential space. Why is the ComboBox limited in this way where most other controls are free to form borders how they wish? Why is the ComboBox's waist force-fed to 21 pixels? We may never know. The bottom line is that MS finally caught up with the rest of the kiddies and have implemented a flat-styled ComboBox control in Windows XP, but for those of us forced to develope with MS's older WFC controls through java on multiple Windows versions we are stuck with the craptastic ComboBox of old. This becomes a problem when you have chosen UI conventions that call for a "Flat, web-style look" and are using flat controls everywhere else.
Example of a (almost) FLAT ComboBox
This was thoroughly unacceptable to me so I headed to google thinking that I could not be the only one infuriated with this design. After an hour of googling "flat combobox ms com" and other likely pointers, I came up pretty much empty handed aside from some lovely Delphi and VB examples that were completely useless to me.
So I decided to fix the problem myself.
And I thought that there was no reason not to share this lovely extension of the ComboBox with the rest of the (admittedly small) jave-ms-com-programming world. So let us see if google can manage to rank this post decently on searches of MS Com ComboBox Flat Java.. yah, good luck.
To fix the problem one must first create a new control that extends the MS ComboBox (I believe it is the com.ms.wfc.ui.ComboBox)
public class improvedComboBox extends ComboBox
{
Next, we must carry around a variable specifying which border style to use (we'll default to the original FIXED3D) as well as a variable to carry the TRUE height of our control (not the nasty 21 MS tries to force on us).
private int m_borderStyle = BorderStyle.FIXED3D;
private int m_height = getHeight();
Next, we need to override the setSize() method so that when we do set a size smaller than 21, we can make sure to set our internal variable.
public void setSize(int x, int y)
{
m_height = y;
super.setSize(x,y);
}
Next, we must override the wndProc method of the original ComboBox so that we can catch the nasty little paint messages that are being fired to this control. Within this method we will check if we are painting a flat style. If so we will first send the message up through the ranks to the original ComboBox to let it do any painting it might want to do and then we will call our special painting routine to paint over it.
protected void wndProc(Message message)
{
if (message.Msg==com.ms.wfc.win32.win.WM_PAINT)
{
super.wndProc(message);
specialPaint();
} else {
super.wndProc(message);
}
}
Now that we are trapping every time that windows tries to paint our little ComboBox and we're making sure to call our specialPaint routine, well I guess we need to write our specialPaint routine, don't we?
private void specialPaint()
{
Graphics g = createGraphics();
Pen p = g.getPen();
Rectangle r = getClientRect();
// let's fix that nasty 21 pixel problem
g.setPen(new Pen(getParent().getBackColor()));
while (r.height > m_height)
{
g.drawLine(0,r.height,r.x+r.width,r.height);
r.height=r.height-1;
}
// now draw the single line border
g.setPen(p);
Point pts[] = new Point[]{
new Point(r.x,r.y),
new Point(r.x,r.y+r.height-1),
new Point(r.x+r.width-1,r.y+r.height-1),
new Point(r.x+r.width-1,r.y),
new Point(r.x,r.y) };
g.drawLines(pts);
// now make sure we clean up old 3D edges
// with the control's background color
// I only do this for convience, see note below
g.setPen(new Pen(getBackColor()));
r = new Rectangle(r.x+1,r.y+1,r.width-2,r.height-2);
pts = new Point[]{
new Point(r.x,r.y),
new Point(r.x,r.y+r.height-1),
new Point(r.x+r.width-1,r.y+r.height-1),
new Point(r.x+r.width-1,r.y),
new Point(r.x,r.y) };
g.drawLines(pts);
// now let's draw a flat button over
// the old 3D dropdown button
r = new Rectangle(r.x+(r.width-16),r.y+1,15,r.height-2);
// clear out old 3D button border
g.drawLine(r.x-1,r.y,r.x-1,r.height+2);
// now set the color back to normal
// and fake out a flat button by telling
// windows to paint a flat scrollbutton
g.setPen(p);
g.drawScrollButton(r,ScrollButton.DOWN,ButtonState.FLAT);
}
You'll note in the above specialPaint() method that I chose not to paint the text. I just let the original ComboBox handle that. I do this because I'm lazy. See, by avoiding the middle area I let the original code do it's thing and I don't have to deal with checking for focus, or for selection, and I don't have to paint the focusRects, or any of that jazz. Good enough for me. If you plan on cramming large fonts into your ComboBox that will overrun it's bounds then you should probably deal with it.
If you do want to deal with it, consider something like this:
r = new Rectangle(3,0,getWidth()-18,getHeight()-2);
g.drawFocusRect(r);
g.drawString(getText(),r,TextFormat.LEFT | TextFormat.VERTICALCENTER);
Where the rectangle r is really just the portion of the ComboBox
not taken up by borders or buttons (the 3 to start with is so there is a little buffer between border and text).
Questions? Comments? Shit to fling? - All welcome.
ken + joel