As a developer, you can find yourself digging into all sorts of code, even the occasional WinForms application. WinForms come with a lot of handy features that can make fluid design quite simple, allowing your forms to be resized by the user but still hold the basic shape you desire without a lot of extraneous programming.
The trick is to know what features to use and how to avoid having some of those features directly conflict with each other. Today I’ll go over a few ways you can use a Table Layout Panel to achieve this fluid design, as well as some pitfalls and how to avoid them. (See our app modernization capabilities)
Fluid Design with a Table Layout Panel
The row heights and column widths of a Table Layout Panel offer a straightforward way to assign the controls in your form to a grid that will grow (or shrink) to fit different screen resolutions without losing its general shape. Do you have a label that should always be visible in a fixed position? Give it an Absolute pixel column and row width to set its size. Frequently you will have data display or input fields that should grow to fit the screen real estate, giving the user more space to see or enter data when available. Assigning a Percentage (0-100) width and/or height achieves this easily. Note that when Absolute and Percentage columns are mixed, the engine will apply the Percentage to the pixels that remain for the control after all Absolute columns have been accounted for. If a control may have a different size based on the situation, then you can use Autosize so the cell will adjust its size to fit the contents.
A good general practice for fluid form design is to set a Table Layout Panel with a Dock of “Fill”. Assign static controls (generally labels, image buttons, and other controls with a set size) to Absolute sizes first, and then split up the available Percentage of space amongst the controls that remain – ideally assigning larger percentage to controls that will have more content or user input to play with. The Percentages across rows or across columns should always add to 100. If there is a need to have some white space that grows with the form, a row or column that is assigned a Percentage with no contents can meet that need.
The built-in Autoellipsis property on labels allows their content to make best use of the percentage width given them; if the space allotted is too small for the label contents, the control will automatically display what content it can, ending with an ellipsis (“…”) and placing the full content on the label’s hover text. This allows a label to be Dock-filled within its Table Layout Panel cell and make best use of the space available.
There may be times when you expect the contents of a panel to grow enough that they may no longer fit in the screen space available, no matter what you do. The Autoscroll feature of a Panel or Table Layout Panel will automatically add a horizontal or vertical scroll bar to the panel as necessary to address contents that go beyond the assigned size of the panel. Be aware that the scroll bar that appears will eat up some of the available screen real estate. The amount of space taken up varies depending on system settings. You can pull these values at run time from the Horizontal Scroll Bar Height and Vertical Scroll Bar Width properties of System Information.
Pitfalls from Autoscroll on Table Layout Panel
There may be times when you expect a panel to grow in only one direction (vertically or horizontally) and expect only a single scroll bar to eventually show up, while the remaining dimension shows contents filling 100% of its real estate. A good example of this might be a cart, frequently seen docked to the right-hand size of a window. Each item in the cart should make the best use of the horizontal space, but eventually, with enough items in the cart, you would expect a vertical scrollbar to appear.
You might think that a simple solution to this issue is a Table Layout Panel with Autoscroll on it. You could Dock-Fill the panel to fill the right side of the form, set one column to 100 Percentage (to fill the space), then set each row (cart item) with an Absolute height, adding one cell to the cart panel for each cart item. When there are enough rows present, the Autoscroll should kick in, providing a vertical scrollbar and giving the remaining 100% to the width… right?
Unfortunately, the behavior of the Table Layout Panel doesn’t quite meet expectations in this case. Yes, a vertical scroll bar will appear…however, the width of this scroll bar is added on top of the 100% width already assigned, making the panel width larger than the space assigned. Since the width becomes larger than the space assigned, Autoscroll assumes that it also needs a horizontal scroll bar and will gladly provide one at the bottom of the panel. In the end, you have an additional horizontal scrollbar that you don’t want, and you also have your nicely formatted content partially blocked by the vertical scrollbar, with the right-hand side of the content only visible if you scroll right on the horizontal scrollbar. (Note that if you are going for only a horizontal scrollbar, the same problem will happen with a bonus vertical scrollbar showing up.)
The fix for this problem is to take the Autoscroll off the Table Layout Panel (which behaves poorly), and to place your original Table Layout Panel within a Panel. This new Panel (let’s called it the “Scrolling Panel”) will be set to Autoscroll and Dock-fill, just as the Table Layout Panel originally was, and will be placed in the same location as the original. A standard Panel, unlike the Table Layout Panel, will shrink its contents when a scroll bar appears, giving the desired effect. Finally, the original Table Layout Panel (now a child of the “Scrolling Panel”) should be set to Dock-top, so as it grows with additional rows, it eventually exceeds the bottom border of the “Scrolling Panel”, triggering the automatic scroll bars.
Pitfalls from Autoellipsis
This first one may seem obvious, but it may be less so when you are trying to debug why you are seeing an ellipsis on your label but not getting the full detail in the hover text: be sure that you are not setting the hover text on an Autoellipsis-ed label to any other value! If a label has already been assigned hover text, you will still get your ellipsis if the data in the label is too long, but you will not get the full content on hover-over. This can sometimes happen, for example, if your label is part of a larger custom user control, and hover-text is getting set consistently on all controls within that custom user control.
You will also want to avoid vertical alignment (other than top) of an Autoellipsis label. While it can handle horizontal alignment just fine, any vertical alignment will get messed up when the label goes into ellipsis mode. While you may have nice, vertically-centered text without an ellipsis, you’ll find that once the content is enough to trigger the ellipsis, the text appears to be top-aligned again. You can get the effect of the appropriate alignment of the label in a few ways:
- If the row the label is in has an Absolute height, padding or margin can be used to push the label to a middle or bottom alignment.
- If you are trying to Middle-align a label within a Percentage-height row, the best bet is to switch to three rows…the middle of which has the label (with an Absolute height), and the other two of which are each given half the percentage-height of the original row. (If this cannot be split due to other controls on the same line, you can insert a new Table Layout Panel in that cell with three rows instead, giving the top and the bottom row each 50%)
- If you are trying to Bottom-align a label within a Percentage-height row, the best bet is to switch to two rows…the bottom of which has the label (with an Absolute height), and the top with the percentage of the original row. (Again, if this cannot be split, you can use a new Table Layout Panel in that space instead)
Pitfalls from Autosize
Make sure to not set any controls to Dock-Fill if they are within a cell that is Autosized by width or height. These two properties directly conflict with each other: Dock-Fill will try to make use of the entirely of a known set of space (so the container size must be known). Autosized (on a container) will adjust the container size to the size of its contents (so the content size must be known). By setting both, neither the container nor the content is controlling the size. Since each looks to the other to determine its size, frequently you end up with a control that shrinks down to nothing. When designing your form, you must decide which is in control: the content (in which case, use Autosize) or the containing space (in which case, use Dock-Fill, and combine with Autoellipsis on labels if you are concerned about not seeing all the data).
With these tips, you should be able to quickly plan and create a form that makes the best use of many screen resolutions without a lot of size tinkering in your code.