Introduction
MinimumSize property of the Control class plays significant role in resizable 
forms. In many cases its value can be determined in advance and set at design 
time. However, there are many opposite cases, where MinimumSize must be 
calculated at runtime. 
This article explains, and demonstrates through a relatively complex example, 
proper way of calculating MinimumSize property value at runtime. Result is that 
all controls can be rendered without loss caused by insufficient size of their 
parent controls.
Importance of Precise MinimumSize Calculation
Every control placed on a form or inside any other control takes space on user 
interface. Some controls require fixed space (most of the buttons), some of them 
require a strip (TextBox, Label, etc.), and some require a larger rectangle for 
their purpose (Panel, TabControl, multiline TextBox, etc.).
Typical user interface heavily relies on Dock and Anchor properties to maintain 
optimal layout of controls so that usefulness of each control is maximized. 
However, docking and anchoring has a side effect - when container (form, panel, 
etc.) is reduced too much, some of the contained controls cannot be rendered 
without loss any more. Captions, images, and other GUI elements would not fit if 
controls showing them continue shrinking beyond certain point.
This is where MinimumSize property comes into frame. Every control inherits 
Control.MinimumSize property which should be set to such size below which 
control may not be reduced. This size will be taken into account when layout 
engine operates on parent control. When hard times come and control's bounds 
start shrinking, MinimumSize property shows to be of higher order than Anchor 
and Dock. If obeying anchoring and docking properties means that bounds of the 
control will go below MinimumSize, then anchoring and docking will be ignored in 
favour of minimum size. Only when control prospers again, in terms of its 
spatial bounds, Anchor and Dock will come back into play as before.
This scenario explains why MinimumSize is important. However, one may say that 
this property can be set arbitrarily, like 200x20 for a TextBox. That is 
generally true, but it is not optimal and sometimes such decision can have 
undesired consequences. Especially when container (e.g. form) is shrank by the 
user so much that every pixel counts. Some smarter way of dealing with size is 
then better.
Always keep on mind that when user decides to reduce size of the window as much 
as possible, application should give a helping hand and to reduce all controls 
to their absolute minimum at which controls can still operate. This is why 
MinimumSize properties of controls should be calculated rather than safely set 
to arbitrarily large values.
Simple Example
Suppose that TextBox is meant to receive person's first name. Minimum size can 
be experimentally calculated by performing these steps:
- Take a database of first names for 
	expected culture. For example, table below lists most frequent traditional 
	French names with their corresponding frequencies.
- Measure all names using some typical font, 
	preferably the one that will be used on control. In rare case when font is 
	not known in advance, pick any non-monospaced font, like Times New Roman.
- Calculate average width of all names when 
	rendered. Don't forget to take each name's frequency into account - more 
	frequent names affect average more than others.
- Finally choose name which renders in 
	bounds closest to average.
Here is the function which picks typical name:
string 
PickAverageName(string[] names,
int[] frequencies)
{
   
Font font = new
Font("Times New 
Roman", 12.0F);
    int sum = 0;
    int count = 0;
    List<string> 
typicalNames = new 
List<string>();
   
for (int i = 0; 
i < names.Length; i++)
    {
        
System.Drawing.Size size = 
System.Windows.Forms.TextRenderer.MeasureText(names[i], 
font);
        sum += size.Width * frequencies[i];
        count += frequencies[i];
       
while (typicalNames.Count <= size.Width)
            typicalNames.Add(null);
       
if (typicalNames[size.Width] ==
null)
            typicalNames[size.Width] = names[i];
    }
   
int average = (sum + count - 1) / count;  
// Take ceiling of average width
   
// Now pick the first sample name at average or higher 
width
    while (typicalNames[average] ==
null)
        average++;
   
return typicalNames[average];
}
For French names listed in the table, this 
method returns name Marguerite, which requires 75 pixels wide rectangle using 
Times New Roman font of size 12. This information can be used to determine 
minimum size of a TextBox at runtime, using very simple method:
private
void SetTextBoxMinimumSize(TextBox 
tb, string text)
{
    System.Drawing.Size size = 
System.Windows.Forms.TextRenderer.MeasureText(text, 
tb.Font);
    tb.MinimumSize = new
Size(size.Width, tb.Height);
}
...
SetTextBoxMinimumSize(textBox1, "Marguerite");
When applied to Microsoft Sans Serif font, size 
8.25, this method sets TextBox's MinimumSize to 57x20 pixels. If table with 
French names is relevant to population using our application, then this method 
guarantees that approximately 50% of all names entered to TextBox will perfectly 
fit its bounds without clipping.
Table: Frequencies of most common traditional French names, according to 
University of Montreal (http://www.genealogie.umontreal.ca/en/nomsPrenoms.htm).
![Control.MinimumSize Property in Windows Forms]()
Role of the Control.Layout Event
Layout event is fired every time when control should reposition and resize 
contained controls. For example, if panel contains several child controls, some 
of which being anchored to panel's borders, exact locations and dimensions of 
each control will be determined in the Layout event handler.
This can be demonstrated with a very simple test. Derive a class from Panel and 
override its OnLayout event, so that custom implementation does not invoke base 
class's implementation of OnLayout. Place CustomPanel onto a form, add other 
controls to CustomPanel and anchor them to different borders. Now feel free to 
resize the CustomPanel control and observe that Anchor properties of child 
controls have no effect - layout logic is not executed.
Layout event interferes with MinimumSize property value in a very simple way. 
When control's MinimumSize property is set, it may cause control to be enlarged 
(never reduced, though). This happens in cases when minimum size is larger than 
current size in at least one dimension. In such case, Layout event will be 
raised and contained controls will be repositioned. Now suppose that MinimumSize 
property of contained controls is also being calculated on the fly. These 
influence contained controls sizes and positions. As can be suspected, setting 
both parent and child control's MinimumSize property must therefore be performed 
with care.
Here is an example which demonstrates where the danger comes from. Create new 
Windows Forms project, open main form's source code and add these functions to 
it:
protected
override void 
OnLoad(EventArgs e)
{
   
base.OnLoad(e);
   
Panel pnl = new
Panel();
    pnl.BackColor = Color.LightYellow;
    pnl.BorderStyle = BorderStyle.FixedSingle;
    pnl.Size = new 
Size((ClientRectangle.Width - 10) / 2, (ClientRectangle.Height - 10) / 
2);
    pnl.Left = 5;
    pnl.Top = 5;
    pnl.SizeChanged 
+= new EventHandler(pnl_SizeChanged);
   
TextBox tb = new
TextBox();
    tb.Width = 2 * (pnl.ClientRectangle.Width - 10) / 3;
    tb.Left = 5;
    tb.Top = 5;
    tb.Anchor = AnchorStyles.Top |
AnchorStyles.Left |
AnchorStyles.Right;
    Size tbSize = tb.Size;
    tb.SizeChanged += new
EventHandler(tb_SizeChanged);
    
pnl.Controls.Add(tb);
   
this.Controls.Add(pnl);
   
Console.WriteLine("Original 
panel size {0}", pnl.Size);
    Console.WriteLine("Original 
text box size {0}", tb.Size);
   
// Now specify minimum sizes for panel and text box
    tb.MinimumSize = new
Size(tbSize.Width * 2, tbSize.Height);
    pnl.MinimumSize = new
Size(pnl.Width * 2, pnl.Height * 2);
}
void 
tb_SizeChanged(object sender,
EventArgs e)
{
    Console.WriteLine("TextBox 
size changed to {0}", ((TextBox)sender).Size);
}
void 
pnl_SizeChanged(object sender,
EventArgs e)
{
    Console.WriteLine("Panel 
size changed to {0}", ((Panel)sender).Size);
}
This code creates new Panel control which occupies top-left quarter of the 
parent form (excluding 5 pixels margin from each of the form's borders). Inside 
the Panel new TextBox control is added, so that it occupies 2/3 of the Panel's 
client width (again, excluding 5 pixels margin from the borders).
If we run the code, output will look something like this:
Original panel size {Width=137, Height=127}
Original text box size {Width=83, Height=20}
TextBox size changed to {Width=166, Height=20}
TextBox size changed to {Width=303, Height=20}
Panel size changed to {Width=274, Height=254}
Now observe the output closely. We have changed TextBox's minimum width to 166 
pixels, but at that moment Panel's width is only 137 pixels. Further on, width 
of the panel is increased to 274 pixels, but this takes effect only after 
Panel's Layout event is handled. Inside Layout handler, however, TextBox grows 
in width by additional 137 pixels because that is exact increase in Panel's 
width - that is how TextBox's Anchor property is handled. As a result, TextBox 
grows to 303 pixels in width, which is again larger than containing Panel 
control.
The problem in this example is order in which MinimumSize has been set on 
TextBox and on Panel. The order must be inversed - first set MinimumSize of the 
Panel. This may raise Layout event (if Panel's size is increased). Layout event 
handler will resize TextBox accordingly. Only then we can set TextBox's 
MinimumSize value, knowing that TextBox will be enlarged only if its MinimumSize 
is still larger than current size.
We can exchange two lines of code that are setting MinimumSize values on TextBox 
and Panel:
pnl.MinimumSize = new
Size(pnl.Width * 2, pnl.Height * 2);
tb.MinimumSize = new 
Size(tbSize.Width * 2, tbSize.Height);
If we run the code now, output will be quite different:
Original panel size {Width=137, Height=127}
Original text box size {Width=83, Height=20}
TextBox size changed to {Width=220, Height=20}
Panel size changed to {Width=274, Height=254}
TextBox's size is now changed in the Layout event, which is raised after Panel's 
MinimumSize is set to value larger than current size. After that, MinimumSize of 
TextBox is set, but that has no effect because TextBox's size is already larger 
than value of MinimumSize. 
This little experiment leads to a conclusion. Set MinimumSize property values 
top-down, i.e. first to parent controls, and only then to child controls. All 
this to let parent control's Layout event opportunity to reposition child 
controls properly before their own MinimumSize properties are changed.
General Rules in Calculating MinimumSize
We have determined in previous text that order of setting MinimumSize properties 
should be top-down, i.e. containers first, contained controls after that. 
However, minimum size of container control often depends on minimum sizes of 
contained controls, which further depends on their own contained controls. 
Hence, we have an opposite conclusion here: MinimumSize value is calculated 
bottom-up, i.e. from the inner-most child controls to the outer-most containers.
From this analysis we come to an algorithm of setting MinimumSize of all 
controls in a safe and reliable way:
- 
	
	For every control calculate minimum sizes of 
	all relevant contained controls.
	 
- 
	
	Calculate own minimum size based on minimum 
	sizes and layout of contained controls.
	 
- 
	
	Set own minimum size.
	 
- 
	
	Order contained controls to set their own 
	minimum sizes to previously calculated values.
 
Observe that we are applying the algorithm only to 
relevant controls. Some controls have fixed size (e.g. buttons). Some of them 
are auto-sized (e.g. labels). These controls are not relevant and their 
MinimumSize property is not used. Only their actual size may be taken into 
account when calculating minimum size of their parent controls.
Proposed Solution
For every control to which we want to set MinimumSize, we can design one 
function with signature like this:
private
Size SetXMinimumSize(Dictionary<Control,
Size> minimumSizes, 
bool calculateOnly)
In this function name X is replaced by an appropriate identifier used to 
distinguish particular control.
This function operates in two modes. 
First mode is when calculateOnly is true. In that case appropriate methods for 
child controls are invoked, again with calculateOnly set to true. Method 
calculates minimum size of the target control and adds record to minimumSizes 
dictionary, mapping target control to its minimum size. Minimum sizes for all 
child controls are already there, placed by calls to their corresponding 
methods.
Second operational mode is to invoke the method with calculateOnly set to false, 
while passing minimumSizes dictionary populated in the first run. In this mode, 
MinimumSize of the control is set to value from the dictionary and appropriate 
methods for all contained controls are invoked, setting calculateOnly to false 
in all calls. That will force all contained controls' MinimumSize to be set as 
well.
In both operational modes method returns size of the target control. This 
simplifies implementation because method does not have to consult the dictionary 
to obtain desired minimum sizes of contained controls. This will be demonstrated 
in example that follows.
Upside of this method is that calculation is done exactly once for each of the 
controls, and every MinimumSize property is set exactly once. Order of 
invocations guarantees that all minimum sizes will be calculated before setting 
the values.
Downside of this method is that setting minimum sizes may trigger event which 
has caused recalculation of minimum sizes in the first place. This method must 
never enter twice into execution, or otherwise loops can be formed. One way to 
prevent double entering is to place a Boolean flags at the top level, which are 
used to control recalculation of minimum sizes in this way:
private
void RefreshMinimumSizes()
{
    
_refreshMinimumSizes = true;
   
if (!_refreshingMinimumSizes)
    {
        
_refreshingMinimumSizes = true;
        while (_refreshMinimumSizes)
        {
            _refreshMinimumSizes = false;
           
// At this place invoke methods for all
            // contained controls. Some of the 
methods
            // may cause this method to be 
re-entered,
            // which would only set 
_refreshMinimumSizes 
            // to true and exit happily.
 
        }
        
_refreshingMinimumSizes = false;
    }
}
This method is used at top level to force recalculation of all minimum sizes. If 
re-entered, due to some event which normally causes recalculation, method will 
just set the flag that minimum sizes should be calculated again and continue 
until current run is finished. Then, if flag is set, method will re-run, 
calculating new values again. Typically, second run will have no effect because 
recalculation is triggered in a more relaxed way than really neccessary. 
When all functions that calculate minimum sizes are in place, we should add 
triggers which act upon conditions that require minimum sizes to be 
recalculated. This task cannot be automated. It is up to the designer to decide 
which events and other conditions require attention. For any such event, just 
invoke RefreshMinimumSizes from its appropriate handler and everything will be 
right.
Example
As a matter of demonstration, we will create a form containing main menu at the 
top and a status bar at the bottom. Between these two strips, split container 
will be docked to fill the rest of the window and to divide it with a vertical 
splitter.
Left panel of the split container will be filled with TabControl, which contains 
two TabPages. First TabPage contains a TableLayoutPanel with two rows and two 
columns. TableLayoutPanel is auto-sized, with size mode set to GrowAndShrink. 
This means that table will always have minimum size required by its content.
TableLayoutPanel contains two ComboBoxes, used to select font family and size. 
These controls occupy cells in the first row. ComboBoxes will have fixed size.
Cells in the second row contain one auto-size label (with column span set to 2), 
which shows sample message in selected font and size.
This is how the desired form should look like:
![Control.MinimumSize Property in Windows Forms]()
What we really want to accomplish is to calculate MinimumSize properties of the 
form and all its contained controls so that, when form is reduced to its minimum 
size, it looks like this:
![Control.MinimumSize Property in Windows Forms]()
When done correctly, there will be no spare pixel on the form.
Implementation
Code used to create and populate controls on the form will be skipped here. 
Anyone interested may find it in attached file, which contains complete source 
code of the project.
In this section, we will concentrate on code used to calculate and set 
MinimumSize properties of TabControl (field _tabControl), SplitContainer (field 
_splitContainer) and the form itself. Following methods are used to recalculate 
minimum sizes of these three controls. (Note that Form also derives from 
Control, which is used to add it to dictionary together with other controls.)
private
Size SetTabControlMinimumSize(Dictionary<Control,
Size> minimumSizes, 
bool calculateOnly)
{
   
Size minSize = new
Size();
   
if (calculateOnly)
    {
       
// When reduced to minimum, tab page must have client 
rectangle size equal to 
        // size of contained table control. 
        // Minimum size of tab control is then 
calculated by adding parts of tab control
        // that are located outside the client 
area of tab page to size of the table panel.
        TabPage page = 
_tabControl.TabPages[0];
        int width = _tablePanel.Width + 
_tabControl.Width - page.ClientRectangle.Width;
        int height = _tablePanel.Height + 
_tabControl.Height - page.ClientRectangle.Height;
        minSize =
new Size(width, 
height);
        minimumSizes.Add(_tabControl, minSize);
    }
    else
    {
        minSize = 
minimumSizes[_tabControl];
        _tabControl.MinimumSize = minSize;
    }
   
return minSize;
}
private
Size SetSplitContainerMinimumSize(Dictionary<Control,
Size> minimumSizes, 
bool calculateOnly)
{
   
Size minSize = new
Size();
   
if (calculateOnly)
    {
       
Size tabControlMinSize = 
SetTabControlMinimumSize(minimumSizes, true);
       
int panelMinWidth = tabControlMinSize.Width;
        // Add left panel to dictionary; this entry 
will be used to set panel's minimum width
        minimumSizes.Add(_splitContainer.Panel1, 
new Size(panelMinWidth, 1));
       
// Horizontally, split container consists of a border, 
left panel,
        // border, splitter, border, right 
panel and again border.
        // Sizes of borders and splitter can be 
calculated as part of the
        // control's width which remains when 
widths of client rectangles of left
        // and right panel are subtracted from 
total control width.
        // Minimum width of the split container 
control is then sum of 
        // minimum sizes of two panels 
increased by total size of all
        // outer elements.
        int width = panelMinWidth + 
_splitContainer.Panel2MinSize +
                    _splitContainer.Width - 
_splitContainer.Panel1.ClientRectangle.Width -
                    _splitContainer.Panel2.ClientRectangle.Width;
       
// Vertically, split container consists of border, 
panel (left or right) 
        // and again border. Borders are 
calculated by subtracting 
        // panel client rectangle height from 
total control height.
        int height = 
tabControlMinSize.Height + _splitContainer.Height -
                        _splitContainer.Panel1.ClientRectangle.Height;
        minSize =
new Size(width, 
height);
        minimumSizes.Add(_splitContainer, minSize);
    }
    else
    {
        minSize = 
minimumSizes[_splitContainer];
        Size panel1MinSize = 
minimumSizes[_splitContainer.Panel1];
        
_splitContainer.MinimumSize = minSize;
        _splitContainer.Panel1MinSize = panel1MinSize.Width;
        
SetTabControlMinimumSize(minimumSizes, false);
    }
   
return minSize;
}
private
void SetFormMinimumSize(Dictionary<Control,
Size> minimumSizes, 
bool calculateOnly)
{ 
    Size minSize = 
new Size();
   
if (calculateOnly)
    {
 
        Size splitContainerMinSize = 
SetSplitContainerMinimumSize(minimumSizes, true);
       
int width = splitContainerMinSize.Width;
        int height = 
splitContainerMinSize.Height + _statusBar.Height;
       
Size clientSize = 
new Size(width, height);
        minSize = 
SizeFromClientSize(clientSize);
        minimumSizes.Add(this, minSize);
    }
    else
    {
        minSize = 
minimumSizes[this];
        this.MinimumSize = minSize;
        
SetSplitContainerMinimumSize(minimumSizes, false);
    }
}
These methods clearly demonstrate why it wasn't attempted to create any general 
solution for the problem. Every kind of control has its own logic and all three 
methods substantially differ from each other.
Method which wraps-up the process is this:
private
void RefreshMinimumSizes()
{
    
_refreshMinimumSizes = true;
   
if (!_refreshingMinimumSizes)
    {
        
_refreshingMinimumSizes = true;
        while (_refreshMinimumSizes)
        {
            _refreshMinimumSizes = false;
           
Dictionary<Control,
Size> minimumSizes = 
new Dictionary<Control,
Size>();
            SetFormMinimumSize(minimumSizes, true);
            SetFormMinimumSize(minimumSizes, false);
        }
        
_refreshingMinimumSizes = false;
    }
}
This method initiates setting form's MinimumSize, which recursively does the 
same to all other controls.
At the very end, just note that triggers for minimum size recalculation are 
SizeChanged event on TableLayoutPanel and ClientSizeChanged event on TabPage 
which contains TableLayoutPanel. First event is important because changes in 
size of the TableLayoutPanel may require all other controls, up to the form, to 
be enlarged to fit the new size of the table. Second event is important because 
TabPage's client rectangle may change due to circumstances not related to size 
of the control. Namely, changed border style may cause TabPage's client 
rectangle to shrink so that TableLayoutPanel cannot fit inside it any more, and 
hence MinimumSize of the TabControl must be increased.
Once again, complete source code of the demo project is located in the attached 
file. Feel free to analyze it in more detail.
Conclusion
MinimumSize property derived from Control class is one of fundamental properties 
in many Windows Forms applications, despite the lack of interest it often 
suffers in general public. Precise calculation of this property helps a lot in 
creating user-friendly interface and improves any application.
On the contrary, setting this property to misfit value or ignoring it, may cause 
user to feel uncomfortably or even enraged; not happy anyway.
General advice is to always pay attention to MinimumSize property of all 
controls other than auto-sized and fixed-sized ones. In simpler cases rule of 
thumb is applicable and MinimumSize can be set at design time. Under more 
complex circumstances, where spatial distribution of controls depends of 
unpredictable conditions, like user provided values shown in our example, 
MinimumSize properties must be determined programmatically. In those cases, do 
not try to save time but perform the task correctly and it will pay back later.
This article has provided guidelines how to calculate MinimumSize of all 
controls in a relatively painless way, at least in a unified way, which is 
always helpful. Guidelines given in this article, if followed, will ensure 
reliable and fast calculation of MinimumSize properties in any complex user 
interface.