Upload
others
View
6
Download
0
Embed Size (px)
Citation preview
COMP 585 Noteset #8
Microsoft APIs for GUI App Development
History
Win32 API
A large collection of global functions to build and configure GUI components. You could call this a “struct” approach to
GUI building: no true OOP encapsulation and very little abstraction. The programmer had to write large complex
programs that interacted with the platform at a very low level. Wikipedia entry quotes well-known Windows
programming author Charles Petzold as observing that the C “Hello, World” program in Windows SDK 1.0 was 150
lines long, with a 20 line .rc (resource) file.
Microsoft Foundation Classes (MFC)
An early attempt at an OOP model for GUI construction, imperfect by today’s standards, but very successful and
influential at the time, and still in use today. Used primarily with C++. MFC succeeded in hiding much – but not all – of
the Win32 level code behind a cleaner higher-level OOP facade. The weakness was that a significant number of
operations required use of Win32 API calls, thus frequently breaking the OOP model. One source of frustration for the
programmer was that the time invested in learning MFC did not eliminate the need to learn the original Win32 API. Still
very much alive for legacy code, but not currently emphasized by Microsoft.
.NET
The .NET framework is Microsoft’s current software framework for the development of Windows applications. It is a
departure from Microsoft’s previous approach, in that source code is compiled to an intermediate, platform-independent
language called Common Intermediate Language (CIL), which is then interpreted by the Common Language Runtime
(CLR). This is analogous to the Java compiler producing bytecodes which are interpreted by the JVM. A significant
element of .NET is an extensive Framework Class Library (FCL). API documentation for the class library is available
at the MSDN (Microsoft Developer’s Network) website. Another major accomplishment of the .NET framework is to
provide a uniform API for all supported programming languages. Previously, Microsoft provided separate libraries for
different languages – C++, VB, etc. – with the result that the different libraries were rarely in sync. In .NET, there is one
underlying library, so no matter the source language, a .NET API function or method call .NET will hit the same code.
Windows Forms or WinForms (Part of .NET)
Rather than defining packages to import as in Java, .NET divides its class library into hierarchical namespaces, which
are referenced at the top of a source file that needs them. In C#, the “using” directive references a namespace. The code
is stored in dynamic link library (.DLL) files. For GUI programming the System.Windows.Forms namespace is the
basis for relatively simple GUIs built around the Form class. The Form plays the role of the JFrame in a Java Swing app.
Graphics are based on an older Microsoft standard called Graphics Device Interface (GDI), which is fast becoming
obsolete by today’s standards, at least in areas like gaming that require high performance animation. With the release
of .NET 3.0, Microsoft introduced a new GUI development standard called Windows Presentation Framework (WPF).
This leaves the status of WinForms unclear. Microsoft is still supporting it in maintenance mode (bug fixes) but there are
few new features being added. WinForms is very much alive and in wide use in some corporations, with a significant
code base for front end apps.
Windows Presentation Foundation or WPF (Also Part of .NET)
WPF represents Microsoft’s current approach to GUI development. It has a similar relation to WinForms as Java Swing
has to AWT. WinForms development is very much alive, but Microsoft’s emphasis has switched to WPF. WPF uses a
modern graphics API – DirectX. New namespaces have been introduced – System.Windows and
System.Windows.Controls. Programmer should be careful to keep WinForms and WPF namespaces separated in their
thinking. WPF introduces an XML-derived markup language called XAML (pronounced “ZAM-ul”) or eXtensible
Application Markup Language as an alternative way to define layout. WPF GUIs can be defined through a mixture of
XAML files for layout and code files for event handlers (called delegates in C#). Code that accompanies and supports
XAML layout is called “code-behind.” Use of XAML is optional, and GUIs can still be built fully programmatically
from source code files. Separation of the design of an app’s layout from the coding of the app’s behavior gives software
developers new options for using their development team more effectively. Layout design can be assigned to a design
specialist who doesn’t also need to be a coding specialist. Similarly, the coding specialist can focus on coding, and not
worry about specialized graphic design tasks that require a different skill set. WPF is still very much alive but its long-
term future is currently being debated in the blogosphere.
COMP 585 Noteset #8 2
Silverlight
Initially, Silverlight was a browser plug-in for streaming video, it became Microsoft’s initiative for developing Rich
Internet Applications (RIAs), similar to Java FX or Adobe Flex (Flex is a programming-oriented development
environment for Flash applications). Microsoft has now officially deprecated it.
Resources for Windows Forms and WPF Development
Since they are part of the .NET framework, WinForms and WPF are currently tightly bound to Microsoft Windows,
support for other platforms is be minimal, although some projects are in the works. The Mono Project has implemented a
subset of the .NET framework for Linux, but there’s no timeline for completion. Microsoft announced in April 2015 that
it would be releasing their own official version of .NET for Linux and Mac, but it is a work in progress.
If you plan to work only from Mac platform, one solution is to set up a virtual environment such as Virtual Box or
VMWare, create a Windows OS instance in this virtual environment, and install the development tools into that virtual
instance. Your Mac hardware will have to meet at least minimum reasonable requirements for RAM/HD/CPU to support
the virtual environment solution. Another solution is to configure your Mac for dual boot and boot directly to Windows
OS for WPF related work. This solution will also require reasonably up-to-date RAM/HD/CPU configuration.
Go to the Microsoft Developer’s Network (MSDN) site at http://msdn.microsoft.com and download the latest version of
Visual Studio. There are many editions, but the Visual Studio Community Edition 2015 is free and has all the features
needed for the projects in this class. The install will include the most recent .NET framework version (4.6), the IDE, and
command line tools for command line access to the compilers.
Extensive API documentation of the .NET framework class library is also available at MSDN. This is a complex site
that is frequently reorganized, so a hardcoded URL in the class notes isn’t a reliable way to give directions. The easiest
way to get to the start page for the documentation is to google “MSDN .NET framework class library” and look for the
first link that takes you to a page on msdn.com. Just as Java organizes documentation by package, the .NET FCL
organizes documentation by namespace. It’s much easier to navigate if you know which namespace your class or other
element is in. If you don’t, your best bet is to use the search box at the top of the documentation page. From the
documentation page, find the tree-based index near the upper left of the page, and start with the .NET Development item:
.NET Development �
.NET Framework 4.6 (or whatever version you’re using) �
.NET Framework Class Library
From this point, the tree-based index is opened to the complete set of namespaces for .NET. It’s a big set and we only
need a handful for the programs in this course.
Intro to C#
.NET applications can be developed using several different languages: C++, C#, VB, and others. Of the popular
languages, C# seems to be the one Microsoft promotes as the default language for .NET development. It is a strict OOP
language with syntax similar to the C/C++/Java family. At a lower level, it is significantly different, it is not simply
Microsoft’s version of Java.
Tools
A clean install of Visual Studio will install the required .NET framework as well as the Visual Studio IDE. The C#
compiler can also be accessed from the command line. WinForms apps have a trivial build process and can be built by a
command line compile. WPF apps cannot, a WPF app is a more complex mix of C# source files and XAML files, the
build process is more complex and cannot be easily run as a series of commands at the command prompt, it will need the
Visual Studio IDE to complete it.
It is not a goal of the course to cover C# in depth, but I will present here an overview of some important features that
directly impact how the language is used with the WinForms and WPF APIs. We’ll start with some basic C# programs
that don’t use the GUI APIs. For these simple examples, we will use the csc (C# compiler) to create an executable, then
run the executable, directly from the Windows command line interface (Command Prompt or (better) PowerShell).
COMP 585 Noteset #8 3
The Hello, World! Program (Console Version)
Type the following into a file named ConsoleHelloWorld.cs
class ConsoleHelloWorld { public static void Main() { System.Console.WriteLine("Hello, world!"); } }
From the command prompt, compile the class file with the C# compiler csc
% csc ConsoleHelloWorld.cs
The result is an executable ConsoleHelloWorld.exe. Execute it by typing its name at the prompt
% ConsoleHelloWorld
Output is directed to command prompt console window. If the current directory “.” is not in your path, you will need to
run it with the command % .\ConsoleHelloWorld
A couple of points about C# programs to notice even on this simple version:
• C# source files end with file name extension .cs. The name of the file is not required to match the name of the
class as in Java.
• The entry point to the program is a method named “Main()” with a capital-M. The C# convention is to name
methods to start with a capital letter. The Main() method can omit the argument list if it doesn’t use it. If it is
included, it’s the same as the signature for “main()” in a Java program: Main(String[] args) or Main(string[]
args).
• The String datatype is aliased to “string,” so you can use either String or string, they are identical in meaning.
• The rough equivalent to Java’s “System.out.println()” is “System.Console.WriteLine()”
COMP 585 Noteset #8 4
The Hello, World! Program (WinForms Version from Charles Petzold)
Jumping ahead, here is a GUI-based Hello, World program using WinForms. There are many new features thrown in to
this example, but they will be explained in detail later.
using System; // namespaces using System.Drawing; using System.Windows.Forms; class HelloWorld : Form { // class HelloWorld inherits from class Form // entry point for the app: Main() public static void Main() { HelloWorld hw = new HelloWorld(); // instantiate HelloWorld object Application.Run(hw); // run the app with Form object } // constructor for class HelloWorld public HelloWorld() { this.Text = "Hello World"; // set Text property of Form this.BackColor = Color.White; // set BackColor property of Form } // override the OnPaint() method of class Form protected override void OnPaint(PaintEventArgs pea) { Graphics grfx = pea.Graphics; grfx.DrawString("Hello, Windows Forms!", Font, Brushes.Black, 0, 0); } }
Some points of explanation:
• Instead of importing packages, C# uses namespaces with the “using” directive. If you don’t add a using
directive, the console output function is called with its fully qualified name System.Console.WriteLine(). But if
you add the “using System;” directive, you can shorten the call to Console.WriteLine().
• The top-level container of a C# WinForm GUI is class Form. It plays the role of a JFrame in a Java Swing app.
• Inheritance is indicated by C++ style syntax: “HelloWorld : Form” The super class is called the base class, and
a sub class is called a derived class.
• Properties of the Form (Text, BackColor, etc.) are set in the HelloWorld constructor by direct assignment, but
set/get encapsulation is provided implicitly (behind the scenes) with set/get blocks, described below.
• The inherited version of the OnPaint() method from class Form is overridden in the derived class HelloWorld to
draw a String (the “override” keyword is normally required in this context).
• The driver instantiates a HelloWorld form object and passes it to the Application.Run() static method.
Application.Run() is responsible for the event handling thread.
COMP 585 Noteset #8 5
Some Differences Between C# and Java
MSDN has an article titled “C# for Java Developers.” Google it by title, or try this link:
https://msdn.microsoft.com/en-us/library/ms228358(v=vs.90).aspx
C# has been influenced by C, C++, Java, and other languages. Syntactically, all of these languages appear superficially
similar. But for Java programmers especially, C# has features that don’t have a direct analog in Java. It also uses certain
keywords that might look familiar but mean something different in C#. In this section, I’ll mention some of the most
important features that will be of interest to Java programmers, but the list is not exhaustive.
Quick observations
• Entry point is public static void Main(String[] args) { } not public static void main(String[] args) { }
• Filenames and class names less strictly coordinated
• Compilation produces a linked executable, not a series of individual .class files.
• “using” directive for namespaces without wildcard character, not “import” with packages
Topics covered in more depth
• Arrays (rectangular vs. jagged)
• Options for parameter passing to methods: out, ref
• Distinction between “class” and “struct” (reference vs. value semantics)
• Fields and Properties (implicit set/get blocks with no explicit set/get methods)
• Inheritance and Polymorphism (virtual, override, and new keywords)
• Delegates (function pointers encapsulated as objects)
• Anonymous methods and Lambda Expressions
• Nested Classes
• Static Classes
• Partial Classes
C# Arrays
C# supports both rectangular arrays and jagged arrays (arrays-of-arrays).
Rectangular arrays: 1D: int[] data = new int[5]; 2D: int[,] values = new int[5,3]; 3D: string[,,] names = new string[10,10,10];
Jagged arrays: 2D: int[][] data = new int[5][]; for (int i=0; i<5; i++) data[i] = new int[3];
Functions and Properties
For Rectangular Arrays data.GetLength(0): number of elements in 0th dimension data.GetLength(1): number of elements in 1st dimension … For Jagged Arrays data.Length: number of elements in 0th dimension data[0].Length: number of elements in 0th row data[1].Length: number of elements in 1st row …
Note: int[,] and int[][] are not the same type. If all you want is a simple rectangular array, use the [x,y] notation, not the
[x][y] notation.
COMP 585 Noteset #8 6
Example Program for Arrays
using System; public class A { public static void a(int[,] x) { for (int i=0; i<x.GetLength(0); i++) { for (int j=0; j<x.GetLength(1); j++) { Console.Write(x[i,j] + " "); } Console.WriteLine(); } } public static void a(int[][] x) { for (int i=0; i<x.Length; i++) { for (int j=0; j<x[i].Length; j++) { Console.Write(x[i][j] + " "); } Console.WriteLine(); } } public static void Main() { int[,] data1 = new int[3,5]; for (int i=0; i<data1.GetLength(0); i++) { for (int j=0; j<data1.GetLength(1); j++) { data1[i,j] = i + j; } } a(data1); int[][] data2 = new int[3][]; for (int i=0; i<data2.Length; i++) data2[i] = new int[5]; a(data2); } }
COMP 585 Noteset #8 7
Static Classes
The meaning of the “static” keyword is problematic in C/C++/Java/C#. It has been overloaded and overused when a new
and more precise keyword would have been better. Furthermore, its meaning is similar but not identical across languages.
In the context of Java classes, here is what it means:
• A top-level class is always static, but counterintuitively, it cannot be explicitly declared as static. One way to
look at the issue is that a static class has no implied reference to an enclosing object, and by definition, a top-
level class isn’t enclosed by another class.
• Nested classes can be static or non-static. A static nested class is similar to a top-level class except that it has
increased visibility into the properties of its enclosing class. A non-static nested class is also called an inner
class, and has an implied reference to an object of the enclosing type.
In the context of C# classes, static means something different:
• A top-level class can be either static or non-static. But the keyword static in the context of a class in C# means
something very different than it does in Java.
• A C# static class can have only static properties and cannot be instantiated.
• Therefore, a top-level non-static class in C# is analogous to a top-level static class in Java. But in Java, top-level
static classes never include the static keyword, it is implicit.
• Nested classes in C# can also be static or non-static. But neither implements the concept of an inner class in
Java, C# doesn’t support the Java concept of inner classes. A C# nested class never gets an automatically
generated reference to an instance of the enclosing class.
See https://msdn.microsoft.com/en-us/library/ms173120.aspx
COMP 585 Noteset #8 8
Here is a C# example of a nested class. The nested class is not declared static, but it has behavior that is similar to a static
nested class in Java.
public class Outer { private int v; private int x; public class Inner { private int p; private int q; public Inner() { p = 0; q = 0; } } public Outer() { v = 0; x = 0; } } class Tester { public static void Main(string[] args) { Outer outer = new Outer(); Outer.Inner inner = new Outer.Inner(); note the “static” style } }
C# Does Not Support Java Inner Classes
In the Main() method, note how the instance of the inner class was instantiated. The instantiation does not use a reference
to an instance of the outer class object. Rather, it uses a static-style instantiation, referencing only the name of the outer
class, not an instance of the outer class.
To experiment further, note that in the Inner() constructor in C#, the code would not be able to make an unqualified
reference to an instance variable of the outer class, for example:
public Inner() { p = v; // illegal in C#, no outer instance q = x; }
This might come as a surprise because the similar operation is permitted in Java because of the implied reference to an
instance of the enclosing class.
public Inner() { p = v; // okay in Java, same as “p = Outer.this.v;” q = x; }
The point of this example is that Java-style inner classes are not supported in C#.
COMP 585 Noteset #8 9
Here is the similar definition in Java:
public class Outer { private int v; private int x; public class Inner { private int p; private int q; public Inner() { p = 0; q = 0; } } public Outer() { v = 0; x = 0; } } class Tester { public static void main(String[] args) { Outer outer = new Outer(); Outer.Inner inner = outer.new Inner(); note the non-static style } }
Note the construction in the main method that would be required to independently instantiate an object of the inner class.
It must be instantiated explicitly with a reference to an instance of the outer class. Java also supports the static style
instantiation, which you would use if class Inner were declared to be a static nested class rather than an inner class.
Here’s another Java example, with example of allowed/disallowed operations:
public class MainClass { private int x; private int y; private static int z; public static class StaticSubClass { private int p; private static int q; public void f() { MainClass m = new MainClass(); //p = x; // not okay p = m.x; // okay p = z; //q = x; // not okay q = m.x; // okay q = z; } } public class SubClass { private int a; //private static int b; // not okay } } class Driver { public static void main(String[] args) { MainClass m = new MainClass(); MainClass.StaticSubClass c1 = new MainClass.StaticSubClass(); MainClass.SubClass c2 = m.new SubClass(); } }
COMP 585 Noteset #8 10
Values and References
Data is stored in memory, either a primitive variable or an object. Variables and objects have both a content and a
location. Value refers to the content of some variable or object, and reference refers to the location of some variable or
object.
In Java, primitive variables are treated strictly with value semantics only. The programmer cannot obtain a reference
to a primitive variable because there is no programming mechanism to refer to where the primitive variable is located. If
a primitive variable is declared inside a method (a local variable), the variable is created on the stack, and the variable is
deleted when the method that contains it returns. Such memory handling is sometimes called static memory allocation.
In Java, object references are handled with strict reference semantics only. Objects cannot be declared or created
directly. Only a reference variable can be declared. Then an object is created using the “new” keyword. The memory
comes from the heap, and the “new” operator returns its location. This location is assigned to the reference variable.
The memory for the object is retained as long as at least one reference to the object is reachable. Objects cannot be
allocated on the stack, although reference variables that point to them may be on the stack.
In C and C++, the programmer can use pointers and reference parameters in methods to treat both primitives and objects
as either references or pointers. This gives rise to quite a bit of additional syntax in C++, in particular. Here’s an
example from the C programming language which provides an address operator & to obtain the address or pointer to any
variable and an indirection operator * to reach a value indirectly through a pointer variable.
int x; // x is of type “int” int *p; // p is of type “pointer to int” x = 5; // assign 5 to x p = &x; // assign address of x to p int y = *p; // assign the indirection of p (in this case 5) to y
C# classes are like Java classes: they define objects which live on the heap and which undergo garbage colllection.
Reference semantics apply.
C# structs don’t have a counterpart in Java: they define objects which live on the stack and which, like local or block
variables, are automatically created and destroyed on function call and function return. Also, structs cannot be inherited,
there are no base structs and derived structs. Structs are reserved for small simple structured types. Value semantics
apply.
Significantly, C# structs are also different from C++ structs. In C++, struct is almost identical to class.
C# has adopted a uniform syntax for instantiating struct objects and class objects, so that it can be hard to impossible to
tell from code usage alone whether a data type was defined using a class or a struct. To be sure whether an object is
defined by a class or a struct, you should check the API or other documentation for the formal definition.
Look at the following program and note its output:
COMP 585 Noteset #8 11
using System; class Record { public int x; public int y; public void Print() { Console.WriteLine("x={0}, y={1}",x,y); } } struct Pt { public int x; public int y; public void Print() { Console.WriteLine("x={0}, y={1}",x,y); } } class StructTest { public static void Main() { Console.WriteLine("Hello, World!"); Record r1 = new Record(); r1.x = 3; r1.y = 4; r1.Print(); Pt p1 = new Pt(); p1.x = 5; p1.y = 6; p1.Print(); Record r2 = new Record(); r2.x = 8; r2.y = 9; r2.Print(); Pt p2 = new Pt();
p2.x = 12; p2.y = 13; p2.Print(); r2 = r1; r2.Print(); // #1 r2.x = 18; r2.y = 19; r1.Print(); p2 = p1; p2.Print(); // #2 p2.x = 23; p2.y = 24; p1.Print(); } }
Output is Hello, World! x=3, y=4 x=5, y=6 x=8, y=9 x=12, y=13 x=3, y=4 x=18, y=19 x=5, y=6 x=5, y=6
The explanation is that the assignment on line #1 performs a reference copy, while the assignment on line #2 performs a
memberwise copy between objects. This is a direct consequence of the how the objects in question were declared, either
as structs or classes.
COMP 585 Noteset #8 12
Classes and Structs in the .NET Class Libraries
In Java, primitives are represented by datatypes that are not part of any class hierarchy. In cases where strict objects are
required rather than a primitive, Java provides a corresponding “wrapper” class for each primitive:
Primitive Wrapper
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
… …
In C#, the primitive or built-in types are each represented by an underlying .NET type. The C# types are aliases for the
underlying types. Here’s a table of the correspondences from https://msdn.microsoft.com/en-us/library/ya5y69ds.aspx
C# Type .NET Framework Type
bool System.Boolean
byte System.Byte
sbyte System.SByte
char System.Char
decimal System.Decimal
double System.Double
float System.Single
int System.Int32
uint System.UInt32
long System.Int64
ulong System.UInt64
object System.Object
short System.Int16
ushort System.UInt16
string System.String
The aliases and the underlying .NET types are fully interchangeable in a C# program. The following two lines are
equivalent:
int x = 123; System.Int32 x = 123;
COMP 585 Noteset #8 13
The .NET types are all represented by a struct or class definition in the FCL. In this sense, C# doesn’t have primitive
types in the sense that Java uses the term primitive. For example, even a variable of type “int” can have methods applied
to it. This can lead to some C# code that looks unusual to a Java programmer:
int x = 12345; string s = x.ToString(); Console.WriteLine(s);
The explanation is that “int” is just an alias for “System.Int32” which is defined as a struct.
Passing Parameters to Methods
• The keyword “out” causes a parameter to be passed by reference.
• The keyword “ref” is similar, but the parameter must be initialized before being passed.
See the MSDN article for more info: http://msdn.microsoft.com/en-us/library/0f66670z.aspx
COMP 585 Noteset #8 14
Class Properties
In Java, standard OOD/OOP style recommends defining properties as private instance variables, with public accessor
and mutator methods for external access to the property value as needed.
class Time { private int hour; // instance var or property … public int getHour() { // accessor return hour; } public void setHour(int h) { // mutator hour = h; } } class Driver { public static void main(String[] args) { Time t = new Time(); t.setHour(3); // use mutator int x = t.getHour(); // use accessor } }
In C#, the instance variable is declared as in Java. A property is then separately defined. The property definition
controls access to the underlying instance variable through get and set operations. But from outside the class, the get and
set operations are invoked implicitly just by naming the property. No explicit reference to set and get is made.
class Time { private int hour; // instance var … public int Hour { // property get { return hour; // “return” is a keyword } set { hour = value; // “value” is a keyword } } } class Tester { public static void Main() { Time t = new Time(); t.Hour = 3; // uses set property int x = t.Hour; // uses get property } }
COMP 585 Noteset #8 15
Inheritance
Java uses the terms “superclass” and “subclass” to describe the inheritance relationship. It uses two keywords: one class
“extends” another class, and “implements” an interface. A class may extend only one class (single inheritance), but may
implement an arbitrary number of interfaces.
C# uses the terms “base class” and “derived class”. Like Java, C# enforces single inheritance of a class, arbitrary
inheritance of interfaces. But C# uses C++ style inheritance syntax and does not use the “extends” and “implements”
keywords. This makes it harder to tell if a base class is a class or an interface, but the C# convention is to choose
interface names that begin with capital “I”.
class A : B { … }
means class A (the derived class) inherits from class B (the base class).
C# uses C++ syntax to handle issues such as the derived class constructor calling the base class constructor. For
example (O’Reilly C# text):
class Window { // base class public Window(int top, int left) { … } // constructor } class ListBox : Window { // derived class public ListBox(int top, int left, String contents) : base(top,left) { … } }
The derived class calls the base class constructor (named base) using the “member initializer list” syntax, which is a
special expression starting with a colon “:” inserted between the argument list and the body of the method, ie, between
the “)” and the “{”. This syntax is borrowed directly from C++.
COMP 585 Noteset #8 16
Polymorphism
Polymorphism is a feature of most OOP languages that ensures that the execution environment determines what type of
object a reference holds dynamically at runtime. The alternative would be to force the compiler to assume the type
always matches the statically declared type of the object. Here’s a Java example.
class Window { public void print() { System.out.println("Window"); } } class ScrollableWindow extends Window { public void print() { System.out.println("Scrollable Window"); } } class ResizableScrollableWindow extends ScrollableWindow { public void print() { System.out.println("Scrollable Resizable Window"); } } public class Poly { public static void main(String[] args) { Window[] examples = { new Window(), new ScrollableWindow(), new ResizableScrollableWindow() }; for (Window w:examples) w.print(); } }
Polymorphism is demonstrated by the usage of the reference variable “w” in the “main” method. If the compiler were
forced to make typing decisions at compile time, since the declared type of “w” is “Window”, then the “print()” method
can only refer to the “print()” method of class “Window”. If there were no polymorphism, the output would be Window Window Window
But polymorphism means that the type decision is made at runtime. Reference “w” can legally refer to objects of any
subclass of “Window”. The type of object being referenced by w is determined by the interpreter at runtime, then the
“print()” method for that type is called. The output is actually Window Scrollable Window Resizable Scrollable Window
This second output demonstrates that Java supports polymorphism. The difference in behavior is sometimes referred to
as static binding vs. dynamic binding. Static binding means that decisions about what type of an object is referenced are
made by the compiler, while dynamic binding means that the decisions are made by the runtime environment. Runtime
decisions clearly give the program the ability to behave more flexibly, at the cost of perhaps slightly worse performance
because of the increase in runtime checking.
Some authors say that polymorphism is the feature or concept, while dynamic binding is a detail implementation that
supports the concept.
COMP 585 Noteset #8 17
The equivalent example in C# is
using System; class Window { public virtual void Print() { Console.WriteLine("Window"); } } class ScrollableWindow : Window { public override void Print() { Console.WriteLine("Scrollable Window"); } } class ResizableScrollableWindow : ScrollableWindow { public override void Print() { Console.WriteLine("Scrollable Resizable Window"); } } public class Poly { public static void Main() { Window[] examples = { new Window(), new ScrollableWindow(), new ResizableScrollableWindow() }; for (int i=0; i<examples.Length; i++) { Window w = examples[i]; w.Print(); } } }
Virtual Methods and Overrides
The common meaning of a virtual method is that the class containing the virtual method can be subclassed, and the
subclass can override or redefine the virtual method.
In Java, all methods are virtual. To prevent a method from being overridden, it can be declared final.
In C#, methods must be explicitly declared as virtual, they are not virtual by default. As in C++, the usual practice is to
declare a method as virtual in the base class when it is first introduced. After this point, in the derived classes that
override it, the virtual keyword is not repeated. Instead, the override (or new) keyword is used.
COMP 585 Noteset #8 18
Overriding Inherited Methods in C#
• In C#, in the derived class, virtual methods being overridden carry the keyword “override”.
• In C#, in the derived class, a method that is intended to hide the inherited virtual method, not override it, uses
the keyword “new”. This is the default, but the compiler generates a warning that the “new” keyword is
implied.
Example: 4-level Class Hierarchy in Java
class Window { public void pr() { System.Console.WriteLine("Window"); } } class ScrollableWindow : Window { public void pr() { System.Console.WriteLine("Scrollable Window"); } } class ResizableScrollableWindow : ScrollableWindow { public void pr() { System.Console.WriteLine("R/S Window"); } } class Frame : ResizableScrollableWindow { public void pr() { System.Console.WriteLine("Frame"); } } public class PolyTest { public static void Main() { // polymorphic behavior disabled Window w = new Window(); w.pr(); ScrollableWindow sw = new ScrollableWindow(); sw.pr(); ResizableScrollableWindow rsw =
new ResizableScrollableWindow(); rsw.pr(); Frame f = new Frame(); f.pr(); // polymorphic behavior enabled (if present) Window w2; w2 = sw; w2.pr(); w2 = rsw; w2.pr(); w2 = f; w2.pr(); ScrollableWindow sw2; sw2 = sw; sw2.pr(); sw2 = rsw; sw2.pr(); sw2 = f; sw2.pr(); } }
COMP 585 Noteset #8 19
General Rules for C#
• Non-virtual functions are not marked in the base class. No dynamic binding possible (no polymorphism) in base
classes.
• For “typical” polymorphic behavior, mark first function in the chain as “virtual”, all others in the chain marked
“override”.
• To break the chain at some point, mark the function “new”, not “override”. The function can still be marked virtual
or not, depending on what behavior is desired for the remainder of the chain.
• If the base function is not marked “virtual”, the derived class cannot mark it as “override”.
• If the base function is marked “virtual”, the derived class should mark it as either “new” or “override”.
• The “override” tag implies “virtual”, and it can’t be repeated (can’t declare “virtual override”).
class Window { public virtual void pr() { System.Console.WriteLine("Window"); } } class ScrollableWindow : Window { public override void pr() { System.Console.WriteLine("Scroll Window"); } }
See another example at
http://www.akadia.com/services/dotnet_polymorphism.html.
COMP 585 Noteset #8 20
Function Pointers in C (Precursor to C# Delegates)
An interesting feature of C is its ability to store the entry point address of a function in a variable and invoke it later.
This is called a function pointer. The hardest part is to correctly declare the variable which is to hold the pointer. Here’s
an example from http://www.newty.de/fpt/intro.html#why. The 4 functions at the top are selected and invoked in two
programming styles: using a “switch” statement with a character selector, and using a function pointer.
float Plus (float a, float b) { return a+b; } float Minus (float a, float b) { return a-b; } float Multiply(float a, float b) { return a*b; } float Divide (float a, float b) { return a/b; } void Switch(float a, float b, char opCode) { float result; switch(opCode) { case '+' : result = Plus (a, b); break; case '-' : result = Minus (a, b); break; case '*' : result = Multiply (a, b); break; case '/' : result = Divide (a, b); break; } cout << result << endl; } void Switch_With_Pointer(float a, float b, float (*pt2Func)(float, float)) { float result = pt2Func(a, b); // call using function pointer cout << result << endl; } int main(void) { Switch(2, 5, '-'); // use switch Switch_With_Pointer(2, 5, &Minus); // use function pointer }
The function input parameter float (*pt2Func)(float, float)
defines “pt2Func” as a pointer to a function which takes two float arguments and returns a float result.
The value passed to the parameter is &Minus This is the same as the assignment pt2Func = &Minus; In some C compilers, the “&” address operator is optional.
The invocation of the “Minus” function indirectly through the pointer is float result = pt2Func(a, b);
COMP 585 Noteset #8 21
Delegates
A C# delegate is an abstraction that allows a method to be passed around as an object and invoked indirectly when
needed. It is similar to a function pointer in C. Java achieves a similar effect with listeners which allow the programmer
to define a method to be called at a later time in response to an event. There are three steps to using delegates. Some of
these steps have been streamlined in the latest versions of the .NET SDK.
Step 1: declare the delegate as a datatype using the delegate keyword.
Examples: delegate void myDelegate1(); delegate void myDelegate2(string);
In the first case, the symbol myDelegate1 is now a delegate type that represents any method with return type void and no
parameters, while MyDelegate2 represents any method with return type void and one argument of type string. For the
programming required to develop C#/.NET GUIs, you will not need to introduce any new delegate types yourself, you
will only need to use delegate types that have already been defined by .NET. It will still be useful for you to see a
complete example to understand how delegates work.
Step 2: Declare a delegate type variable, instantiate a delegate object of the declared type, and assign it to the variable.
delegate long binop(long,long); public long add(long a, long b) { return a + b; } binop op = new binop(add);
• Line 1 declares a delegate type named binop. The delegate name appears in the position of a function call. The
return type and argument list complete the declaration, indicating what kind of function the delegate can
represent.
• Line 2 is a method that happens to conforms to the binop signature.
• Line 3 instantiates an instance of the binop delegate and stores a reference to it in variable op. So op can now
be used as an indirect function reference back to the add() method.
Step 3: use the delegate to indirectly invoke the original method
long x = op(3,5);
So this is an indirect invocation of the add() method, accessed indirectly through the op() delegate instance. The real
utility of the delegate instance is that it can be passed around from method to method as a parameter, and invoked only
when needed. Here’s the definition of a method that accepts a delegate as a parameter:
void someMethod(int x, long y, binop someop) { … }
Here’s a program that shows how to use someMethod()
COMP 585 Noteset #8 22
using System; public class MathClass {
public static long Add (int i, int j) { return (i+j); } public static long Multiply (int i, int j) { return (i*j); }
} public class DelegateTest {
delegate long myDelegate (int i, int j); // define delegate
public static void Main(string[] args) {
Console.WriteLine("Call to Add method through delegate"); myDelegate operation = new myDelegate(MathClass.Add); // instantiate long l = operation(10, 20); // invoke Console.WriteLine("Sum of 10 and 20 is " + l);
Console.WriteLine("Call to Multiply method thru delegate"); operation = new myDelegate(MathClass.Multiply); // instantiate l = operation(1639, 1525); // invoke Console.WriteLine("1639 multiplied by 1525 equals " + l);
} } Or more compactly:
using System; public class MathClass {
public static long Add (int i, int j) { return (i+j); } public static long Multiply (int i, int j) { return (i*j); }
} public class DelegateTest {
public delegate long binop(int i, int j);
public static long eval(binop op, int x, int y) { return op(x,y); }
public static void Main(string[] args) { Console.WriteLine(eval(new binop(MathClass.Add),20,30)); Console.WriteLine(eval(new binop(MathClass.Multiply),1639,1525));
} }
In these examples, the delegate objects are initialized with static methods from class MathClass. Instance methods can
also be used to initialize a delegate object by first creating an instance of the object, then referencing the method in the
context of the object of the class.
COMP 585 Noteset #8 23
Shortcut for Instantiating Delegates
The full syntax for instantiating a delegate object from its definition is similar to instantiation of any object with the
“new” operator. This first statement is merely a definition of a delegate template named “MyDelegate”: delegate long myDelegate (int i, int j);
This next statement instantiates a delegate object from this template and stores a reference to it: myDelegate operation = new myDelegate(MathClass.Add);
Finally, the third statement invokes the “MathClass.Add” method indirectly through the delegate long l = operation(10, 20);
The second statement can now be abbreviated as follows: myDelegate operation = MathClass.Add;
Although this syntax makes the delegate look more like a simple function pointer (as in C), it is really just a shortcut for
the earlier full version. It is understood that a delegate object is still instantiated implicitly.
Here’s the previous example with the shortcut for delegate instantiation added:
using System; public class MathClass {
public static long Add (int i, int j) { return (i+j); } public static long Multiply (int i, int j) { return (i*j); }
} public class DelegateTest {
public delegate long binop(int i, int j);
public static long eval(binop op, int x, int y) { return op(x,y); }
public static void Main(string[] args) { Console.WriteLine(eval(MathClass.Add,20,30)); Console.WriteLine(eval(MathClass.Multiply,1639,1525));
} }
COMP 585 Noteset #8 24
Delegates and Events
Delegates are central to event handling in C#. In Java, an object fires events, and the response is provided by an event
listener attached to that object. In C#, instead of a listener, a delegate is registered with the source of the event. The
delegate is called when the event is triggered, so the response to the event is assigned to the delegate.
Before discussing how to define your own events and delegates (something that you will not need to do very often for
the projects in this course), let’s get a preview of how some existing events and delegates that will be used in GUI
programming work.
In WinForms, the top-level container class is called class Form. The form object accepts a wide range of events such as
mouse clicks. Here’s a section of the API on class System.Windows.Forms.Form:
Selected Events for class Form
Name Description
Activated Occurs when the form is activated in code or by the user.
AutoSizeChanged Occurs when the AutoSize property changes.
Click Occurs when the control is clicked.(Inherited from Control.)
ClientSizeChanged Occurs when the value of the ClientSize property changes. (Inherited from Control.)
Closed Occurs when the form is closed.
Closing Occurs when the form is closing.
ControlAdded Occurs when a new control is added to the Control.ControlCollection.(Inherited from Control.)
ControlRemoved Occurs when a control is removed from the Control.ControlCollection.(Inherited from Control.)
Deactivate Occurs when the form loses focus and is no longer the active form.
Disposed Occurs when the component is disposed by a call to the Disposemethod. (Inherited from Component.)
DoubleClick Occurs when the control is double-clicked.(Inherited from Control.)
DragDrop Occurs when a drag-and-drop operation is completed.(Inherited fromControl.)
DragEnter Occurs when an object is dragged into the control's bounds.(Inherited from Control.)
DragLeave Occurs when an object is dragged out of the control's bounds.(Inherited from Control.)
DragOver Occurs when an object is dragged over the control's bounds.(Inherited from Control.)
Enter Occurs when the control is entered.(Inherited from Control.)
FormClosed Occurs after the form is closed.
FormClosing Occurs before the form is closed.
GotFocus Occurs when the control receives focus.(Inherited from Control.)
COMP 585 Noteset #8 25
Invalidated Occurs when a control's display requires redrawing.(Inherited fromControl.)
KeyDown Occurs when a key is pressed while the control has focus.(Inherited fromControl.)
KeyPress Occurs when a character. space or backspace key is pressed while the control has focus.(Inherited
from Control.)
KeyUp Occurs when a key is released while the control has focus.(Inherited fromControl.)
Layout Occurs when a control should reposition its child controls.(Inherited from Control.)
Leave Occurs when the input focus leaves the control.(Inherited from Control.)
Load Occurs before a form is displayed for the first time.
LocationChanged Occurs when the Location property value has changed.(Inherited fromControl.)
LostFocus Occurs when the control loses focus.(Inherited from Control.)
MouseClick Occurs when the control is clicked by the mouse.(Inherited fromControl.)
MouseDoubleClick Occurs when the control is double clicked by the mouse.(Inherited fromControl.)
MouseDown Occurs when the mouse pointer is over the control and a mouse button is pressed.(Inherited from Control.)
MouseEnter Occurs when the mouse pointer enters the control.(Inherited fromControl.)
MouseHover Occurs when the mouse pointer rests on the control.(Inherited fromControl.)
MouseLeave Occurs when the mouse pointer leaves the control.(Inherited fromControl.)
MouseMove Occurs when the mouse pointer is moved over the control.(Inherited from Control.)
MouseUp Occurs when the mouse pointer is over the control and a mouse button is released.(Inherited
from Control.)
MouseWheel Occurs when the mouse wheel moves while the control has focus.(Inherited from Control.)
Move Occurs when the control is moved.(Inherited from Control.)
Paint Occurs when the control is redrawn.(Inherited from Control.)
Resize Occurs when the control is resized.(Inherited from Control.)
SizeChanged Occurs when the Size property value changes.(Inherited from Control.)
COMP 585 Noteset #8 26
As an example, you can see that objects of class Form generate MouseDown events (roughly equivalent to Java
mousePressed). The MouseDown event is associated with a delegate called MouseEventHandler, which is defined like
this:
public delegate void MouseEventHandler( object sender, MouseEventArgs e )
The declaration introduces a delegate type called MouseEventHandler. It represents a function with a return type of void
and two arguments, the first of type object and the second of type MouseEventArgs. Any concrete function with this
signature can be used as the delegate instance to respond to the event. For example:
using System; using System.Windows.Forms; class Tester { public static void f(object obj, MouseEventArgs args) { // 1 Console.WriteLine("mouse down"); } public static void Main() { Form f = new Form(); f.MouseDown += new MouseEventHandler(Tester.f); // 2 Application.Run(f); } }
At comment 1, we define a method named f with the appropriate signature. At comment 2, we instantiate a
MouseEventHandler instance from f and assign it as the handler for the MouseEvent event for Form f.
In order to use the .NET framework class libraries to write your code, you will not be defining new events and delegates,
only using existing event and delegate definitions from the libraries.
Here is one more example of how to write your own.
COMP 585 Noteset #8 27
Example from URL given above:
using System; public class EventClass { // Declare the Delegate Datatype public delegate void CustomEventHandler(object sender, EventArgs e); // Declare Event using Delegate Datatype + “event” keyword public event CustomEventHandler CustomEvent; public void InvokeEvent() { CustomEvent(this, EventArgs.Empty); } } class TestClass { static void Main(string[] args) { EventClass myEventClass = new EventClass(); // Associate the handler with the events myEventClass.CustomEvent += new EventClass.CustomEventHandler(CustomEvent1); myEventClass.CustomEvent += new EventClass.CustomEventHandler(CustomEvent2); myEventClass.InvokeEvent(); myEventClass.CustomEvent -= new EventClass.CustomEventHandler(CustomEvent2); myEventClass.InvokeEvent(); } private static void CustomEvent1(object sender, EventArgs e) { Console.WriteLine("Fire Event 1"); } private static void CustomEvent2(object sender, EventArgs e) { Console.WriteLine("Fire Event 2"); } }
Output is Fire Event 1 Fire Event 2 Fire Event 1
COMP 585 Noteset #8 28
In the definition of the EventClass class, objects of this type are given an event of type CustomEvent. As a programmer,
you need to be familiar with the features of each predefined class that you use, including its methods, properties, and
events.
When the “InvokeEvent” method of this class is called, it creates or “fires” an event of type CustomEvent.
The statements that register delegates with the event
myEventClass.CustomEvent += new EventClass.CustomEventHandler(CustomEvent1); myEventClass.CustomEvent += new EventClass.CustomEventHandler(CustomEvent2);
can be abbreviated using the shortcut discussed earlier:
myEventClass.CustomEvent += CustomEvent1; myEventClass.CustomEvent += CustomEvent2;
Delegates Used as Event Handlers
In Java, we saw a pairing between an event and a listener. A Java event is an object and a listener is any object that
provides the necessary methods.
In C#, the pairing is between events and event handlers or delegates. The delegate used in the example above has a
signature that is representative of many event handlers. Here’s the definition of a very common event called the Paint
event.
The delegate for the Paint event looks like this (in the System.Windows.Forms namespace):
public delegate void PaintEventHandler(object objSender,
PaintEventArgs pea);
Any method that has this signature can act as the event handler for the Paint event.
static void MyPaintHandler(object objSender, PaintEventArgs pea) { … }
The Form class contains a property or field named Paint. This is where you attach or register handlers that implement
responses to the Paint event for that Form:
Form form = new Form(); form.Paint += new PaintEventHandler(MyPaintHandler);
This can be abbreviated to: form.Paint += MyPaintHandler;
The “+=” operator is used in the same sense as the “addListener()” methods in Java.
COMP 585 Noteset #8 29
Anonymous Methods/Delegates and Lambda Expressions
Delegates are the mechanism for responding to events in both Windows Forms and WPF. For example, the Paint event
for a Form uses the PaintEventHandler delegate. The complete process for defining and assigning the delegate might
look like this:
public void DoPaint(object sender, PaintEventArgs args) { // paint/draw operations here } PaintEventHandler p = new PaintEventHandler(DoPaint); Form f = new Form(); f.Paint += p;
A delegate method can be created from an anonymous method using the “delegate” keyword:
f.Paint += delegate(object obj, PaintEventArgs args) { // paint/draw operations here };
Another kind of anonymous method can be defined using lambda expressions. Lambda expressions are a mathematical
notation for describing functions that predates C#. Here is a simple example
x => x*x
uses a lambda expression to describe a function of x, namely x2. It could be used in a program like this:
using System; public class LambdaDemo { delegate int F(int x); delegate double G(double x, double y); public static void Main(string[] args) { F f = x => x * x; Console.WriteLine(f(5)); G g = (x,y) => Math.Sqrt(x*x + y*y); Console.WriteLine(g(3.0,4.0)); } }
The previous anonymous method could be defined as follows using a lambda expression:
f.Paint += (object obj, PaintEventArgs args) => { // paint/draw operations here };
COMP 585 Noteset #8 30
Parameter types can be frequently omitted when they can be determined through type inference:
f.Paint += (obj, args) => { // paint/draw operations here };
And a common convention is to use the parameter name “_” for a single parameter that is not used in the body of the
lambda:
f.Paint += (_, args) => { // paint/draw operations here };
Here’s the equivalent in C#. Compared to Java, the main differences are these:
• C# supports static nested classes, but not inner classes (non-static nested classes).
• However, the keyword “static” is not used with the nested class, it must be implicit.
Declaring a C# class to be static means something entirely different, discussed next.
C# Static Classes Similar to Java Interfaces with Data Members
Adding the keyword “static” to a class in C# simply means that the class consists solely of static data and cannot be
instantiated.
Partial Classes
C# GUI building in WPF can be optionally divided into 2 parts: markup for layout and code for event handling. The
two types of notation must be defined in separate files, followed by various preprocessing and linking steps before the
complete executable application is built. Because of this initial separation of partial definitions, the keyword “partial” is
required to tag the class definition in a code file. The markup file defines a “x:Class” attribute for the top-level markup
tag. Its value then corresponds to a partial class defined in a corresponding code file. This feature of WPF will be
discussed in detail later.
COMP 585 Noteset #8 31
Intro to Programming Windows Forms
The focus of GUI development for the Microsoft .NET platform is gradually shifting away from Windows Forms and
toward the Windows Presentation Foundation (WPF). Nevertheless, Windows Forms is not dead, and is worth a brief
look because at a minimum it serves as a “gentle” intro to WPF. Windows Forms is discussed briefly here, but the main
focus of GUI building for .NET is WPF, which will be covered later in more depth.
Namespaces
A namespace is a logical collection of predefined symbols. The concept was introduced by C++. They are somewhat
analogous to packages in Java. The contents of a namespace are made available to a source file with the “using”
directive instead of the “import” command. Typical Form-based GUIs require the following namespaces:
using System; using System.Drawing; using System.Windows.Forms;
These statements will appear at the top of most source files. Others will be introduced later (e.g.,
System.ComponentModel).
The .NET Framework Class Library
Access to the .NET FCL online documentation was described above. Here is a list of relevant namespaces for programs
in this course. Even this list is a little long, we will probably only use a subset of these.
• System
• System.Collections
• System.Collections.Generic
• System.Data
• System.Drawing
• System.IO
• System.Net
• System.Security
• System.Text
• System.Threading
• System.Web
• System.Windows.Forms
• System.Xml
Within each namespace is a list of classes. The title of the namespace should be a general guide to the kinds of classes it
contains. The documentation for each class includes sections for the following:
• Constructors
• Properties
• Methods
• Events
Because of the heavy use of inheritance within the framework classes, it may take some study to determine answers to
questions such as “from which base class was the BackColor property of class Form first inherited?” The expandable
tree view menu on the left is generally restricted to features defined in the current class. The more complete page
displayed on the right includes features present via inheritance.
COMP 585 Noteset #8 32
Class Control
The Control is analogous to the Component/JComponent classes in Java. It is the base class for most objects used in the
creation of a GUI. It introduces many properties and events that are inherited throughout the classes of the GUI-related
namespaces.
Class Form
The Form is similar to a JFrame/JPanel class in Java. It acts as the top-level container for a .NET-based GUI. An
application can either
• create a generic Form instance and customize it by changing its properties on the fly, or
• extend class Form, customize the class by setting properties in a constructor and/or overriding methods, then
create the customized object by calling the class constructor.
The first is a “quick-and-dirty” way to create a “one-shot” object. The second is better if a template for multiple objects
is needed. In addition, some methods of class Form are “protected” and can be customized only by subclassing and
overriding.
The Application.Run() Method
This method defines an entry point to a GUI-based application. The argument to the method is the main Form for the
application.
C# Properties VS Java Instance Variables Java Example C# Example JFrame f = new JFrame(); Form f = new Form();
f.setSize(300,200); f.ClientSize = new Size(300,200);
Objects in Java are instantiated, then customized by changing the values of instance variables via the appropriate set or
get methods. Objects in C# are instantiated, then customized by direct assignment to the appropriate property (which
implicitly invokes the “set” block for the property).
COMP 585 Noteset #8 33
Java Events
In Java, both events and event listeners are organized around composites of low-level “atomic” events. For example, the
MouseEvent and MouseListener are composites that represents 5 atomic events:
• mousePressed
• mouseReleased
• mouseClicked
• mouseEntered
• mouseExited
Each atomic event and listener is handled by a single method within this composite. In contrast, Windows Forms defines
only the atomic events. Each atomic event is handled by a delegate with the appropriate signature (return type void, 2
parameters, one of type object, one of type EventArgs or one of its derived classes).
Interface Methods Adapter Event
ActionListener actionPerformed n/a ActionEvent
MouseListener mousePressed MouseAdapter MouseEvent
mouseReleased
mouseClicked
mouseEntered
mouseExited
MouseMotionListener mouseMoved MouseAdapter MouseEvent
mouseDragged
WindowListener windowOpened WindowAdapter WindowEvent
windowClosing
windowClosed
windowIconified
windowDeiconified
windowActivated
windowDeactivated
In C#/WinForms, low-level events are handled individually by delegates. There is no grouping of related events into a
higher-level handler like a Java listener. To add event handling to a C# program:
• Determine the type of the object being customized (for example, Form or Button).
• Look up the events that the object supports (for example, MouseUp or Paint).
• Determine the delegate for that event type (for example, MouseEventHandler, PaintEventHandler).
• Write a handler for that event that matches the signature of the delegate.
• Register the handler for that event with the object (for example, form.Paint += myPaintMethod; )
Here are some of the most common events and handlers. For each handler type, there is a corresponding event argument
type. The number of possible combinations is much less than it appears, because most events use a generic
EventHandler delegate. Only a few events have custom handlers.
COMP 585 Noteset #8 34
Event Handler Event Argument
MouseUp MouseEventHandler MouseEventArgs
MouseDown
MouseMove
MouseWheel
MouseClick
MouseDoubleClick
MouseEnter EventHandler EventArgs
MouseLeave
MouseHover
MouseCaptureChanged
Paint PaintEventHandler PaintEventArgs
KeyUp KeyEventHandler KeyEventArgs
KeyDown
KeyPress
Click EventHandler EventArgs
DoubleClick
ClientSizeChanged
SizeChanged
Resize
ControlAdded
ControlRemoved
LostFocus
GotFocus
VisibleChanged
Disposed
Note the difference between the MouseClick and the Click events, and the MouseDoubleClick and DoubleClick events.
A Click event is a “high level” event and a MouseClick is a “low level” event. For example, clicking the mouse on a
control generates the following sequence:
MouseDown � Click � MouseClick � MouseUp
Double clicking generates:
MouseDown � Click � MouseClick � MouseUp � MouseDown � DoubleClick � MouseDoubleClick
� MouseUp
Click and Double Click events are “high level” because other user actions such as pressing the <ENTER> key can
generate a “Click” event. This is counter-intuitive since the mouse is not involved.
Java provides many window-specific events, while C# provides only generic control events. But a Form is a Control, so
you can infer how to respond to changes to window state (for example, Java windowActivated can be approximated by
C# GotFocus).
Also note the absence of a “MouseDrag” event in C#/WinForms. The program must keep track of MouseUp and
MouseDown events, and keep track of the state of the mouse buttons to respond to this situation.
Event Handling A specific class defines a subset of the possible event types. The programmer has two choices for implementing a
response to an event for that class:
• Register a delegate for the event property of an object of that class.
• Subclass the class and override the “On” method for that event.
For example, the Form class defines the “Paint” event as a class property, and it defines a protected method named
OnPaint which is the built-in handler for the Paint event.
To create a form and customize its response to the Paint event, you have several choices:
COMP 585 Noteset #8 35
Approach #1: Define a Delegate for the Paint Event for a Generic Form void myHandler(object objSender, PaintEventArgs pea) { … }
Form form = new Form(); form.Paint += new PaintEventHandler(myHandler);
Approach #2: Subclass Form and Override OnPaint Method class MyForm : Form { protected override void OnPaint(PaintEventArgs pea) { … } } … MyForm form = new MyForm();
Approach #3: Do Both
When doing both, it is important for the “On” method to call its inherited counterpart: class MyForm : Form { protected override void OnPaint(PaintEventArgs pea) { base.OnPaint(pea); // <= call inherited method … } } The inherited method is responsible for calling any externally registered delegates. They will not get executed if the
override method omits the call to the original inherited method.
A generic response to an event then looks something like this:
class SomeControl
supports SomeEvent
with handler SomeHandler
and event args SomeEventArgs
Approach #1 SomeControl ctrl = new SomeControl(); … void MyHandler(Object obj, SomeEventArgs args) { … } … ctrl.SomeEvent += MyHandler;
Approach #2 class MyControl : SomeControl { protected override void OnSomeEvent(SomeEventArgs args) { base.OnSomeEvent(args); … } } … MyControl ctrl = new MyControl();
COMP 585 Noteset #8 36
Here are two implementations of a simple GUI whose main form tracks the MouseMove event and outputs mouse
coordinates to the console for each detected event.
Approach #1: Generic Form with Event Handling Delegate using System; using System.Windows.Forms; using System.Drawing; public class ClickTest2 { public static void RespondToMouseMove(object obj, MouseEventArgs a) { System.Console.WriteLine(a.X + " " + a.Y); } public static void Main() { Form form = new Form(); form.MouseMove += RespondToMouseMove; Application.Run(form); } } Approach #2: Derived Class with Method Overrides using System; using System.Windows.Forms; using System.Drawing; public class ClickTest : Form { protected override void OnMouseMove(MouseEventArgs a) { System.Console.WriteLine(a.X + " " + a.Y); } } public class Driver { public static void Main() { ClickTest form = new ClickTest(); Application.Run(form); } }
COMP 585 Noteset #8 37
Example
Here’s a C# version of a greatly simplified version of the Java paint application exercise.
using System; using System.Windows.Forms; using System.Drawing; using System.Collections; // for class ArrayList public class Shape { private int xul,yul,width,height; public Shape(int x1, int y1, int x2, int y2) { xul = (x1<x2)?x1:x2; yul = (y1<y2)?y1:y2; width = (x1<x2)?x2-x1:x1-x2; height = (y1<y2)?y2-y1:y1-y2; } public int Xul { set { xul = value; } get { return xul; } } public int Yul { set { yul = value; } get { return yul; } } public int Width { set { width = value; } get { return width; } } public int Height { set { height = value; } get { return height; } } public void draw(Graphics g) { g.DrawRectangle(Pens.Black,xul,yul,width,height); } } public class PaintForm : Form { private ArrayList shapes; private Shape currentShape; private int startx,starty; private bool shapeInProcess; public PaintForm() { shapes = new ArrayList(); currentShape = null; startx = starty = 0; shapeInProcess = false; } public void startShape(int x, int y) { startx = x; starty = y; shapeInProcess = true; } public void stopShape(int x, int y) { currentShape = new Shape(startx,starty,x,y); }
COMP 585 Noteset #8 38
protected override void OnMouseDown(MouseEventArgs evt) { startShape(evt.X,evt.Y); } protected override void OnMouseUp(MouseEventArgs evt) { stopShape(evt.X,evt.Y); shapes.Add(currentShape); currentShape = null; shapeInProcess = false; Invalidate(); } protected override void OnMouseMove(MouseEventArgs evt) { if (shapeInProcess) { stopShape(evt.X,evt.Y); Invalidate(); } } protected override void OnPaint(PaintEventArgs evt) { Graphics g = evt.Graphics; if (currentShape!=null) currentShape.draw(g); for (int i=0; i<shapes.Count; i++) ((Shape)shapes[i]).draw(g); } } public class Driver { public static void Main() { PaintForm form = new PaintForm(); Application.Run(form); } }
COMP 585 Noteset #8 39
Summary: Events and Delegates
A Windows Forms component defines events that it receives. Events are atomic, not aggregated into related groups as in
Java.
Mouse Events
• MouseUp, MouseDown, MouseClick, MouseDoubleClick, MouseMove, MouseEnter, MouseLeave,
MouseHover, MouseWheel
Window Events
• Activated, Closed, Closing, Deactivated, Disposed,
Others
• Click, DoubleClick, Paint, Load, Resize, SizeChanged
Each event has a corresponding delegate and corresponding “On” method
Example: Paint
• Delegate: public delegate void PaintEventHandler(object objSender, PaintEventArgs args);
• On Method protected void OnPaint(PaintEventArgs args) { … }
To customize the component’s response to the event, either define a delegate and register it with the object’s Paint event,
or override the “OnPaint” method in a subclass of the component.
Similar to anonymous class objects in Java, C# delegates may be instantiated with anonymous methods.
One Subtlety When Overriding …
There is a small asymmetry regarding these two ways of adding event responses if the classes also involve inheritance.
The “On” methods are responsible for calling the registered delegates. So when you create a subclass and override an
“On” method, the “On” method should call the inherited version. Otherwise the override turns off the ability to keep any
delegates “in the loop”. Remember that in C#, the superclass is called the “base” class, so the superclass constructor is
called “base()” not “super()”.
class MyForm : Form { … protected override void OnPaint(PaintEventArgs args) { base.OnPaint(args); … } } class AnotherForm : MyForm { } … AnotherForm f = new AnotherForm(); f.Paint += someDelegate; // won’t work unless MyForm method
// calls base.OnPaint() …
Connecting Controls To Forms
When attaching a component to a container in Java, the “child” is added to the “parent”: parent.add(child) // Java Swing
When attaching a control to a form in C# and Windows Forms, there are two approaches. The first approach is similar
to Java, in which the child is attached to the parent. In C#/Windows Forms, the child must be explicitly added to the
parent’s array of controls: parent.Controls.Add(child); // C# and Windows Forms
This is an interesting statement because it reflects more accurately what happens “behind the scenes” in Java with the
“parent.add(child)” statement. The “add” statement is actually inserting the child component into the parent’s array of
child controls in this case too.
COMP 585 Noteset #8 40
The second approach in C#/Windows Forms is to set the “parent” property of the child component:
class MyForm : Form { public MyForm() { Control c = new Control(); c.Parent = this; // attaches “c” to “this” … } … }
Telling the control who is its parent is the same as adding the control to the parent. This second style is more common in
the Petzold code examples.
Absolute Positioning
The default style in Windows Forms programming for arranging controls on a form is absolute positioning. This style is
inherited from older versions of the Visual Basic GUI builder. Once a control has been positioned on a form, it is
assumed that it will remain in that position. Button btn = new Button(); btn.Parent = this; btn.Text = "Click me!"; btn.Location = new Point(50, 25); // set location btn.AutoSize = true; // set size btn.Click += ButtonOnClick;
Complete Example (2nd Petzold Text): Simple Form + Button //----------------------------------------------- // FormWithButton.cs (c) 2005 by Charles Petzold //----------------------------------------------- using System; using System.Drawing; using System.Windows.Forms; class FormWithButton: Form { [STAThread] // compiler directive public static void Main() { Application.EnableVisualStyles(); Application.Run(new FormWithButton()); } public FormWithButton() { Text = "Form with Button"; Button btn = new Button(); btn.Parent = this; btn.Text = "Click me!"; btn.Location = new Point(50, 25); btn.AutoSize = true; btn.Click += ButtonOnClick; } void ButtonOnClick(object objSrc, EventArgs args) { MessageBox.Show("The button was clicked!", "Button"); } }