How can I create a wrapping radio button / checkbox?

By default the RadioButton and CheckBox class take on the Button's philosophy of AutoSizing - size to fit all contents on one line. If you have an AutoSize=false RadioButton, and size it manually, you'll notice that the text nicely wraps on the word breaks (i.e. spaces between words). It seems that if we could just *change* the size of an AutoSized RadioButton/CheckBox to return the correct height/width, we'd be able to work around this limitation.

Fortunately, we can do this by overriding the GetPreferredSize method, the workhorse of AutoSize and PreferredSize. Behind the scenes, it does all the measurement to calculate how large a control should be. GetPreferredSize has a funny variable passed into it, sometimes it's called proposedSize, however you should really think of it as the constraining size. 

public override Size GetPreferredSize(Size constrainingSize) {
...
}

The constraining size suggests a size the control should fit into - if it's width=100, height=200, the preferred size returned should try as hard as possible to fit within these dimensions. Sometimes it's just not possible. If the radio button's text is set to "Supercalifragilisticexpialidocious", it just might not be possible to fit that radio button within 100px. So really the constraints are "guidance" for sizing. 

Now that we have this "wrapping guidance" how can we use it? Well TextRenderer itself takes in a similar parameter to MeasureText - this is sometimes called a "Bounding Box". So we can feed our constraints passed into GetPreferredSize directly into TextRenderer's MeasureText to figure out our wrapping point. However if we are to do this, we need to figure out a way to take into account the size of the actual check box or radio button, which is a pain because we dont know the details of the control's dimensions and spacing. 

We do happen to know the opposite information though - we know that the Control has sized the Text out to be one line. So we can call base.GetPreferredSize to figure out what the size of the control would be if we had one line of text, then subtract off size of one line of text. 

Size

prefSize = base.GetPreferredSize(proposedSize);
Size bordersAndPadding = prefSize - cachedSizeOfOneLineOfText;
Size newConstraints = proposedSize - bordersAndPadding;

Now that we have the newConstraints, we can call MeasureText again, this time with WordBreak.

TextRenderer.MeasureText(this.Text, this.Font, newConstraints, TextFormatFlags.WordBreak);

This should allow the text to wrap at the correct place - we add back in the borders and padding, and poof we've got a new calculation for GetPreferredSize!

One final thing to note: because we're overriding GetPreferredSize, we may want to do some caching - measuring text can actually be expensive, especially when WordBreak is turned on. Because the size of the text should only change if the text changes or the font changes, we'll clear our cache only when these change. 

See the full code (C#) (VB)

For more information on GetPreferredSize, consult the Layout FAQ.