Home | Lehre | Videos | Texte | Vorträge | Software | Person | Impressum, Datenschutzerklärung | Blog RSS

Event-driven Programming

Basic approaches to handling user input:

Event-driven programming has become the standard in applications frameworks. In object-oriented languages it is implemented to some part through virtual functions and to another part through special functionality: Event listeners (Java), events and delegates (.NET), signals and slots (Qt). The idea of these special techniques is to allow calling one or several functions of some objects whenever a certain event occurs.

Why event-driven programming has become the standard for typical applications:

Demo: Visually designing a graphical user interface with Windows Forms in Microsoft Visual C# Express 2005 (2005!). Designer-generated code. Events. Delegates.

Demo: Designing a form with the Windows Presentation Framework (WPF) in Microsoft Visual C# Express 2008 (2008!). Designer-generated XML-based layout description (XAML). Event handling. Adding Name attributes to the XAML description to refer to the elements.

Sometimes, however, event-driven programming can be a pain in the neck. Every routine that is invoked by some event has to return very quickly; otherwise, your program seems to hang. In particular, the routines invoked must not contain long or even infinite loops. For instance: If you want to capture strokes done with the mouse, you must store one mouse (x,y) position at a time, always returning immediately. In pseudocode this looks as follows:

OnMouseDown
Create new stroke in memory
Store first position in stroke

OnMouseMove
If mouse button pressed append position to stroke

(Demo in C#)

If you want something to happen at regular intervals, create a timer and handle the events that it fires. If you want to do something lengthy, use a separate thread.

Immediate-Mode Painting

Virtually all current event-based systems trigger the repainting of the screen via a pseudo-event “Screen has to be repainted”. The idea is to collect all drawing code at one place. (Avoid to spread functionality across your software!) If the runtime environment finds that it needs to redraw your graphical component, it will invoke this function to immediately (hence the name “immediate mode”) draw or redraw the appropriate portion of the screen. The system will not remember what this function has drawn, but rather call this function again, should a screen redraw be necessary.

If you want to draw yourself, you will typically not invoke this function directly, but rather tell the runtime environment that your graphical component needs an update. Thus, the environment can organize the redrawing efficiently, in particular by collapsing several requests into one.

Java 2D (Graphics API for Java Swing)

GDI+ with Windows Forms (APIs for .NET)

WPF (API for .NET)

Explanation

repaint()

Invalidate()

InvalidateVisual();}

Note: called on the element to be updated

member function of the widget to call for a display update

protected void paintComponent(Graphics g)
{
super.paintComponent(g);
//...
}

override void protected OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
//...
}

protected override void OnRender(DrawingContext drawingContext)

{

base.OnRender(drawingContext);

//...

}

Note: Inherit from FrameworkElement. Add xmlns:myapp="clr-namespace:NAMESPACE_OF_YOUR_CLASS" to the window in XAML. Add <myapp:YOUR_CLASS Canvas.Top="13" Canvas.Left="42" /> to a Canvas object or similar.

member function of the widget to override for your own drawing commands

The .NET object e contains a reference e.Graphics to the graphics context. All popular windowing systems use such a graphics context to both store information about which part of the window has to be drawn etc. and as an object on which one invoke all kinds of drawing operations. For instance, we may write:
using System.Drawing;
// ...
Pen p = new Pen(Color.Blue, 7);
e.Graphics.DrawLine(p, 12, 34, 56, 78);
You can consult the documentation or the automatic code completion for further drawing commands.

Demo: Control the form of a painted shape by a UI element such as a slider. Nothing will appear on screen as long as we don't force a redraw either by manipulating the windows or by explicitly calling Invalidate(), e.g. on the change event of the slider. Note that if you hide and unhide a part of a window, only this part will be redrawn for reasons of efficiency.

Demo: Create paint strokes with the mouse. Note that you have to store the strokes in order to reproduce them if the window has to be redrawn.

As the program repaints the window's content, we perceive flickering: The window is cleared and then the drawing is completed. It is better (but more costly) to complete the drawing in an offscreen buffer (back buffer) and then to quickly copy the final image into the window (front buffer). This is called double buffering. It's enabled by setting the form's property of the same name. In Java 2D, double buffering is enabled by default.

A transformation matrix Graphics.Transform controls the position, orientation, scaling, etc. of all (!) subsequent drawing commands. The effects of subsequent commands to change the matrix are cumulated to facilitate embedding graphics into graphics. (Again, this is similar in Java 2D.) The matrix can be changed through Graphics.RotateTransform(42) etc. The matrix cannot be changed through
Graphics g = ...; g.Transform.Rotate(42);
since a matrix is a C# struct, not a class; thus, the property
g.Transform returns only a copy of the matrix.

Retained-Mode Painting

In WPF and many other newer frameworks such as Adobe Flash, the rendering happens in retained mode: Our program does not issue drawing commands to be performed immediately. Rather, we create graphical objects and hand them to the framework. It retains them (hence the name) and will draw or redraw them as necessary. If the user changes properties of the graphical objects, the framework will automatically update the screen.

In WPF, graphical objects (descendants of Shape) are added to the Children collection of the parent System.Windows.Control, for instance a Canvas object. Only add after the call to InitializeComponent(). Geometric transformations are determined through an object's property RenderTransform. This can also be a TransformGroup to which single transformations are added through the Add(...) method of the Children property. Demo.

WPF allows property changes to be animated, which means that the rendering is done repeatedly but with changed values. Demo.

// class member:
Ellipse myEllipse = new Ellipse();

// in some event handler:
DoubleAnimation dba = new DoubleAnimation();
dba.From = 100;
dba.To = 500;
dba.Duration = new TimeSpan(0, 0, 2);
myEllipse.BeginAnimation(Ellipse.WidthProperty, dba);