Vous êtes sur la page 1sur 31

Whidbey Layout

Layout for the purposes of this document applies to layout related changes to Windows Forms during the Whidbey product cycle. Some highly dependant content will be covered as a baseline for completeness. Layout for Whidbey translates to TableLayoutPanel, FlowLayoutPanel, AutoSize and AutoScale. About this document: This material should be neither be considered a complete coverage of Layout nor a deep dive into any one area. It simply tries to capture common talking points, interesting elements and answer common questions.

Contents
Whidbey Layout............................................................................................................1 1 Layout Overview..........................................................................................................2 2 TableLayoutPanel........................................................................................................8 3 FlowLayoutPanel........................................................................................................16 4 AutoSize..................................................................................................................18 5 AutoScale................................................................................................................23 6 Layout Perf..............................................................................................................26 7 AutoLayout...............................................................................................................29

Layout Overview

This section includes layout topics that are broadly applicable. The sections that follow contain control or technology specific content.

1.1

Terms
Assortment of layout related terms. AutoLayout - a foofy term used to describe the set of whidbey features which aid in localization of UI. A UI that exhibits AutoLayout does not require localizers to move the locations of controls after they have updated the text in a new language. This generally implies usage of AutoSize on controls, and usage of FlowLayoutPanel and TableLayoutPanel - although there are certain cases where this is not necessary. If the text of the control can grow by ~30% without clipping, you do not need to do anything special. AutoScale - a feature which (in V1 AutoScaleBaseSize, in V2 in AutoScaleDimensions) which grows/shrinks and shifts controls by a scaling factor when the font has changed for the form. (e.g. when customer switches DPI or moves to large fonts). This is the managed solution for dialog units. AutoSize - a boolean flag on control which automatically forces the size of the control to be the "preferred size" as specified by the protected virtual GetPreferredSize method. Bounds represents the total size of the control and the location of the control with respect to the controls parent. In the case where the control does not have a parent, (usually a form), the location represents the location on the screen. ClientRectangle represents the size of the control without the non-client border elements such as scrollbars, title bars, menus, and window borders. Since this rectangle is represented in the client coordinate system, by definition, the value returned by the ClientRectangle.Location will always be (0,0). ClientSize returns/sets ClientRectangle.Size. DisplayRectangle: the area in which to layout, which is also used in scrolling. This rectangle should be used as the frame of reference to layout child controls. Normally, the display rectangle is the same as the client rectangle. However for controls deriving from ScrollableControl, this rectangle can be larger than the client area. Unlike the ClientRectangle, the origin of the DisplayRectangle.Location property can be negative, representing how much the window is currently scrolled. The location of the DisplayRectangle corresponds to the AutoScrollPosition property of a ScrollableControl. In order to have proper scrolling support, is important to respect the location of the display rectangle when setting the bounds of a child control. E.g. Button1.Location = new Point(this.DisplayRectangle.Location.X + 20, this.DisplayRectangle.Location.Y + 20); FlowLayoutPanel - a panel which lays out its child controls in a flowing manner. Different flow directions, wrapping, and flow breaks are supported. GetPreferredSize - a method which controls override to specify how big they want to be. Containers/panels usually measure all their children, children usually measure their visual elements (text, image) and report back their suggested size. Location returns/sets Bounds.Location MinimumSize: The smallest Size a control is allowed to become. MaximumSize: The largest Size a control is allowed to become.
2

PreferredSize: This generally returns the ideal size for the control to show its contents correctly. This is implemented by calling GetPreferredSize(0,0). RightToLeftLayout - a flag that controls whether the control layout is right-to-left or left-toright when RightToLeft==Yes (uses WS_EX_LAYOUTRTL mirroring). Size returns/sets Bounds.Size TableLayoutPanel - a panel which lays out its child controls according to rows and columns. Row and column styles, row spanning and column spanning are supported.

1.2

Docking & Anchor Layout


Dock and anchor concepts apply to the default layout used for Forms. Simply put, anchor maintains the distance between the edge of the control and the corresponding edge of the container in which it is anchored. By default, controls are anchored top left this is what keeps them in the same place when a form is resized. Ill leave anchor=none as an exercise in mind expansion for the ready. Dock and anchor are closely related in fact dock is a subset of the anchor where the distance from the corresponding anchored sides are zero. Dock -> Anchor Mapping Dock None Top Bottom Left Right Fill Anchor None Top, Left, Right Bottom, Left, Right Top, Bottom, Left Top, Bottom, Right Top, Bottom, Left, Right

What happens when I have Dock and Anchors that conflict? The simple answer is this is not possible. When you set an anchor, the dock style clears. And vise versa if you set the dock style. Under the covers, we use the same bit fields to represent dock and anchor, so it is not possible to get into a conflicting state. Anchor Scenarios These scenarios are in a form which employs a dock and anchor layout engine. Keeping a fixed distance from an edge If you have a control you want to remain a fixed distance from an edge of the parent.

Anchor Bottom|Right

Resizing a child control when the parent grows/shrinks If you have a control you want to resize when the parent resizes using opposite anchors (Left | Right) or (Top | Bottom) will help maintain the same distance from the edge as the parent resizes.

Anchor = Top|Bottom

Anchor = Left|Right

Dock Scenarios These scenarios are in a form which employs a dock and anchor layout engine. Always glued to one edge of the form - Dock Layout can generally be represented by combinations of Anchors, but its quite handy for things that always need to be glued to one edge of the parent control.

Dock Top ToolBar

Splitter layouts through a combination of Dock styles, splitter style layouts can be achieved. This scenario is not as important in Whidbey, as we have provided a SplitContainer to do this layout magic for you. Dock Left Panel Dock Fill Panel

Dock Left Splitter

1.3

Padding & Margin


Padding is the internal space you can add to your control/item Margin is the external space you can add to separate controls/items.

Depending on the implementation of GetPreferredSize for a control, changing the Padding property can inflate the control by the height and width specified in the Padding property.

1.4

You have a perf problem with Layout - how to diagnose?


In the UI that you believe to be laying out the most, either override OnLayout or sync the Layout event. In the event use Debug WriteLines to write both the current size of the control and the call stack of how we got here. You can also print out the layoutEventArgs.AffectedProperty. Usually you'll find very interesting results. For the scenarios where you think the Layout is not necessary, add in a SuspendLayout. When the operation is complete, add a matching ResumeLayout. If you dont feel that a layout is necessary at that time, call ResumeLayout(false) which means dont do the layout just this second.

1.5

A note about SuspendLayout and ResumeLayout


SuspendLayout and ResumeLayout only prevent OnLayout from being called. Additionally they only prevent OnLayout from being called for that particular control. So if you have a Form with a Panel in it, and call SuspendLayout on the Form, the Panel's layout is not suspended.
private void button1_Click(object sender, EventArgs e) { this.Layout += new LayoutEventHandler(Form1_Layout); panel1.Layout += new LayoutEventHandler(Panel1_Layout); // Test one - calling PerformLayout here does not call Form1_Layout this.SuspendLayout(); this.PerformLayout(); this.ResumeLayout(false);

// Test two - calling PerformLayout here calls Panel1_Layout // Child controls are not suspended when the parent is suspended. this.SuspendLayout(); panel1.PerformLayout(); this.ResumeLayout(false); // Test three, properly suspending layout this.SuspendLayout(); panel1.SuspendLayout(); panel1.PerformLayout(); // <--- Layout event on Panel NOT called panel1.ResumeLayout(false); this.ResumeLayout(false); panel1.Layout -= new LayoutEventHandler(Panel1_Layout); this.Layout -= new LayoutEventHandler(Form1_Layout); }

1.6

What properties/methods cause layout?


This lists contains all the "reasons" for layout we pass to the LayoutEventArgs. Usually the strings correspond to the properties.
Alignment Anchor AutoScroll AutoSize Appearance AutoEllipsis BorderStyle CellBorderStyle Bounds CheckAlign ChildIndex ColumnHeadersHeight ColumnHeadersVisible Columns ColumnSpan ColumnStyles Controls Dock DisplayRectangle DisplayStyle DrawMode DropDownButtonWidth FlatAppearanceBorderSize FlatStyle FlowBreak FlowDirection Font GripStyle GrowStyle Image ImageIndex ImageScaling ImageScalingSize ImageKey ImageAlign Items LayoutSettings LinkArea Links LayoutStyle Location Margin MaximumSize MinimumSize Multiline Orientation PreferredSize Padding Parent RightToLeft RightToLeftLayout RowHeadersVisible RowHeadersWidth Rows RowSpan RowStyles Renderer ScrollBars Size ShowDropDownArrow ShowImageMargin ShowCheckMargin Spring Style TableIndex Text TextAlign TextImageRelation UseCompatibleTextRenderi ng Visible WordWrap WrapContents

Additionally, anything that plays with the Control collection. These include but are not limited to
control.Controls.Add(..) control.Controls.Clear() control.Controls.Remove and RemoveAt control.BringToFront() control.SendToBack() control.Controls.SetChildIndex control.Parent

1.7

Selection
Once youve built up a form, it can be cumbersome to select various pieces of the UI. There are several ways to navigate your dialog in the designer. Document outline: While you are designing your dialog, use the Visual Studio Document Outline window to navigate from panel to panel. This shows a hierarchical view of the panels within your project. Typically this can be opened by using View->Other Windows->Document Outline. Escape key: When you select a control, you can get select its parent by pressing ESC key. ContextMenu: When you select a control, you can select its parent by right clicking and choosing Select <parent> from the context menu.

1.8

Other resources
Everett Layout material on MSDN Windows Forms Layout Introduction (for Everett)

TableLayoutPanel
Our primary weapon in our auto-layout arsenal is the Table Layout Panel (TLP). TLPs act similar to HTML tables in their ability to regulate space and flow on a form. The TableLayoutPanel has several key sources of information that it uses to determine the size/location of child controls, a collection of child controls to layout (in the Control collection), a number of rows and columns to generate, and a collection of row and column styles to determine the sizing characteristics for each row/column.

2.1

So I should use TableLayoutPanel everywhere, right?


No - TableLayoutPanel is very powerful, but was designed to provide a very specific set of functionality. It is tempting to try to use it in all new Whidbey forms, but this generally leads to sadness and misery, specifically in the form of shoddy UI performance. Here is a quick list of the sorts of UI that benefit most from the use of TLP: UI in which there are multiple parts of the Form that, when the Form is resized, you'd like to see grow portional to one another UI that will be modified or generated dynamically at runtime, such as data entry forms that have user-customizable fields added or substracted based on preferences. UI that should remain a fixed size overall (e.g. the dialog should stay under 800x600), but you need to support localized strings (see note above for an example of this usage) Here's a list of types of UI that do not benefit greatly from the use of TLP: simple data entry forms with a single column of labels and a single column of text entry areas you CAN use the TLP, but it doesn't buy you anything that v1/1.1 functionality would buy you (though see the localization FAQ below for more info on how AutoSize helps here) forms with the "one big thing" idiom - a form with a single large PropertyGrid, for instance - that should fill all the available space when a resize occurs - just use anchoring, since nothing else shoulw grow on form resize Suggestions Avoid nesting TableLayoutPanels. The TLP is relatively performant, but it was designed with at most double-nesting in mind - beyond that, performance can suffer dramatically on resize. Avoid visual inheritance (putting a TLP in a base form and adding stuff in in the derived class). The code spit for this is not perfomant and usually this situation gets folks into trouble.

2.2

Scenarios for TableLayout


AutoLoc use in dialogs - Push adjacent controls out of the way when localized using autosized rows/columns will automatically push labels/textboxes that have grown as a result of localization.

Username: Password:

Username Localized: Password Localized:

AutoSize ColumnStyle

100% ColumnStyle

Proportionally distribute up space on resize rather than have the splitter model where only one side grows, as the dialog grows, grow both sides of the dialog equally. This was not something you could easily do with Anchor layout.

50%

50%

50%

50%

2.3

Adding Controls
Controls can be added to the TableLayoutPanel in a free-styled manner via the Add method on the Controls collection. The newly added control will be added in the next available cell, but generally column and row positions are assigned to the child controls:
tableLayoutPanel.Controls.Add(button1, 2,3)

will add at column = 2, row = 3.

2.4

Positioning Controls
Additionally the tableLayoutPanel provides several methods to change the position of controls that are already added to the table:
tableLayoutPanel.SetCellPosition(new TableLayoutPanelCellPosition(4,2))

NOTE: Via extender provider on the control itself the SetRow and SetColumn methods can be used. To determine where a control was place programmatically use the following:
public TableLayoutPanelCellPosition GetPositionFromControl(Control control);||

Absolutely position elements within a TableLayoutPanel The margin property of control can be used to adjust the distance from the edge of the cell. If you change the anchor to be Top|Left and change the Margin to be (20,3,0,0) the control will be 20 pixels from the left edge and 3 pixels from the top. Stretching and aligning elements within a TableLayoutPanel You can use the anchor property of the control to align to a particular side (e.g. Left will stick it to the Left side). Using opposing anchors, the control can be stretched. E.g. Anchor=Left|Right will stretch to fill the width. The Dock property works in a similar manner.

2.5

Column and RowStyles


The ColumnStyles and RowStyles collection control the sizing of all the rows and columns. If these are empty or there are more rows/columns than styles, it is assumed the column/row is "AutoSized". AutoSize sizes the column/row to the minimum size necessary to fully contain the largest content in the column/row. Absolute sizes to a fixed width Percent sizes to a normalized percentage of remaining space. NOTE: Although not strictly required, at design time we attempt to preserve a 1:1 mapping between row and rowstyle along with column and columnstyle. Because these collections in runtime are independent, alteration of one collection can result in sync problems between the two and unexpected results. Row/Column space distribution The Row and Column Styles collection are used to determine how to allocate space within the table. Rows and columns are allocated space in a non-autosized TableLayoutPanel in the following order: 1. Absolutely columns/rows (full space allocated) 2. AutoSized columns/rows (as much as possible to fit the controls in the column/row) 3. Percentage columns/rows (distribution of remaining space) Example I: Percentage styled columns attempt to fill the remaining space. So a 200px wide table with 20px absolute, an auto sized column and a 100% column would go like this: 1. col1 ABS: 20 px 2. col2 AUTO: whatever the preferred size is... say this measures to be 100px 3. col3 100%: 200 - 20 - 100 = 80px. Example II: If there are two percentage style columns, the 80 pixels would be divied up between the two columns in proportion to one another - so if we had 20% and 80% it would be: 1. 2. 3. 4. col1 ABS: 20 px col2 AUTO: whatever the preferred size is... say this measures to be 100px col3 20%: (200 - 20 100) = 80px remaining space. 80px * .20 = 16px. col4 80%: (200 - 20 100) = 80px remaining space. 80px * .80 = 64px.

Example III: Exactly the same as above but the two percentage style columns are both 75%, the 80 pixels would be split equally between the two normalized columns: 1. 2. 3. 4. col1 ABS: 20 px col2 AUTO: whatever the preferred size is... say this measures to be 100px col3 75% (normalizes to 50%): (200 - 20 100) = 80px remaining space. 80px * .50 = 40px. col4 75% (normalizes to 50%): (200 - 20 100) = 80px remaining space. 80px * .50 = 40px.

Example IV: The last column covers the table layout panels underwear. So a 200px wide table with 20px absolute, an auto sized column and a 20px absolute would go like this:

10

1. col1 ABS: 20 px 2. col2 AUTO: whatever the preferred size is... say this measures to be 100px 3. col3 ABS: 20 px, not enough pixels so we stretch the last column to be (200 - 20 100) = 80px

2.6

Generation of Rows and Columns


Whereas the getter for RowCount / ColumnCount returns the number of rows or columns in the table layout panel respectively, the setter is peculiar in that it sets the minimum number of rows or columns to create. This is throttled by the surprisingly well named GrowStyle property that determines how and if rows or columns are added via the AddRows (default), AddColumns or Fixed option. GrowStyle Fixed AddRows AddColumns Rows property Specifies the number of rows to create specifies the minimum number of rows to create, more will be created if needed. specifies the number of rows to create. Columns property Specifies the number of columns to create Specifies the number of columns to create Columns property specifies the minimum number of rows to create, more will be created if needed.

2.7

Extender-provider properties
Rather than have properties on Cells, the table layout panel proffers properties on the controls within the cells via extender providers. Below is the list of those offered. CellPosition public void SetCellPosition(Control control, TableLayoutPanelCellPosition position); Specifies the where the column and row where a control should be placed. -1, -1 means place at next free position. Column public int GetColumn(Control control); public void SetColumn(Control control, int column); Specify the where the column where a control should be placed. -1, means place at next free position ColumnSpan public int GetColumnSpan(Control control); public void SetColumnSpan(Control control, int value); Specify how many columns this control should span. (default is 1) Row public int GetRow(Control control); public void SetRow(Control control, int row); Specify the where the row where a control should be placed. -1, means place at next free position.

2.8

Example: Maintain equal sizing amongst horizontally-aligned autosizing buttons


Here is the scenario:

Simply setting Button.AutoSize=true would cause the buttons to have unequal size, ala:
11

Instead, 1. place the controls within a TableLayoutPanel 2. One row and three columns for this example

3. Set each column to equal percentage column style 4. Anchor all buttons Left, Right (stretch) 5. Set TableLayoutPanel AutoSize to true

The buttons now will resize correctly.

2.9

Example: Autolayout considerations for textbox button combinations


Every scenario differs a bit, but a TableLayoutPanel can often address this issue. For instance, if you have the following arrangement:

Simple setting the button to AutoSize (assuming the button is anchored Top, Left) would "push" the form bigger (assuming the Form was AutoSize=true), ala:

What you really want is for the form to remain its current size and the button to take up space from the textbox. To accomplish this: 1. Place both in a 2 column, 1 row TableLayoutPanel 2. Set the Button's column to be AutoSize 3. Set the TextBox's column to be 100%, and presto, you get:

12

2.10

Example: Layout group boxes with TableLayoutPanel


Group boxes tend to have similar behavior to dialogs except that they contain fewer controls. Also, many group boxes will be required to grow vertically with font changes and still stretch with the form. The below example has a TableLayoutPanel inside a group box. The Group Box has its padding set to 0 all around and the TableLayoutPanel has a margin of 9 all around. A single absolute column style in this table layout panel restricts the width of the rest of the contents and allows the font to wrap. Also, this way we can anchor the table layout panel top, left and right so the contents will stretch with the groupbox.

2.11

Example: Non-resizable dialogs


Dialogs with a fixed border are generally the easiest to set up auto-layout for. They should have an overarching TLP with AutoSize set to true and AutoSizeMode set to GrowAndShrink. The Form it is on should be set to AutoSize = true with an AutoSizeMode of GrowAndShrink.

2.12

Resizable Dialogs
Resizable dialogs require that controls are present that the user may want to resize to better use. In general, items such as labels and buttons will not need to be modified by the user. They will be covered by auto-layout. Controls that do need to be resizable are multi-line text boxes, data grids, etc...

13

To set up a resizable form, set the columns and rows that should grow to a percentage style. The TLP and the form should not be set to autosize. This will get us into problems with being able to resize larger without being able to resize the dialog smaller again. Default padding and margins around the borders will suffice. The TLP should be anchored top, bottom, left, right. The controls that need to grow will need to be anchored top, bottom, left, right. The other controls should be anchored however you want them to look on the form. Here are two screenshots of a resizable form before and after resizing.

14

2.13

TextWrapping
What are the the techniques for text wrapping? Wrapping radio button/checkbox http://blogs.msdn.com/jfoscoding/articles/478300.aspx Wrapping text http://blogs.msdn.com/jfoscoding/articles/478299.aspx

2.14

Accessibility and Mnemonics considerations:


Some controls in windows forms pick up their accessible names from the previous item in the tab order. E.g. a text box can pick up its accessible name from the label preceding it. If you have a label which describes a text box or combo box, you should set the TabIndex of the label to be one less than the TabIndex of the text box.

15

FlowLayoutPanel
Flowlayout panel is a much simpler control than TableLayoutPanel. It arranges items as a linear collection, supporting wrapping. It works well for menuing and list like scenarios and also supports textwrapping and some interesting alignment options. FlowLayoutPanel has a much smaller range of uses than TableLayoutPanel. Some scenarios in which it's interesting to consider FlowLayoutPanel include: Placing controls one after another without overlapping lists of links, such as explorer bars. Multi Column UI Portal an application such as money with lots of little panels of information that reflow/wrap when the window is resized. ToolStrip layout ToolStrips support flow layout by a property called LayoutStyle. By switching the LayoutStyle to Flow and casting the LayoutSettings property to FlowLayoutSettings, a ToolStrip can behave much like a FlowLayoutPanel. Toolbar-esque windows that can float and take on different orientations: if the contents of the FLP are properly laid out (e.g. integral heights/widths of one another), you can get a fair amount of rearrangement for free if the window switches between a set of fixed sizes. NOTE: FLP sample in the SDK provides a rudimentary demonstration of this.

3.1

Adding Controls
You simply add to the controls collection, but unlike the TLP, you cant specify row and column the controls are laid out in natural order from the collection. To move the controls, alter the position within the collection for that control.

3.2

Positioning Controls
Currently, the implementation of Anchoring and Docking with FLP, as illustrated in the picture below, is that a control is "box stretched" only as far as the extent of the longest/tallest control in the FLP. Dock and Anchor control how an individual control lines up with the other controls in the row. Diagram below.

16

How can I get the items in a flow layout panel to flow TopDown and stretch from edge to edge? The sample essentially sizes an item to hold the width to a certain size.
private void fLP1_Layout(object sender, LayoutEventArgs e) { fLP1.Controls[0].Dock = DockStyle.None; for (int i = 1; i < fLP1.Controls.Count; i++) { fLP1.Controls[i].Dock = DockStyle.Top; } fLP1.Controls[0].Width = fLP1.DisplayRectangle.Width fLP1.Controls[0].Margin.Horizontal; } // Where the flow layout is set up as this.fLP1.FlowDirection = System.Windows.Forms.FlowDirection.TopDown; this.fLP1.Size = new System.Drawing.Size(175, 180); this.fLP1.Layout += new System.Windows.Forms.LayoutEventHandler(this.fLP1_Layout);

FlowBreak You can set FlowBreak on the control which is the last of that row/column. The following control should appear in the next column/row.

3.3

Properties
The following is a brief discussion of properties pertinent to the FLP. FlowDirection set the direction of flow to be LeftToRight, RightToLeft, TopDown or BottomUp. WrapContents specifies whether the flow wraps into multiple columns/rows when it runs out of space. Margin specify the separation between controls. E.g. if two buttons are in FlowDirection.TopDown with WrapContents = false, the spacing between button1 and button2 should be button1.Margin.Bottom + button2.Marigin.Top.

17

AutoSize
Autosize in its most elemental form means to both size and resize in an "intelligent" way for the particular control. The most common interpretation of this is to size the control the smallest size to fully display the contents.

4.1

AutoSizeMode
AutoSizes reign of terror is often throttled by the AutoSizeMode this has two settings: GrowAndShrink control can be sized freely to fully comply with its preferred size. GrowOnly control can only grow from the size explicitly set.

4.2

What are all the size properties?


MinimumSize: the bare minimum size a control can be set to MaximumSize: the maximum size a control can be set to PreferredSize: the size a control prefers to be - this is implemented by calling GetPreferredSize(0,0) AutoSize: Boolean specifying whether or not the control should be set to it's preferred size public virtual Size GetPreferredSize(Size constrainingSize) - This method returns the "preferredSize" of the control given some size contstraints. If the size passed in is 0 in any dimension or Int32.MaxValue, this implies that the size is not constrained in that dimension. o GetPreferredSize(0,0) Unconstrained in width,height o GetPreferredSize(0,h) Constrained in height by "h", unconstrained in width o GetPreferredSize(w,0) Constrained in width by "w", unconstrained in height o GetPreferredSize(w,h) Constrained in width by "w", constrained in height by "h"

What direction do controls grow when they AutoSize? Controls "grow" (expand) opposite their anchors - so a button anchored Bottom, Right would grow leftwards if its string grew, and upwards if its font grew. Debugging tips: Want to know when a control's size is set to X,Y? Put a conditional breakpoint in Control.cs SetBoundsCore. http://blogs.msdn.com/jfoscoding/archive/2005/09/05/461096.aspx

18

4.3

Table of controls and their implementation of GetPreferredSize


Control Had i AutoSize AutoSiz by e in default Everett Shrinks in Implementation details Anchor + Forwards to ButtonBase - applies a minimum size of Button.DefaultSize, unless its a FlatStyle.System, in which case it only measures the text and tacks on space for system borders + Padding Forwards to ButtonBaseAdapter to measure the contents of the button + space of padding, taking into account Text/Image Align&Relation Text is measured to fit on one line Forwards to ButtonBase, unless it's FlatStyle.System, in which case we measure the Font, Text, text and tack on extra space for win32 borders, Padding Padding, and some extra space to bump it up to UI guidelines Uses the SpecifiedBounds.Width* and the PreferredHeight (height of measured text borders padding) - exact mixture depends on style of combobox Font, Padding, Size, Bounds, Width, Height Could be Affected by...

Button

No

No

No

Text, Image, Padding

ButtonBase

No

No

No

CheckBox

No

Set in DT Yes only

ComboBox

No

No

No

ContainerControl

No

No

No

Subtracts the Padding from the constrainingSize, calls Padding, child LayoutEngine.GetPreferredSize(constrainingSize control layout ) then adds back on its Padding. returns SpecifiedBounds.Size* Uses the SpecifiedBounds.Width* and the PreferredHeight (height of measured text borders padding) changes to Size, Bounds, Width, Height Font, Padding

Control

No

No

No

DateTimePicker

No

No

No

DataGridView

No

No

No

changes to Measures column widths, row heights, tacks on column widths, room for scrollbars + padding heights, Padding Uses largest string in collection as width, height Text, Font, depends on font + bordersm, added with Padding Padding.Size Gets the PreferredSize from ContainerControl, Padding, child if padding is empty - adds a magic 9 pixels to control layout conform to UI guidelines Uses similar pattern to ContainerControl - asks Padding, child layout engine control layout Manages its own size in dock and anchor layout, tries to mush all text into one line. In other Font, Text, layouts, wraps at contstraining width. This can Padding be controlled by setting MaximumSize.Width at the point you want to wrap Forwards to Label Font, Text, Padding

DomainUpDown

No

No

No

Form GroupBox

No No

No No

No No

Label

Yes

Set in DT Yes only Set in DT Yes only No No No No

LinkLabel

Yes

ListBox ListView

No No

item height uses MaxItemWidth, measures item heights in changes, PreferredHeight, adds space for system borders padding, border and Padding styles Padding not used

NumericUpDown Panel

No No

No No

No No

uses width of largest digit height of text system Text, Font, borders + padding Padding uses similar logic to ContainerControl border styles, padding, child control layout changes to image, border style, padding

PictureBox

No

No

No

uses image.Size system borders Padding

RadioButton

No

Set in DT Yes only

Forwards to ButtonBase, unless it's FlatStyle.System, in which case we measure the changes to text, text and tack on extra space for win32 borders, padding, font Padding, and some extra space to bump it up to UI guidelines Forwards to TextBoxBase, tacking on room for scrollbars changes to text, font, padding, multiline, wordwrap

RichTextBox

No

No

No

TextBox TextBoxBase

No No

No No

No No

changes to text, Forwards to TextBoxBase, tacking on room for font, padding, scrollbars (typically sizes height to Font height multiline, in multiline=false) wordwrap Measures text, adds on borders and padding Similar to ContainerControl. In SplitStack, it measures all the room for the Overflow.AsNeeded and Overflow.Never items + room for overflow & grip. In all other layouts, refer to that layout engine's behavior changes to child element layout properties, font, padding

ToolStrip

No

Yes

No

ToolBar TrackBar

Yes Yes

Yes No

No No

Manages its own size according to widths of changes to buttons - calculates number of rows needed to button width display them all. Manages its own size - scrollbar height * 8

4.4

Example: Vertically-aligned autosizing buttons


Here is the scenario:

Simply setting Button.AutoSize=true on each button will break your alignment, since the localized strings are likely to be different lengths - so you'd get this:

The solution is to place the three (or however many) buttons (or whatever controls) in a vanilla, non-flow or table Panel* like this:

and anchor the buttons Left and Right. Then set the Panel to AutoSize, and when the text is changed, causing the buttons to adopt different lengths, the largest button "pushes" the Panel to the size it needs, and the anchors on the other (smaller) buttons "pulls" the smaller buttons to the same size, ala:

4.5

Example: Sizable autosized forms


If you set AutoSize = true for a form, the default setting for AutoSizeMode is GrowOnly. This means an AutoSized dialog in this mode cannot snap back down to a smaller size. Typically this is not what you want. If you want a resizable dialog that can stop at the PreferredSize, use the MinimumSize property.
this.MinimumSize = this.PreferredSize

Alternately, if you do not want the dialog to be resizable, switch AutoSizeMode to GrowAndShrink.

4.6

AutoSize considerations with LayoutEngines


The LayoutEngine iterates through all child controls and asks each control for its preferred size via GetPreferredSize when AutoSize = true. Each layout engine is free to do what it wants with this information. Dock layout Left, Right - controls size to PreferredSize.Width, stretch to fill height Top, Bottom - controls size to PreferredSize.Hidth, strech to fill width Fill - controls stretch to fit remaining space, do not use preferred size Anchor layout Controls generally size to their preferred size, constrained by their anchors. By default, controls in AnchorLayout do not shrink below their specified size, that is turning on AutoSize should not shrink a control - rather it can only make it grow. The controls that are the exception to this rule are CheckBox and RadioButton. In later versions, we plan on adding a ControlStyle switch to let this be more configurable. FlowLayout Controls are generally set to their preferred size, except in the case that they have a Dock or Anchor property set to Align or Fill them to a row. TableLayout Controls are generally set to their preferred size, if there isnt enough width in a column, it asks the control to grow in height if it can. All of the LayoutEngines try their best to set the size to the PreferredSize, however this can be limited by MinimumSize and MaximumSize. These set the absolute upper and lower bounds for control sizing.

AutoScale
AutoScale refers in general to recalculating sizes and positions for controls without reliance on an outer layout container or engine doing the calculation. This is done for environment changes like font, DPI etc. For Everett the default code spit was :
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

AutoScaleBase size attempts to store the size of the font rendered at the DPI of the machine. As is clearly apparent the Size type only stores integer values, from here the problem of conversion accuracy stems. After repeated changes, rounding errors can introduce noticable and at times, unacceptable loss of accuracy. For Whidbey, the default code spit is now
this.AutoScaleDimensions = new System.Drawing.SizeF(6.0F, 13.0F);

We now store the dimensions of the font in a SizeF which leads to much greater accuracy. In addition we offer three AutoScaleModes. Interestingly, both of these autoscale approaches coexist. The two members controlling these two systems are below AutoScale bool that enables/disables the Everett auto scaling system. AutoScaleMode enum including entry of None to map to AutoScale true. All other settings enable Whidbey auto scaling system. AutoScale AutoScaleMode Font DPI True n/a n/a False DEFAULT Whidbey scaling Whidbey scaling enabled, only dpi enabled, font AND changes are dpi changes are handled handled. n/a means that that property combination is not achievable. Property sets on one side set the property on the other. For AutoScale set to False, AutoScaleMode will be set to Font. None Everett scaling n/a

5.1

Example: Syncing system font changes


The following example explains how to correctly sync system font changes and make the dynamic change in your applciation. 1. Hook UserPreferenceChanged 2. Set the Form font again when this event is raised 3. Unhook from UserPreferenceChanged in forms Dispose NOTE: Make sure you unhook the UserPreferenceChanged in Dispose - otherwise you will cause a GC leak due to the reference to SystemEvents.
using System; using System.ComponentModel; using System.Data; using System.Drawing; using System.Windows.Forms; using Microsoft.Win32; namespace WindowsApplication1 { public class Form1 : Form {

public Form1() { this.Font = SystemFonts.IconTitleFont; InitializeComponent(); SystemEvents.UserPreferenceChanged += new UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged); } void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) { if (e.Category == UserPreferenceCategory.Window) { this.Font = SystemFonts.IconTitleFont; } } protected override void Dispose(bool disposing) { if (disposing) { SystemEvents.UserPreferenceChanged -= new UserPreferenceChangedEventHandler(SystemEvents_UserPreferenceChanged); if (components != null) { components.Dispose(); } } base.Dispose(disposing); } private void InitializeComponent() { this.AutoScaleMode = AutoScaleMode.Font; this.AutoScaleDimensions = new System.Drawing.SizeF(5, 13); this.ClientSize = new System.Drawing.Size(292, 266); this.Name = "Form1"; this.Text = "Form1"; } } }

5.2

How Do I turn off scaling for one control


For user control AutoScaleMode applies to the contents of the container that way controls within a form can be repositioned and scaled according the Form level AutoScaleMode and the user control contents are scaled according to the user control level AutoScaleMode. Below is the same form with user control with three different combinations of user control AutoScaleMode and FixedWidth/FixedHeight.

Comment
Default

Starting form (Font 8)

Starting Form (Font 12)

UserControl location scaled size scaled UserControl Contents location scaled size scaled

UserControl.AutoSizeMode = None;

UserControl location scaled size scaled UserControl Contents Location NOT scaled size NOT scaled

UserControl.AutoSizeMode = None; UserControl.ControlStyles FixedWidth/Height true

UserControl location scaled size NOT scaled UserControl Contents location NOT scaled size NOT scaled

NOTE: The code used to enable fixed width fixed height user control:

public UserControl1() { this.SetStyle(ControlStyles.FixedHeight | ControlStyles.FixedWidth, false);

6
6.1

Layout Perf
The section lists out common performance problems and suggested solutions.

Changing the UI in OnLoad


Problem: Changing properties such as Bounds, Size, Location, Visible and Text/Image/etc for AutoSized controls InitializeComponent and/or Suspend/ResumeLayout can cause perf problems. Each time one of these properties are touched a layout is forced. Changing any of these in Form.Load is particularly bad, at that point the handles have all been created, and window messages have to be sent to change size/locations. Solution: Add a Suspend/Resume Layout to prevent extra layouts from occurring. If possible, make all of these changes within InitializeComponent this way only one layout is ever needed.

6.2

Visual Inheritance
Problem: The code generated for visual inheritance (class Form2 : Form1) is actually not that efficient, as the constructor for the base form executes, then the constructor for the derived form executes. This essentially forces two layouts as ResumeLayout is called on the form twice. Visual Inheritance also has problems for localization, as the localization tools dont work well with this kind of situation. Solution: For perf reasons, if you can avoid using it, this is the best solution. If you must, you may want to consider using a base form and swapping in a panel with the derived controls as needed.

6.3

Setting the Size/Location of child controls in the Resize event.


Problem: Using the Resize event to size/position child controls circumvents the Suspend/Resume Layout perf protection. Solution: Use the Layout event to size/position child controls.
private void button1_Click(object sender, EventArgs e) { // Inefficient Layout practice this.Resize += new System.EventHandler(this.Form1_Resize); this.SuspendLayout(); this.Size = new Size(500, 500); // SuspendLayout circumvented! this.ResumeLayout(false); this.Resize -= new System.EventHandler(this.Form1_Resize); // Efficent Layout pracitce this.Layout += new LayoutEventHandler(Form1_Layout); this.SuspendLayout(); this.Size = new Size(500, 500); this.ResumeLayout(false); this.Layout -= new LayoutEventHandler(Form1_Layout); } void Form1_Layout(object sender, LayoutEventArgs e) { // for added performance, consider storing off the last size // that happened when we got here and only perform the layout if the size has changed. Rectangle bounds = this.ClientRectangle; bounds.Inflate(-10, -10); outerPanel.Bounds = bounds; this.Text = "Resized in LAYOUT!"; } private void Form1_Resize(object sender, EventArgs e) {

Rectangle bounds = this.ClientRectangle; bounds.Inflate(-10, -10); outerPanel.Bounds = bounds; this.Text = "Resized on RESIZE!"; }

6.4

Using nested AutoSized table layout panels more than two deep.
Problem: The tablelayoutpanel is a great control for automatically controlling layout, however it has its price performance wise. The simpler you can make the layout, the faster it will be. Solution: Keep the complexity of your layout to a minimum. If the flowlayoutpanel can achieve the same results for your UI, consider tatically using that in places. A flow is always mathematically cheaper to calculate than a table.

6.5

Setting the Font after InitializeComponent is complete.


Problem: The font controls the auto-scaling mechanism for the form. Setting this after the form has finished laying out will cause it to perform an expensive layout all over again, changing the size/location of child controls. Solution: If it knows this data before the final resume layout of initialize component, it will have all the data it needs to perform the layout efficiently, ONCE.

6.6

Dynamically filling in data


Problem: Changing the text of child controls can cause layouts. Solution: Call SuspendLayout on the parent control first, then set the text of all the controls, then call ResumeLayout. If this can be done before the handles are created, it will be even faster.

6.7

Individually setting the Size/Location of a control in multiple properties.


Problem: There are several properties in which you can change the size/location of a control. These include, but are not limited to: Width, Height, Top, Bottom, Left, Right, Size, Location, Bounds, and ClientSize. Setting panel1.Width = 10 and panel1.Height = 10 causes twice the work to occur than setting them both together (especially after the handle has been created as windows forms is chatting live with the operating system about what the size should really be.) Solution: Do all your calculations, then set the property that reflects the most information you have. If you're just chaning the Size, set Size, if you're changing the Size and Location change Bounds.
private void button1_Click(object sender, EventArgs e) { Stopwatch sw = new Stopwatch(); sw.Start(); for (int i = 1; i < 1000; i++) { panel1.Width = i; panel1.Height = i; } sw.Stop(); Stopwatch sw2 = new Stopwatch(); sw2.Start(); for (int i = 1; i < 1000; i++) { panel1.Size = new Size(i, i); } sw2.Stop(); MessageBox.Show(String.Format("Trial 1 {0}\r\nTrial 2 {1}", sw.ElapsedMilliseconds, sw2.ElapsedMilliseconds)); }

// As expected, the second one takes about 1/2 the time because there's half the conversation going on. // Trial 1 3717 // Trial 2 1865

6.8

Clearing a controls collection to swap views


Problem: Someone has a form which has a panel which swaps in and out. When swapping panels, Controls.Clear() is used to clear off the old panel. This is inefficient as it: Forces a layout - the Panels parent is set to null, which causes a ParentChanged, which results in a layout Leaks resources as the Panel is no longer associated with the form and will hold open USER32 handles. Solution: There are a couple of solutions, depending if youre done with the panel or not. If youre not done with the panel just set it to be Visible=false If youre done with the panel call dispose on it.

AutoLayout
The following are notes primarily sourced from the autolayout work Jereme did. Prior to the Whidbey release, dialogs were created by developers, individually opened and modified by each localization team for each specific language. Because localization bugs tend to lag product development, localization bugs are particularly expensive (as they tend to occur closer to the end-game of a product cycle). The PLOC (pseudo localization) process has solved some of this by localizing the strings much earlier on in the game, however it does not solve the labor involved in individually opening and modifying each dialog for each locale. How do I know if my dialog is ready for localization? Each text field can grow by 30% without clipping The dialog can respond to High Contrast without clipping The dialog can respond to font changes without clipping The dialog can respond to DPI changes without clipping The dialog can be switched to RightToLeft mode without clipping (RightToLeft = true, RightToLeftLayout = true on the form).

7.1

The Dialog Checklist


Text clipping via Localization - When a localizer changes the text increases the size of the control by changing the text will anything be clipped? Problem: Changing the text in another language can cause elements of the UI to overlap. Solution: There are multiple solutions for this: Use AutoSize on controls that can grow as the text changes e.g. buttons, labels, etc. If the control can grow 30% and not overlap others, no additional work is necessary. For controls that could obscure others when grown, use a FlowLayoutPanel or TableLayoutPanel to automatically place the controls. Uniform OK/Cancel Buttons - When the Cancel button gets larger, does the OK button always stay the same size? Problem: When Ok and Cancel buttons are localized, it is a best practice to make sure that the localized OK and Cancel buttons are of the same size. Solution: Put the OK and Cancel buttons in an AutoSized TableLayoutPanel with column styles set to 50% and 50% respectively. The anchors for the Ok and Cancel buttons should be set to Left|Right so they stretch from edge-edge of the cell. Large Font/High Contrast When the font size increases - will anything be clipped? Problem: When the font changes, the size and locations of controls need to be adjusted in order not to clip. Solution: Use whidbey auto scaling to dynamically change the size/location of controls. Tabbing and Mnemonics Have you set the tab order correctly? Problem: Mnemonics for labels depend on the tab index being set correctly. Solution: Tab through your dialog, play with mnemonics to ensure it is correct. Accessibility Considerations Do your text boxes and combo boxes have accessible names? Problem: TextBoxes and ComboBoxes dont automagically pick up accessible names.

Solution: Some controls in windows forms pick up their accessible names from the previous item in the tab order. E.g. a text box can pick up its accessible name from the label preceding it. If you have a label which describes a text box or combo box, you should set the TabIndex of the label to be one less than the TabIndex of the text box. Localization Considerations - Have you created a .resx file? Problem: Hand coding UI generally causes problems for localizers. If they have a .resx file, the dialog can be opened in a resource editor such as WinRes. Solution: Do not hand code UI, keep the YourForm.Designer.cs file around, hook up your events in a function outside of the YourForm.Designer.cs, so you can easily revist the file later using Visual Studio.

Shrinks in anchor: If the control doesnt shrink by default in anchor, and the preferred size is smaller than the current size of the control, DockAndAnchorLayout will not shrink the control. In order to shrink controls like this, turn AutoSize=true and set Size=0,0

Vous aimerez peut-être aussi