comp 585 noteset #9 strings and other basic datatypes in ...cov/comp585f15/notes/noteset09.pdf ·...
TRANSCRIPT
COMP 585 Noteset #9 Strings and other basic datatypes in C#
Outline
C# Basics
String Formatting
WinForms
Quick Tour of Namespaces, Classes and Structs, Properties
Events and Delegates
Internal vs. External Event Handling
Mouse and Keyboard Events
Layout
Dock and Anchor Properties
Layout-Aware Controls
Tour of Common Controls
Scrolling
Do-It-Yourself Layout
2
Primitive Types
In Java, primitive types are defined separately from the class libraries or packages. True, there are wrapper classes for
each primitive, but int and Integer are different types. int class Integer { }
double class Double { } … …
In C#, each primitive type is really just an “alias” for a struct that defines that type.
int: alias for struct Int32 { …}
For example: int x = 5; // or Int32 x = 5; Console.WriteLine(x);
int y = int.Parse("345"); // or Int32.Parse("345"); Console.WriteLine(y);
Strings
A String in C# is a collection of Char objects. Each Char object typically represents a single 16-bit Unicode character
code. In some cases, a Unicode character might require 2 Char objects to represent it. In lowercase, the datatype “string”
is just an alias for “String.” There is no difference between them.
String Properties Char[] Chars
int Length
C# classes support an indexer property that allows a composite object like a string to be indexed directly as if it were an
array. Here’s a simple Console I/O example:
string s = "abcde";
String t = "pqrst"; Console.WriteLine(s); Console.WriteLine(t);
Console.Write("Enter a String: ");
String u = Console.ReadLine(); Console.WriteLine("You entered " + u); Console.WriteLine("First character is " + u[0]);
C# Parse Methods
Use the Parse() method for the appropriate datatype to convert a String to a corresponding numeric value: String s = "123";
int x = int.Parse(s); // same as Int32.Parse(s);
C# String.Format Method
The String class provides the String.Format() static method that performs composite formatting. This kind of
formatting is similar to the printf() method provided by the C language. For example in C, you can write the following: int x = 546;
double y = 33.25; printf("x=%d, y=%f\n", x, y);
In this example, the first argument to printf() is called the format string. The other arguments are variables. The values
of the variables are inserted into the format string at the positions indicated by the format specifiers
%d d for decimal (base 10) integer
%f for floating point
3
In C#, use String.Format to perform an equivalent operation. The method takes a variable number of arguments, where
the first argument is a format string, and the remaining arguments are variables whose values are to be substituted into
the format string. The positions for the substitutions are indicated by format items embedded into the format string.
The number of format items embedded into the format string should match the number of arguments that follow the
format string.
Example int x = 3, y = 4, z = 9; string s = String.Format(“x = {0}, y = {1}, z = {2}”, x, y, z);
System.Console.WriteLine(s);
Output is x = 3, y = 4, z = 9
In this example, the expressions “{1}” “{2}” “{3}” etc. are called format items and are embedded into the format string
where desired. After the format string is terminated, the additional arguments to String.Format() follow in a standard
comma separated list. The index of the format item refers to the value of these additional arguments, where argument 0
is the first argument after the format string itself.
Other methods such as Console.WriteLine() also accept composite format expressions: int x = 3, y = 4, z = 9; Console.WriteLine(“x = {0}, y = {1}, z = {2}”, x, y, z);
Standard Numeric Format Strings
To gain more detailed control over detailed formatting of each format item, the item can contain several numeric format
specifiers.
c: currency
d: decimal (base 10) integer
e: scientific notation real number
f: fixed point real number
g: general real number
n: number
r: round-trip [number � string � number yields same value]
x: hexadecimal (base 16) integer
Specifiers are added after the position indicator and a colon separator: {0:f}
{1:g} {2:x}
The format specifier can also define alignment and precision. The meaning of this value varies from format to format.
{0:f3} display 3 digits to the right of the decimal, padding with 0s on the right if necessary
{1:d10} display the integer in ten digits, padding with 0s on the left if necessary
Alignment is specified by a comma plus field width. {2,10:d5} // the “,10” means field width of 10
Examples: double x = 12345.678912345;
double y = 12345.67; string s1 = String.Format("{0:f3} {0:f4} {0:f5}",x); string s2 = String.Format("{0:f3} {0:f4} {0:f5}",y);
System.Console.WriteLine(s1); System.Console.WriteLine(s2);
Output 12345.679 12345.6789 12345.67891
12345.670 12345.6700 12345.67000
4
Other Methods
Use the Split() method to break a string apart based on the position of some character (like Java’s StringTokenizer class
in package java.util).
string delimstring = " ,"; // separators
char[] delims = delimstring.ToCharArray(); // convert to char[] string q = "this is a string,with delimiters"; // string to split
string[] tokens = q.Split(delims); // split it for (int i=0; i<tokens.Length; i++) { // print out tokens
Console.WriteLine(tokens[i]); }
Standard DateTime Format Strings
Format specifiers are also available for dates and times. For example, print out the current date and time using the
default format (struct DateTime is defined in namespace System):
DateTime t = DateTime.Now;
Console.WriteLine(t);
Use the format specifiers to customize:
d: short date pattern
D: long date pattern f: full date, short time
F: full date, long time g: general date, short time G: general date, long time
M or m: month day pattern s: sortable date/time pattern
t: short time pattern T: long time pattern u: universal sortable date/time pattern
U: same as full date/time assuming UTC y or Y: year month pattern
5
Example Program
DateTime t = DateTime.Now; Console.WriteLine("d {0:d}",t); Console.WriteLine("D {0:D}",t);
Console.WriteLine("f {0:f}",t); Console.WriteLine("F {0:F}",t);
Console.WriteLine("g {0:g}",t); Console.WriteLine("G {0:G}",t); Console.WriteLine("M {0:M}",t);
Console.WriteLine("s {0:s}",t); Console.WriteLine("t {0:t}",t);
Console.WriteLine("T {0:T}",t); Console.WriteLine("u {0:u}",t); Console.WriteLine("U {0:U}",t);
Console.WriteLine("Y {0:Y}",t);
Output d 11/9/2006 D Thursday, November 09, 2006
f Thursday, November 09, 2006 12:26 PM F Thursday, November 09, 2006 12:26:39 PM
g 11/9/2006 12:26 PM G 11/9/2006 12:26:39 PM M November 09
s 2006-11-09T12:26:39 t 12:26 PM
T 12:26:39 PM u 2006-11-09 12:26:39Z U Thursday, November 09, 2006 8:26:39 PM
Y November, 2006
6
Namespaces for WinForms Apps
Use the System, System.Windows.Forms, and System.Drawing namespaces.
System
• This namespace is analogous to the java.lang package for Java programs.
• Contains basic classes and structs used in almost any program
• Classes:
o Console
o EventArgs
o Math
o String/string
• Structs:
o Boolean/bool
o Char/char
o DateTime
o Double/double
o Int32/int
System.Windows.Forms
• This namespace defines the containers and controls used to create a WinForm GUI
o Form
� The standard top-level container for a WinForm GUI
• Control
o Base class for most GUI controls
• Common Widgets:
o Button
o ComboBox
o DateTimePicker
o Label
o ListBox
o NumericUpDown
o RadioButton
o RichTextBox
o TextBox
o TreeNode
o TreeView
• Menus
o MainMenu
o Menu
o MenuItem
• Dialogs/Alerts
o FontDialog
o MessageBox
o OpenFileDialog
o PrintDialog
o SaveFileDialog
• Containers
o FlowLayoutPanel
o Panel
o SplitContainer
o TableLayoutPanel
7
System. Drawing
Classes:
Brush/Brushes
Font
Graphics
Icon/Image
Pen/Pens
Structs:
Color
Point
Rectangle
Size
System.Collections
ArrayList
Hashtable
Queue
Stack
System.IO
BinaryReader
BinaryWriter
Directory
File
FileStream
Path
TextReader
TextWriter
System.Threading
Thread
Properties
C# uses its style to manage object properties. Properties are generally capitalized, and public properties appear to be
read and written by direct access and direct assignment. In reality, properties are governed by set and get code blocks
which are similar to set and get methods in Java objects.
Common Form Properties
Anchor, AutoScroll, Autosize, BackColor, Bottom, Bounds, ClientSize, Controls, Dock, Enabled, Focused,
Font, ForeColor, Height, Left, Location, Menu, Parent, PreferredSize, Right, Size, TabIndex, TabStop, Top,
Width
Common Control Properties
Anchor, AutoSize, BackColor, Bottom, Bounds, ClientSize, Controls, Dock, Enabled, Focused, Font, ForeColor,
Height, Left, Location, Margin, MaximumSize, MinimumSize, Padding, Parent, PreferredSize, Right, Size,
TabIndex, TabStop, Visible, Width
Each property has a data type, don’t confuse the property name with its type, you’ll need to know both to make an
assignment. For example, the type of the Location property is Point. So to set a control’s location, you would need to
assign a value of type Point to the property:
Button b = new Button(); b.Location = new Point(25,35);
8
Events and Delegates in Windows Forms
Each event in WinForms is paired with a corresponding delegate. The delegate for the event defines the signature for
functions that can act as a delegate for that event. There are not a large number of unique delegate types, and all delegate
types follow a fairly simple pattern:
Event Delegate Delegate Signature Click EventHandler public void f(Object x, EventArgs args);
Paint PaintEventHandler public void f(Object x, PaintEventArgs args);
MouseEnter
MouseHover
MouseLeave
MouseDown
MouseUp
MouseClick
MouseDoubleClick
MouseWheel
MouseMove
EventHandler
MouseEventHandler
public void f(Object x, EventArgs args);
public void f(Object x, MouseEventArgs args);
KeyDown
KeyUp
KeyPress
KeyEventHandler
KeyPressEventHandler
Public void f(Object x, KeyEventArgs args);
public void f(Object x, KeyPressEventArgs args);
To summarize, in order for the programmer to write a delegate that responds to an event, the required method signature
for that delegate must be known. Note the pattern for all delegate types:
• All methods have return type = void
• All methods have two input parameters
o The first parameter is a reference of type Object.
o The second parameter is a reference of type EventArgs or some derived class. This is actually the only
element of the delegate that changes. And it changes in a uniform way from delegate to delegate.
Note: events and handlers are organized similarly in WPF, but the exact names of the delegates and event arguments are
similar but not identical to their corresponding elements in Windows Forms.
9
Internal vs External Event Handlers
There are two approaches to responding to events for Windows Forms GUIs:
Example: Click Event for Button Object
External Response: Write a Delegate Method and Attach it as an Event Handler
Delegate for Click Event is void EventHandler(Object object, EventArgs args)
void f(Object obj, EventArgs args) { // � TWO args
... Console.WriteLine(“…”);
}
Button b = new Button();
b.Click += f;
Internal Response: Write a Derived Class and Override the OnClick() Method
Each class has a built in method whose name begins with the prefix “On” for each event that the objects of that class can
respond to.
class Button {
…
protected void OnClick(EventArgs args) { … } // � ONE arg
}
The programmer can write a derived class and override the definition of the OnClick method to implement an internal
response to the event.
class CustomButton : Button {
… protected override void OnClick(EventArgs args) {
base.OnClick(args); ...
} }
The programmer can also combine them (do both). If doing both, it is important that the OnClick() override call the
inherited version base.OnClick(). It is the base method that is responsible for invoking the externally attached delegates.
10
Mouse And Keyboard Events
Providing keyboard alternatives for most mouse operations is an essential part of good GUI design. Programming with
accessibility in mind is already encouraged by many organizations and is gradually becoming required policy.
The Four Basic Mouse Events
• MouseDown
• MouseUp
• MouseMove
• MouseWheel
• Only one control receives mouse events, and that control must be visible and enabled.
• Any control that is derived from class Form receives mouse events only when the mouse is positioned over the
control’s client area (exception during mouse capture).
MouseEventArgs Properties
• int X
• int Y
• MouseButtons Button
• int Clicks
• int Delta
Example: Check to see if the Right Mouse Button was Pressed
protected override void OnMouseDown(MouseEventArgs args) { if (args.Button & MouseButtons.Right != 0) … }
Entering, Leaving, Hovering The last 3 mouse-related events are
• MouseEnter
• MouseLeave
• MouseHover
Entering and leaving are similar to the equivalent Java events. The MouseHover event is generated when the mouse first
stops moving after entering. There can be at most one hover event between an enter and exit event.
11
Example: Scribble App
Implement a C#/WinForms app that is equivalent to the Java Swing scribble exercise discussed earlier. One issue to
resolve is the lack of a MouseDrag Event in C#/WinForms. You can simulate a drag operation by implementing your
own state machine and keeping track of the state of the mouse across several events.
To implement the scribble app, you will need to collect a set of points generated across the GUI during a mouse drag.
Then in response to a paint event, you will draw a series of line segments to connect the points to render the scribble onto
the GUI.
OnMouseDown:
Start tracking
Remember location of mouse event in “ptLast”
OnMouseMove:
If not tracking return
Remember location of mouse event in “ptNew”
Draw line from ptLast to ptNew
ptLast = ptNew
OnMouseUp
Stop tracking
Limitations
Doesn’t remember the drawing between OnPaint events
Screen refresh will erase the drawing
ScribbleWithSave
Need to save an array of points, and redraw them during OnPaint. Note that .NET provides a convenient Graphics
method named DrawLines():
public void DrawLines(Pen pen, Point[] pts);
But each MouseDown-MouseMove-MouseUp sequence generates a different array.
Fix: use two ArrayList collections
• For each drawing sequence accumulate an ArrayList of points
• During the current sequence, connect the current series of points as before, since it won’t get saved until the
MouseUp event.
• At the end of the sequence, convert the ArrayList to an array and add it to an ArrayList of arrays.
• During OnPaint, redraw each array of points in turn.
12
Keyboard
Keyboard input is directed to the active control on the active form. The ActiveForm static property of class Form
always tracks which form this is. This is related to the concept of keyboard focus.
Form Methods
void Activate()
Form Events
Activated
Deactivate
Keys and Characters
Two views of a keyboard
• Collection of keys
• Character code generator
Key Groups
• Toggle Keys (capslock, numlock, scrolllock, insert)
• Shift Keys (shift, ctrl, alt)
• Noncharacter Keys (function keys, arrow keys, pause, delete)
• Character Keys (characters, numbers, punctuation, spacebar, tab, backspace, esc, enter)
Events
• KeyDown (KeyEventHandler, KeyEventArgs)
• KeyUp (KeyEventHandler, KeyEventArgs)
• KeyPress (KeyPressEventHandler, KeyPressEventArgs)
KeyEventArgs Properties
• Keys KeyCode
• Keys Modifiers
• Keys KeyData
• bool Shift
• bool Control
• bool Alt
• bool Handled
• int KeyValue
KeyPressEventArgs Properties
• char KeyChar
• bool Handled
Most programs that need to monitor key events will only monitor the KeyDown event, and only for cursor-movement
keys, insert, and delete. It’s also common to combine cursor-movement keys with modifier keys like “Shift”.
13
What Mouse/Keyboard Events Does a Button Receive? What Events Does a Form Receive?
To respond to a button click, provide a handler with the right signature and register it with the Click event. The “Click”
event is similar to the Java “ActionEvent”.
If a single button is placed on a Form, when that form becomes the active form, the button receives the focus, and
therefore (almost) all the keyboard input. The form receives no keyboard input in this case.
There are a few keystrokes that are handled in a special way. Neither the button nor the form receives them:
• KeyDown event for Enter, Tab, Arrow Keys
• KeyPress event for Enter, Tab
These keystrokes are used for GUI navigation, and so are not available to the form or its controls.
A Control can receive keyboard and mouse input only if it is both visible and enabled.
Alignment and Appearance
Set the TextAlignment control property to one of these values:
ContentAlignment Enumeration:
TopLeft, TopCenter, TopRight, MiddleLeft, MiddleCenter, MiddleRight, BottomLeft, BottomCenter,
BottomRight
Set the FlatStyle control property to one of these values:
FlatStyle Enumeration
Flat, Popup, Standard, System
14
Layout in Windows Forms
Windows Forms is gradually providing more support for the concept of dynamic layout, but in general the support is not
a good as in Java. The original style in Windows-based GUIs using VB was absolute positioning of controls on a fixed-
size form.
Problems
• Platform and device independence issues such as monitor resolution, default font, etc.
• Old style was for the form to impose a size on its controls.
• New style is for the form to accommodate the size desired by its controls.
Property AutoSize
• Tells a control to pick its size in a reasonable way.
• false by default, generally should set it to true
• Property AutoSizeMode options: GrowAndShrink, GrowOnly
Anchor and Dock Properties
These are properties of individual controls that provide some simple layout behavior. But they are not a substitute for a
real layout manager. Properties must be manipulated for individual control, global coordination between controls is
minimal.
Dock Property
The possible values of this property are defined by the DockStyle Enumeration: None, Top, Bottom, Left, Right, Fill.
These are absolute values and cannot be combined. For example, DockStyle.Top causes a control to be held against the
top of the container, and stretched to fill the width of the container.
Guidelines
• Default value of Dock property is None
• If one control sets Dock to a non-default value, other controls should too
• Only one control should use the Dock value Fill.
Docking
Docking is different from Anchoring. Docking causes a control to be pressed flush against the indicated edge, then
stretched to fill the edge.
DockStyle Enumeration
None 0
Top 1
Bottom 2
Left 3
Right 4
Fill 5
These are not bit values that can be bitwise-ORed together. The control defines a “Dock” property to hold these values.
Docking causes a control to be pressed flush against the indicated side, and expanded (resized) along one dimension to
occupy the entire edge of the container.
Anchor Property
Possible values given by the AnchorStyles Enumeration: None, Top, Bottom, Left, Right. These are bit vector values so
they can be combined with bitwise OR. The Anchor property causes a control to remain a fixed distance from the
indicated container edge or edges, as the size of the container changes.
Dock and Anchor settings could be duplicated by appropriate logic explicitly added to the OnResize() method of the
parent form for the controls. A carefully constructed handler for OnResize() could play the role of a simple layout
manager (true in Java Swing as well).
15
Guidelines
• Default value of Anchor property is Top | Left (not None).
• Dock and Anchor don’t mix well on the same form.
• Exception is when using Dock to position a tool bar to one edge, then position other controls in the remaining space
with Anchor.
• Better to introduce a Panel with Dock value Fill, and attach Anchored controls to the panel.
• Some layout alignment problems can be solved with nested panels that dock sequentially across or down the
container.
Note that there is an implicit relationship between the Anchor property and the Resize event. A Resize event will impact
the Location and Size properties of any control that does not have the default value of the Anchor property. This
happens automatically, the programmer does not have to explicitly program the OnResize() handler to do this.
More specifically:
• Anchor property for Control c includes two opposite sides (Top + Bottom, or Left + Right): then Resize changes
Size property for Control c
• Anchor property for Control c specifies one side: then Resize changes the Location property for Control c
• Anchor property for Control c specifies AnchorStyles.None: then Resize changes the Location property to keep
Control c in the same relative location within the form.
16
Example: Dock Style Buttons
This example illustrates the effect of different Dock values for a group of buttons. using System; using System.Drawing;
using System.Windows.Forms;
using System.Threading; using System.Collections; using System.Collections.Generic;
public class Layout1 : Form {
Button[] btns;
public Layout1() : base() { btns = new Button[5];
for (int i=0; i<btns.Length; i++) { btns[i] = new Button(); btns[i].Text = "Btn " + i;
btns[i].Parent = this; }
btns[0].Dock = DockStyle.Top; btns[1].Dock = DockStyle.Right; btns[2].Dock = DockStyle.Bottom;
btns[3].Dock = DockStyle.Left; btns[4].Dock = DockStyle.Fill;
} public static void Main(string[] args) {
Layout1 form = new Layout1(); Application.Run(form);
} }
17
Example: Anchor Style Buttons
This example illustrates the effect of different Anchor values for a group of buttons.
using System;
using System.Drawing; using System.Windows.Forms;
using System.Threading; using System.Collections;
using System.Collections.Generic;
public class Layout2 : Form { Button[] btns;
public Layout2() : base() {
btns = new Button[5]; for (int i=0; i<btns.Length; i++) { btns[i] = new Button();
btns[i].Text = "Btn " + i; btns[i].Parent = this;
} btns[0].Anchor = AnchorStyles.Top; btns[1].Anchor = AnchorStyles.Right;
btns[2].Anchor = AnchorStyles.Bottom; btns[3].Anchor = AnchorStyles.Left;
btns[4].Anchor = AnchorStyles.None; }
public static void Main(string[] args) { Layout2 form = new Layout2();
Application.Run(form); }
}
18
Panels and Containers: Panel, FlowLayoutPanel, TableLayoutPanel
A Panel is a container object for visual grouping of controls. A FlowLayoutPanel is a container object that simulates
HTML flow layout (like Java FlowLayout). Flow can be either horizontal or vertical. Also, a specific control can be
tagged as a row or column break. This control will then start a new row or column.
Example
Here’s an example using FlowLayoutPanel, with randomly sized buttons and several choices of Anchor property. This
example also demonstrates two other useful techniques.
• The AutoScroll property of class Form
• The Application.EnableVisualStyles() method call.
using System; using System.Drawing;
using System.Windows.Forms;
public class Layout3 : Form { public Layout3() : base() {
FlowLayoutPanel p = new FlowLayoutPanel(); p.Parent = this;
p.Dock = DockStyle.Fill; p.AutoScroll = true;
Random r = new Random(); for (int i=0; i<40; i++) {
Button btn = new Button(); int x = r.Next()%100; int y = r.Next()%100;
btn.Size = new Size(30+x,20+y); btn.Parent = p;
btn.Anchor = AnchorStyles.None; // or Top,Bottom btn.Text = String.Format("{0}",i); }
}
public static void Main(String[] args) { Layout3 form = new Layout3(); Application.EnableVisualStyles();
Application.Run(form); }
}
19
TableLayoutPanel
Similar to GridBagLayout in Java. Allows you to layout components in a grid. By default, the grid has one column, set
the ColumnCount property to a different value for more columns.
Example
In this example, create a TableLayoutPanel and attach it to a Form. Use the Dock property to make the
TableLayoutForm completely fill the client area of the Form
class Demo : Form {
public Demo() { TableLayoutPanel p = new TableLayoutPanel();
this.Controls.Add(p); p.Dock = DockStyle.Fill;
} }
Set up the desired number of rows and columns p.RowCount = 4; p.ColumnCount = 3;
Attach controls in row-major format for (int i=0; i<12; i++) {
Button b = new Button(); b.Text = …;
b.AutoSize = true; p.Controls.Add(b);
}
Or attach individual controls at specific grid positions Button b = new Button(); b.Text = …; b.AutoSize = true;
p.SetRow(b,2); p.SetColumn(b,1);
Individual controls can span multiple rows or columns: p.SetColumnSpan(b,2);
You can set a row or column to have a specific style, which controls how the row or column behaves when the container
is resized. Style can be auto, absolute or percent. for (int i=0; i<6; i++) {
p.RowStyles.Insert(i,new RowStyle()); } p.RowStyles[0].SizeType = SizeType.AutoSize;
p.RowStyles[1].SizeType = SizeType.AutoSize; p.RowStyles[2].SizeType = SizeType.AutoSize;
p.RowStyles[3].SizeType = SizeType.Percent; p.RowStyles[3].Height = 25; p.RowStyles[4].SizeType = SizeType.Absolute;
p.RowStyles[4].Height = 10; p.RowStyles[5].SizeType = SizeType.AutoSize;
20
Example
using System; using System.Drawing; using System.Windows.Forms;
public class Layout4 : Form {
public Layout4() : base() { TableLayoutPanel p = new TableLayoutPanel();
p.AutoSize = true; p.Parent = this;
p.CellBorderStyle = TableLayoutPanelCellBorderStyle.Single; for (int i=0; i<10; i++) {
Button btn = new Button(); btn.Text = "Button " + i;
btn.AutoSize = true; btn.Parent = p; }
}
public static void Main(string[] args) { Layout4 form = new Layout4();
Application.EnableVisualStyles(); Application.Run(form);
} }
We can change the number of columns, and furthermore allow some buttons to span multiple columns.
… p.ColumnCount = 2;
for (int i=0; i<10; i++) { Button btn = new Button();
if (i==2) { p.SetColumnSpan(btn,2); btn.Dock = DockStyle.Fill;
} btn.Text = "Button " + i;
btn.AutoSize = true; btn.Parent = p; }
21
Padding and Margin
Padding is a way to create extra space inside a control. For example, if a button is autosized, the boundary of the button
will conform closely to the boundary of the text label that appears on the button. Use padding to force the button to take
up more space.
Button btn = new Button(); btn.Padding = new Padding(20);
btn.AutoSize = true;
Margin is also a property of type Padding, but is used in conjunction with FlowLayoutPanel and TableLayoutPanel.
22
Simple Layout Effects with Docking Styles
• Create a Form
• Attach one component with Dock set to DockStyle.Left
• Attach a second component with Dock set to DockStyle.Fill.
• This results in a simple “BorderLayout” arrangement
For example, in “PrimevalNotepad.cs”, the component to the left is a TreeView, and the component in the fill position is
a TextBox. A more general example is to use a Panel object in the “fill” position.
Plain Panels
Here’s an excerpt from “SimplePanel.cs” Ch 3 Petzold Text 2. Two controls are placed onto a form: a Panel and a
TreeView. By careful use of DockStyle settings, simple layout manager logic is implemented. More specifically, this
implements a rudimentary “BorderLayout”. One component is a “plain” panel that “fills” most of the region of its
containing form (like BorderLayout.CENTER). Other controls like the TreeView are constrained to “hug” the edge of
the form (like BorderLayout.WEST, etc.).
Panel pnl = new Panel(); pnl.Parent = this;
pnl.Dock = DockStyle.Fill; // BorderLayout.CENTER pnl.AutoScroll = true;
TreeView tree = new TreeView(); tree.Parent = this;
tree.Dock = DockStyle.Left; // BorderLayout.WEST tree.Nodes.Add("tree");
The “plain” or “do-nothing” panel is analogous to class JPanel in Java Swing. Other controls (Button, TextBox, etc.) can
be attached to the Panel.
23
More Examples with Anchors
Here’s an example from Petzold Text 2 (Ch 3). It creates a form containing several rows of controls. Each row contains
a label and a text box.
//---------------------------------------------
// AnchorFields.cs (c) 2005 by Charles Petzold //--------------------------------------------- using System;
using System.Drawing; using System.Windows.Forms;
class AnchorFields : Form {
[STAThread] public static void Main()
{ Application.EnableVisualStyles(); Application.Run(new AnchorFields());
} public AnchorFields()
{ Text = "Anchor Fields"; int iSpace = Font.Height;
int y = iSpace;
for (int i = 0; i < 4; i++) { Label lbl = new Label();
lbl.Parent = this; lbl.AutoSize = true;
lbl.Text = (new string[] { "Name:", "Address:", "Job:", "Very personal information:" })[i]; lbl.Location = new Point(iSpace, y);
TextBox txtbox = new TextBox();
txtbox.Parent = this; txtbox.Location = new Point(lbl.Right + iSpace, y); txtbox.Size = new Size(ClientSize.Width - iSpace - txtbox.Left,
txtbox.Height); txtbox.Anchor |= AnchorStyles.Right;
y = txtbox.Bottom + iSpace; }
} }
24
The basic strategy is still absolute positioning, with the position of the label on each row specified in terms of x-y
coordinates. But notice that the text box uses relative positioning (in part). It locates itself according to the right edge of
the previous control on the same row. The right edge of the text box is calculated to mostly fill the row, based on how
wide the form currently is.
More layout-like logic is provided by setting the anchor style of the text box. By including the setting for
AnchorStyles.Right, the text box will now expand to fill the width of the form whenever the user resizes the form.
Since the default anchor style is “top” + “left”, the effect of the setting for each text box is the same as the more explicit
statement:
txtbox.Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right;
The effect is somewhat subtle here. The size of the textbox depends on the position of its left edge, so the “Location”
property must be set before the “Size” property. Also, the AnchorStyles setting causes the size of the control to be
recalculated during a “Resize” event. This is not obvious from a quick glance at the code.
25
Other Controls
Summary of Common Controls
• Label
• Button
• TextBox
CheckBox
Properties
• bool Checked
• bool AutoCheck
The Checked property indicates the current logical value of the check box control. The AutoCheck property causes the
Checked property to automatically toggle in response to a Click event on the control.
Events
If AutoCheck property is true, clicking on a check box generates a Click event. Regardless of the state of the
AutoCheck property, changing the value of the Checked property generates a CheckedChanged event. Imagine
writing a subclass of class CheckBox called MyCheckBox, and think about what the following handler for class
MyCheckBox would do:
protected override void OnCheckedChanged(EventArgs arg) { Checked ^= true;
}
It uses the exclusive-OR operation to toggle the value of the Checked property. But changing this property generates
another CheckedChanged event, so we’re stuck in a loop.
If AutoCheck is set to false, the check box control no longer responds to Click events.
26
RadioButton
Similar to CheckBox. Uses Checked and AutoCheck properties in a similar way. Unlike Java radio buttons, Windows
Forms buttons automatically provide mutually exclusive behavior in a default way: all radio buttons attached to a form
are automatically mutually exclusive. If you want more fine-tuned control (eg, two groups of radio buttons that are
mutually exclusive only internally to a group), you have to use a GroupBox class.
Example
Four mutually exclusive buttons; they implicitly form a group because they have a common parent (current form = this).
class RadioTest : Form {
RadioButton b1, b2, b3, b4; public RadioTest() {
b1 = new RadioButton();
b1.Location = new Point(50,50); b1.Text = "choice 1"; b1.Parent = this;
b2 = new RadioButton();
b2.Location = new Point(50,b1.Bottom+10); b2.Text = "choice 2"; b2.Parent = this;
b3 = new RadioButton();
b3.Location = new Point(50,b2.Bottom+10); b3.Text = "choice 3"; b3.Parent = this;
b4 = new RadioButton();
b4.Location = new Point(50,b3.Bottom+10); b4.Text = "choice 4"; b4.Parent = this;
} }
27
Example
Two groups of radio buttons, each group contained in a GroupBox object. The GroupBox has the extra benefit of
visually clarifying the grouping (bounding box, text label, etc.)
class RadioTest : Form {
RadioButton b1, b2, b3, b4; public RadioTest() {
GroupBox box1 = new GroupBox();
box1.Parent = this; box1.Text = "Group 1"; box1.Location = new Point(50,50);
b1 = new RadioButton();
b1.Location = new Point(10,20); b1.Text = "choice 1"; b1.Parent = box1;
b2 = new RadioButton();
b2.Location = new Point(10,b1.Bottom+10); b2.Text = "choice 2"; b2.Parent = box1;
GroupBox box2 = new GroupBox(); box2.Parent = this; box2.Text = "Group 2";
box2.Location = new Point(50, box2.Bottom + 50);
b3 = new RadioButton(); b3.Location = new Point(10,20); b3.Text = "choice 3";
b3.Parent = box2;
b4 = new RadioButton(); b4.Location = new Point(10,b3.Bottom+10); b4.Text = "choice 4";
b4.Parent = box2; }
}
28
Scrolling and ScrollBars
For some applications, scrolling behavior for a container control like a Form or Panel can be defined by using the
AutoScroll and AutoScrollMinSize properties.
Example
class ScrollTest : Form { public ScrollTest() {
AutoScroll = true; AutoScrollMinSize = new Size(300,400); }
protected override void OnPaint(PaintEventArgs args) {
Graphics g = args.Graphics; g.DrawRectangle(Pens.Black,50,50,100,150); }
public static void Main() {
Application.Run(new ScrollTest()); } }
In this example, scroll bars appear whenever the size of the form is reduced below the specified minimum size. To make
the application more practical, the minimum size should be made to depend on the contents of the form (either a drawing
or a collection of controls).
Scroll bars can be added explicitly if needed. This is discussed in Petzold.
Properties
• int Value
• int Minimum
• int Maximum
• int SmallChange
• int LargeChange
Events
• ValueChanged (EventArgs)
• Scroll (ScrollEventArgs)
See ColorScroll Example in Petzold Ch 12. This uses three ScrollBar controls as if they were JSlider controls to set the
color of a panel. An alternative is the TrackBar control, which is more like the JSlider.
29
Variations on Absolute Positioning: Simple Layout Logic
Petzold presents several programs that reveal how much work the programmer must do to imitate only a part of the
functionality that is performed effortlessly (for the programmer) by the layout managers for Java containers. For
example, one of Petzold’s techniques is to keep controls approximately in the center of their form. This requires that the
form receive Resize events, then reset the control’s locations. To improve upon this approach, the programmer could
add logic to create complex dependencies on the size of the form that impacts: the buttons’ sizes, relative orientations,
length of text label, choice of label font, etc. At some point, you have implemented a rudimentary layout manager.
More will be said about automatic layouts in Windows Forms later. For now, we’ll stick to absolute positioning and its
variations.
The techniques for simulating simple layout logic, without actually having a real layout manager, involve the following:
Control Properties
• Anchor
• Dock
• AutoSize
• Location
• Size
• ClientSize
Control Events
• OnResize
In effect, implementing the OnResize() method can simulate a simple layout manager. As of 2.0 of the SDK, additional
containers have been introduced to automatically provide some of this functionality.
Example: Simple Flow Layout Simulation
There are two situations when layout logic must be addressed:
• When the form is first constructed
• Whenever the form is resized
Define a method that performs the job of a layout manager. The method must look at the size of the controls and the size
of the form. Then call this method from both the constructor and the OnResize() event handler.
30
This example creates 10 buttons then lays them out based on the logic of the flow layout manager.
using System; using System.Drawing; using System.Windows.Forms;
public class ResizeForm : Form {
int bcount = 10; Button[] buttons;
// constructor creates buttons, uses separate method to set locations
public ResizeForm() { buttons = new Button[bcount];
for (int i=0; i<bcount; i++) { buttons[i] = new Button();
buttons[i].Parent = this; buttons[i].Text = "Btn " + i; }
DoLayout(); // sets locations of all buttons }
private void DoLayout() {
Size csize = this.ClientSize;
int cx = 10; int cy = 10;
for (int i=0; i<bcount; i++) {
int tempx = cx + buttons[i].Size.Width + 10; // if current row will overflow the form, start a new row
if (tempx > csize.Width) { cx = 10;
cy = cy + buttons[i].Size.Height + 10; } buttons[i].Location = new Point(cx,cy);
cx = cx + buttons[i].Size.Width + 10; }
} // add method to do layout when form is resized
protected override void OnResize(EventArgs args) { Console.WriteLine("form was resized.");
DoLayout(); // reset locations based on new form size Invalidate(); // force form to be repainted }
}
class Driver { public static void Main() {
ResizeForm form = new ResizeForm(); Application.Run(form); }
}
31
How To Generalize
This example introduces a useful idea, but the implementation shown is not fully general. How would the logic have to
be modified for an arbitrary collection of components?
Step 1: instead of using a customized array to hold references to buttons, the general version would access all members
of the form’s predefined “Controls” property.
Step 2: instead of assuming that every control is the same size, the general version would have to calculate the width and
height of the current row of controls by checking the Size property of each control as it is added to the current row.
With these adjustments, the approach would now be a good approximation to flow layout from the Java JPanel class.
TabStop and TabIndex
When a form is active, one control has the focus. If the user presses the TAB key, the focus moves to the next control in
sequence. Repeatedly pressing the TAB key eventually causes the focus to move throughout the entire form and wrap
around back to the first control.
This behavior is determined by two control properties:
• bool TabStop
• int TabIndex
In order for a control to receive the focus at all, its TabStop property must be set to “true”. Given the set of all controls
whose tab stop property is true, the tab index property defines the order in which the focus moves from control to control.
The initial value of the index is defined by the order in which the controls were programmatically added to the form, but
the programmer can change them.
In general, tab order is an important property for the programmer to notice. Many “power users” of applications depend
on reasonable tab order behavior in order to perform rapid navigation of the form without lifting their hands off the
keyboard to use the mouse. I’ve listened to conversations between clients and developers where the client is
complaining about unexpected tab order and the developer has to fix it.