During its lifetime, the form is likely to take up space at some location. The initial location for the form is governed by the StartPosition property, which can have one of several values from the FormStartPosition enumeration: enum FormStartPosition { CenterParent, CenterScreen, Manual, WindowsDefaultBounds, WindowsDefaultLocation, // default } These values have the following behavior:
The size and location of the form are exposed via the Size and Location properties, of type Size and Point, respectively (both from the System.Drawing namespace). As a shortcut, the properties of the size of a form are exposed directly via the Height and Width form properties, and those of the location are exposed via the Left, Right, Top, and Bottom properties. Figure 2.3 shows the basic size and location properties on a form. Figure 2.3. The DesktopLocation, Location, ClientSize, and Size Properties
When the upper-left corner of a form changes, that's a move , which can be handled in the Move or LocationChanged event handler. When the width or height of a form changes, that's a resize , which can be handled in the Resize or SizeChanged event handler. [6] Sometimes one gesture of the mouse can cause all move and size events to happen. For example, resizing a form by dragging the top-left corner would change the location and the size of the form.
The location of the form is in absolute screen coordinates. If you're interested in the location of the form relative to the desktop ”so that, for example, your form's caption never appears underneath the shell's taskbar ”then even if it's on the top edge, as shown in Figure 2.3, you can use the DesktopLocation property. Here's an example: void Form3_Load(object sender, EventArgs e) { // Could end up under the shell's taskbar this.Location = new Point(1, 1); // Will always be in the desktop this.DesktopLocation = new Point(1, 1); // A simpler form of the preceding line this.SetDesktopLocation(1, 1); } Locations are expressed via the Point structure from the System.Drawing namespace, the interesting parts of which are shown here: struct Point { // Fields public static readonly Point Empty; // Constructors public Point(int x, int y); // Properties public bool IsEmpty { get; } public int X { get; set; } public int Y { get; set; } // Methods public static Point Ceiling(PointF value); public void Offset(int dx, int dy); public static Point Round(PointF value); public virtual string ToString(); public static Point Truncate(PointF value); } The PointF structure is very similar to the Point structure, but PointF is used in drawing applications when more precise floating point measurements are required. Sometimes you'll need to convert from a Point to a PointF object to be able to call some methods or set some properties. You can do so without any extra effort: // Can convert directly from Point to PointF Point pt1 = new Point(10, 20); PointF pt2 = pt1; // Yields PointF(10.0f, 20.0f) However, because floating point numbers contain extra precision that will be lost in the conversion, you'll need to be explicit about how to convert from a PointF to a Point object using the static Truncate, Round, and Ceiling methods of the Point class: // Need to be explicit when converting from a PointF to a Point PointF pt1 = new PointF(1.2f, 1.8f); Point pt2 = Point.Truncate(pt1); // Yields Point(1, 1); Point pt3 = Point.Round(pt1); // Yields Point(1, 2); Point pt4 = Point.Ceiling(pt1); // Yields Point(2, 2); The size of a window is reflected in the Size property, also from System.Drawing (Size also has a SizeF counterpart and provides the same capabilities for conversion): struct Size { // Fields public static readonly Size Empty; // Constructors public Size(int width, int height); // Properties public int Height { get; set; } public bool IsEmpty { get; } public int Width { get; set; } // Methods public static Size Ceiling(SizeF value); public virtual bool Equals(object obj); public static Size Round(SizeF value); public virtual string ToString(); public static Size Truncate(SizeF value); } Although the Size property represents the size of the entire window, a form isn't responsible for rendering all of its contents. The form can have edges, a caption, and scrollbars, all of which are drawn by Windows. The part that the form is responsible for is the ClientSize, as shown in Figure 2.3. It's useful to save the ClientSize property between application sessions because it's independent of the current adornment settings the user has established. Similarly, resizing the form to make sure there's enough space to render your form's state is often related to the client area of the form and not to the size of the form as a whole: void Form2_Load(object sender, EventArgs e) { this.ClientSize = new Size(100, 100); // Calls SetClientSizeCore this.SetClientSizeCore(100, 100); } A Rectangle combines a Point and a Size and also has a RectangleF counterpart. Structure RectangleThe Bounds property gives a rectangle of the form relative to the screen, whereas the DesktopBounds property is a rectangle relative to the desktop for top-level windows (and not for child windows). The ClientRectangle property is a rectangle relative to the form itself, describing the client area of the form. Of the three, ClientRectangle tends to be the most used, if for no other reason than to describe which area to use when drawing: void Form1_Paint(object sender, PaintEventArgs e) { Graphics g = e.Graphics; g.FillEllipse(Brushes.Yellow, this.ClientRectangle); g.DrawEllipse(Pens.DarkBlue, this.ClientRectangle); } Also, it's sometimes necessary to convert a point that's relative to the screen to one that's relative to the client or vice versa. For example, the HelpRequest event, generated when the user clicks on the Help button and then clicks on a control, is sent to the handler in screen coordinates. However, to determine which control was clicked on requires the mouse position in client coordinates. You can convert between the two coordinate systems by using PointToScreen and PointToClient: void Form1_HelpRequested(object sender, HelpEventArgs e) { // Convert screen coordinates to client coordinates Point pt = this.PointToClient(e.MousePos); // Look for control user clicked on foreach( Control control in this.Controls ) { if( control.Bounds.Contains(pt) ) { Control controlNeedingHelp = control; ... break; } } } To translate an entire rectangle between screen and client coordinates, you can also use RectangleToScreen and RectangleToClient. Restricting Form SizeOften our careful control layouts or rendering requirements dictate a certain minimum amount of space. Less often, our forms can't be made to take advantage of more than a certain amount of space (although anchoring and docking, described later, should help with that). Either way, it's possible to set a form's minimum or maximum size via the MinimumSize and MaximumSize properties, respectively. The following example sets a fixed height of 200, a minimum width of 300, and a maximum width so large as to be unlimited: void Form2_Load(object sender, EventArgs e) { // min width is 300, min height is 200 this.MinimumSize = new Size(300, 200); // max width is unlimited, max height is 200 this.MaximumSize = new Size(int.MaxValue, 200); } Notice that the code uses the maximum value of an integer to specify that there is no effective maximum width on the form. You may be tempted to use zero for this value instead, but if either the Width or the Height property of the Size used to set the minimum or maximum is nonzero, then both values are used. This would set the maximum size of your form to zero instead of "no maximum." One other setting that governs a form's size and location is WindowState, which can be one of the values from the FormWindowState enumeration: enum FormWindowState { Maximized, Minimized, Normal, // Form.WindowState default value } By default, the WindowState is set to Normal, which means that it's not maximized to take up the entire desktop, nor is it minimized so that none of the form shows at all and only a button is shown in the taskbar. Your program can get or set this property at will to manage the state of your form. However, if you're saving the size and location of your form between application sessions, you may decide to reset the WindowState to Normal so that the size being saved represents the size in the normal state and not the minimized or maximized size: void Form2_Closing(object sender, CancelEventArgs e) { // Capture the properties before the form is gone FormWindowState state = this.WindowState; this.WindowState = FormWindowState.Normal; Point location = this.Location; Size size = this.ClientSize; // ... save state, location and size properties between sessions ... // ... restore properties in Load event ... } For a description of how and where to keep application settings between sessions, read Chapter 11: Applications and Settings. Z-OrderAnother location property that you may let your users influence or keep between sessions is the TopLevel property. So far I've discussed location in terms of x and y. However, as the user switches between windows, Windows also juggles the z-order , which dictates which windows are drawn on top of one another. Furthermore, z-order is split into two tiers. Normal windows are drawn lowest z-order to highest, front to back. Above all the normal windows are the topmost windows, which are also drawn relative to each other, lowest z-order to highest, but no matter the z-order, are always drawn on top of any normal window. For an example of a topmost window, pressing Ctrl+Shift+ESC under many versions of Windows will bring up Task Manager. By default, it's a topmost window and always draws on top of normal windows, whether or not it is the active window. You can change this behavior (I always do) by unchecking the Options Always On Top setting. If Task Manager were implemented using WinForms, it would implement this feature by toggling the TopMost property on its main form. |