When a form isn't lurking in the background behind a notification icon or task bar button, it is visible in the foreground and takes up space in some location on the screen. A form's initial location is governed by the StartPosition property, which can be one of several FormStartPosition enumeration values: enum FormStartPosition { CenterParent = 4, // Center modeless owned forms and modal forms // over owner. Center modeless unowned forms over // currently active form CenterScreen = 1, // Centered over the visible desktop Manual = 0, // You use code to specify the initial start location WindowsDefaultBounds = 3, // Windows is asked for a default location // and size WindowsDefaultLocation = 2 // Windows picks a location staggered from // the top-left corner of the screen // (default) } 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.14 shows the basic size and location properties of a form. Figure 2.14. The DesktopLocation, Location, ClientSize, and Size Properties (See Plate 3)
When the upper-left corner of a form changes, that's a move, which can be handled in either Move or LocationChanged event handlers. When the width or height of a form changes, that's a resize, which can be handled in the Resize or the SizeChanged event handler, the latter being fired after the former. [12] You can also handle ResizeBegin and ResizeEnd for more fine-grained control over resizing. Sometimes, one gesture of the mouse can cause all move and size events to happen. For example, resizing a form by dragging the upper-left corner changes 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, you can instead lean on the DesktopLocation property: void Form1_Load(object sender, EventArgs e) { // Could end up under the shell's task bar 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); } You might want to do this to ensure that your form's caption never appears underneath the shell's task bar, even if it's on the top edge, as shown in Figure 2.14. Locations are expressed via the Point structure, the interesting parts of which are shown here: namespace System.Drawing { struct Point { // Fields static readonly Point Empty; // Constructors Point(int x, int y); // Properties bool IsEmpty { get; } int X { get; set; } int Y { get; set; } // Methods static Point Ceiling(PointF value); void Offset(int dx, int dy); static Point Round(PointF value); override string ToString(); 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 need to convert from a Point to a PointF object to call certain methods or set certain 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 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 (Size also has a SizeF counterpart and provides the same capabilities for conversion): namespace System.Drawing { struct Size { // Fields static readonly Size Empty; // Constructors Size(int width, int height); // Properties int Height { get; set; } bool IsEmpty { get; } int Width { get; set; } // Methods static Size Add(Size sz1, Size sz2); static Size Ceiling(SizeF value); static Size Round(SizeF value); override string ToString(); static Size Truncate(SizeF value); static Size Subtract(Size sz1, Size sz2); } } 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 scroll bars, all of which are drawn by Windows. The area the form is responsible for is marked by ClientSize, as shown in Figure 2.14. 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 Form1_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 only 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 eventwhich is generated when the user clicks on the Help button and then clicks on a controlis passed the mouse position in screen coordinates. [13] However, to determine which control was clicked on, you must have 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 the control that the 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 SizeOur careful control layouts or rendering requirements often 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 and 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 Form1_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, thinking that it is a way of saying "no maximum." However, if either the Width or the Height property of the Size used to set the minimum or maximum is nonzero, then both values of that Size are used for the specified property. That sets the maximum size of our 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: namespace System.Windows.Forms { enum FormWindowState { Maximized = 2, Minimized = 1, Normal = 0 // default } } By default, the WindowState property is set to Normal, that is, visible but neither maximized nor minimized. Your program can get or set this property at will to manage the state of your form. Form Z-OrderIn addition to being located in terms of x and y, forms live in a third dimension known as the z-order, the order that dictates whether one form is drawn above or below another form. Furthermore, z-order is split into two tiers. Normal windows are drawn lowest z-order to highest, front to back. On top of 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, topmost forms are always drawn on top of any normal window. Figure 2.15 illustrates this two-tiered forms z-order. Figure 2.15. Topmost Window Z-Order in Relation to Normal Window Z-Order
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 by unchecking the Options | Always On Top setting. [14] If Task Manager were implemented using Windows Forms, it would implement this feature by toggling the TopMost property on its main form.
|