Software engineering and GUI design
WPF graphics intro
Usage of the FrameworkElement class
WPF graphics – coordinate system
• The (0,0) point is in the top-left corner, so every location is the position of the top-left corner based on the top-left corner of the container
– An object’s final location is affected by many properties:
HorizontalAlignment, Margin, Padding, layout manager settings,
…
– Taking all factors into account, there is a separate phase
(“Layout”) that determines the final 2D position – not trivial to read, very hard to write!
–
WPF graphics – types
(System.Windows)• Nothing is displayed, these types are only used for data representation!
• Struct types (value types)
• Point: a single point in 2D space – X, Y
• Size
– Width, Height
• Rect, Int32Rect: a rectangle in 2D space
– X, Y, Width, Height, Top, Bottom, Left, Right
• Vector: a location vector in 2D space – X, Y, Length, LengthSquared
WPF graphics – colors
• Every color is represented with four 8bit values:
• Red (R), Green (G), Blue (B) – bytes (0-255), so totally 256^3 ≈ 16 million colors are possible (“good enough”)
• Alpha (A) – Defines the transparency of the color, 0: fully transparent, 255: not transparent
• The Color struct is used to represent colors
Color color = Colors.Red;
Color color = Color.FromArgb(20, 255, 0, 0);
WPF graphics – brushes
WPF graphics – brushes
• C# definition:
• XAML definition:
//pre-defined solid color brush:
button.Background = Brushes.Red;
//self-made brush with pre-defined color:
button.Background = new SolidColorBrush(Colors.Aqua);
//self-made brush with self-made color:
Color almostRed = Color.FromArgb(20, 255, 0, 0);
button.Background = new SolidColorBrush(almostRed);
<Button Background="AliceBlue" Content="Button" .../>
<Button Background="#FFFF0000" Content="Button2" .../>
<Grid.Resources>
<SolidColorBrush x:Key="myColor" Color="Azure"/>
</Grid.Resources>
...
<Button Background="{StaticResource myColor}"></Button>
WPF graphics – other types
• Pen
– Used for drawing lines
– Fill (brush), thickness, line style, line endings
• Geometry
– Can represent an arbitrary geometrical shape (abstract class) – The descendants are used
– System.Windows.Rect vs
System.Windows.Media.RectangleGeometry vs System.Windows.Shapes.Rectangle
– Universal geometrical operations, even with very complex
geometrical shapes (e.g. we can create geometry from letters, union/intersection, splines, etc…)
Other geometry types
EllipseGeometry ellipse = new EllipseGeometry(new Point(10, 10), 10, 10);
FormattedText text = new FormattedText("This is my ellipse.",
CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Tahoma"), 16, Brushes.Black);
Geometry textGeometry =
text.BuildGeometry(new Point(10, 20));
GeometryGroup group = new GeometryGroup();
group.Children.Add(ellipse);
group.Children.Add(textGeometry);
• StreamGeometry = polygons
• PathGeometry = set of complex line segments
– From text, using FormattedText.BuildGeometry(xxx) method
• Handle multiple Geometry instances with GeometryGroup
– The GeometryGroup is a Geometry descendant, so it is very easy to handle with complex geometric shapes
8
UI element to display images: Image
• Image.Source: the image to be displayed
– Descendants of System.Windows.Media.ImageSource class
• Image.Stretch automatic resize (None, Fill, Uniform, UniformToFill)
D3DImage DrawingImage
BitmapSource (+descendants)
abcdef
Graphics possibilities in WPF
• Shapes (System.Windows.Shapes.Shape descendants) – Pre-made, simple controls
– Also accessible via the Toolbox (Rectangle, Ellipse...)
– Input, focus, events, data binding: to be handled individually…
– Only if few (max ~100) objects are used
• Drawings (System.Windows.Media.Drawing descendants)
– Cannot be used on their own, must be put into a hosting object ( Image.Source = new DrawingImage(xxx) )
– No support for input events, the hosting object handles its own events
– Also usable from XAML, with a few data bindings and converters – Faster than the Shape controls (max ~1000 objects)
• Visuals (System.Windows.Media.Visual descendants)
– The most complex, also the fastest (max ~10000 drawn objects) – Drawn objects no need to be stored in the memory
– Always handled from code hard to write pretty code!
Graphics possibilities in WPF
Approach Event handling /
Display handling Usage Number of
objects Shape Drawn object =
control, individually for every controls
XAML + Binding ~100
Drawing Hosting object (image) handles all drawn
objects together
XAML + Binding + Converters
(xxx Drawing)
~1000
Visual Hosting object displays the drawn objects
together, no real object storage, only used as a “drawing canvas”
CS code:
OnRender +
DrawingContext + Geometry
~10000
Usage of Visual descendants
• Typically we do this not in a Window, but rather in a unique FrameworkElement descendant
– Using the OnRender() method we can issue drawing commands – DrawingContext the drawing canvas that we draw upon
– Not an immediate operation (retained mode)
• Refresh of the control can always be enforced using the
InvalidateVisual() method which MUST NEVER be called from the OnRender method
class MainWindow : Window // window, we need Background=Transparent
{
protected override void OnRender(DrawingContext drawingContext)
{
drawingContext.DrawGeometry(Brushes.Blue, new Pen(Brushes.Red, 2), geometry);
} }
FrameworkElement rules
• Empty (not drawn) areas will not fire the mouse events
– The first drawn object should be a big background rectangle
• Keyboard events are not captured
– The window will capture them, and we can always access the window using Window.GetWindow(this) (not elegant, but this semester it is good enough for us)
– OR: Focusable=true and the control will also capture the keyboard events (if it has the keyboard focus!)
• Try and use it in a nicely layered way
– It is very easy to write hard-to-maintain code – Aim: Model + Logic + Control
• Home-practice exercises: at the end of this presentation
Accessing UI elements from multiple threads
• In Windows, UI elements (controls) can typically only be accessed from the creator thread (GUI thread, UI thread)
– That includes indirect access
– Few exceptions (some methods, PropertyChanged event)
• Universal solution: Invoke
– Ask a callback from the UI thread (Blocking! Deadlock danger!)
Dispatcher.Invoke(() =>
{
label.Content = ...;
listBox.Items.Add(...);
});
• Using Task: alternative Task scheduler – Default: Thread Pool Task Scheduler
– It is possible to assign new tasks to the GUI-thread scheduler
Accessing UI elements from multiple threads
• GUI task scheduler
– Obtain reference from the GUI thread
– TaskScheduler.FromCurrentSynchronizationContext()
Task<int> taskWithReturnValue =
Task.Run(() => { Thread.Sleep(1000); return 6; });
taskWithReturnValue.ContinueWith(
t => textBox1.Text = "Result: " + t.Result, CancellationToken.None,
TaskContinuationOptions.OnlyOnRanToCompletion,
TaskScheduler.FromCurrentSynchronizationContext());
System.Windows.Threading.Dispatch erTimer
• We use it to execute operations periodically (GameLogic,automatic actions)
– Interval: property to set how often the timer will be executed (TimeSpan type)
– Start(), Stop(): Methods
– Tick: Event that signals that the specified interval is over (with the event, the measurement of the interval re-starts)
• Many different Timer types – do not mix them up!
– System.Timers.Timer Threadsafe, very precise
– System.Threading.Timer Uses a Threadpool thread, not threadsafe – System.Windows.Forms.Timer Windows forms component
– System.Web.UI.Timer Periodic webpage postback (synchronized or async)
– System.Windows.Threading.DispatcherTimer WPF threadsafe timer that uses the DispatcherThread
Lesson Exercise: Pong
Flappy Bird
Transformations
• Represented using Transform descendants (they are matrices) – RotateTransform
– ScaleTransform – SkewTransform
– Move (TranslateTransform)
– Universal transformations (MatrixTransform)
– Multiple transformations after each other (TransformGroup)
Using transformations
• FrameworkElement + Layout manager (Grid, StackPanel…) – LayoutTransform (before the layout phase)
– RenderTransform (after the layout phase)
• Geometry
– Geometry geo = new RectangleGeometry(xxx);
– geo.Transform = new xxxTransform(xxx);
– Warning: doesn’t modify the original geometry coordinates – Geometry result = geo.GetFlattenedPathGeometry()
– Erase transformation: geo.Transform = Transform.Identity;
• DrawingContext
– DrawingContext.PushTransform(new TranslateTransform(xxx));
– DrawingContext.DrawXXX(xxx);
– DrawingContext.Pop();
Using transformations (in our games)
• We should set every gameitem’s Geometry datafield to make them centered around (0;0)
• Then before draw/collision detection, we transform them into the appropriate position/orientation
• Slower (due to the continuous re-calculation), but makes modeling/programming a lot easier
Intersection of geometries (collision detection)
•To check for collisions, we have to create a new geometry from the intersection of the two geometries (Geometry.Combine()), and then calculate surface area (GetArea())
• Warning: LineGeometry has no surface area, so the combined geometry will always have a surface area of zero
– Solvable by „extending” the line – Geometry result =
geometry.GetWidenedPathGeometry(new Pen(Brushes.Blue, 2))
– The color of the pen is not used, only the line style/thickness
Geometry intersection = Geometry.Combine(geometry,
otherGeometry, GeometryCombineMode.Intersect, null);
return intersection.GetArea() != 0;
Lesson exercise: Flappy Birds Turbo
Class Bird : GameItem
Class Pipe : GameItem
Lesson exercise: Laby the Ultimate
Labyrinth
Single-responsibility classes
• Combine the previously known solutions AND – Attaching resources (file-s, do not overuse!) – Load images: BMP/PNG/JPG ImageBrush
– Tiled Brush: for drawing walls, Tile vs Pixel coordinates – More efficient render process
• Builder Design Pattern
• Saving the ImageBrush instances into a Dictionary
• Saving the Drawing (Brush + Pen + Geometry) instances into variables
– Mouse clicks only work on drawn areas big background image
– Subscribe to the KeyDown event of the owner window – Refresh the GameControl
Tiled brush?
Creating a self-made control
• Can be used from XAML the usual way – Zero-parameter constructor must exist
• Inheritance: from FrameworkElement base class – All our games beforehand were in fact, controls
– Using the same rules, we can create a normally usable control that won’t fill up the whole window, and fits into the layout – Subscription to window events / MessageBox is not elegant
• Inheritance: from arbitrary control as a base class
– Extend a pre-existing control with extra functionality
• Composition: User control
– Create new control by assembling pre-existing controls
Accessing properties from XAML
• Properties/events can be accessed the usual way – <local:UpDownControl IsDown="True"
IsDownChanged="UpDownControl_IsDownChanged„
• Data binding: IsDown="{Binding Path=PointsDown}"
– Data binding WILL NOT work with simple properties – It uses “Dependency Property” properties
– If you want a bool-typed Dependency property, it can be described as a global storage similar to
public static Dictionary<UpDownControl, bool> IsDownProperty public bool IsDown
{
get { return IsDownProperty[this]; } set { IsDownProperty[this] = value; } }
Dependency Property
public static readonly DependencyProperty IsDownProperty =
DependencyProperty.Register(xxx);
public bool IsDown {
get { return (bool)GetValue(IsDownProperty);
}
set { SetValue(IsDownProperty, value); } }
DependencyProperty.Register(
nameof(IsDown), typeof(bool), typeof(UpDownControl),
Lesson exercise: UpDownControl +
SortedLB
Software engineering and GUI design
Practice exercises
Home practice: SimpleLaby
Home practice: SimpleLaby
• If the player finishes a level, a new one should be automatically loaded
• We should measure the time for every level (System.Diagnostics.Stopwatch class)
• In a new window, display the level times using a Graph
drawn using a GeometryDrawing
Home practice: Asteroids
Home practice: Asteroids
• Asteroids rules:
– The player can only rotate and shoot.
– The asteroid moves in a random direction using a constant speed, when it hits the side of the gamescreen, it re-appears on the other side
– If the bullet hits an asteroid, it disappears (real version: it splits up into smaller asteroids)
– The bullet also re-appears on the other side of the gamescreen (but the bullet is only “alive” for a given amount of time)
Home practice: Chaos Frogger
Home practice: Chaos Frogger
• A single obstacle can only move in the up-down OR in the left-right directions. If they reach the side of the window, they reverse
• Their speed changes randomly, and also there are some evil obstacles that speed up if the player is nearby
• The player has three lives at the beginning, and we have
to get to the bottom-right corner. If an obstacle touches
the player, then the player loses a life.
Home practice: Tic Tac Toe
– Rules: the players take turns in putting down their respective
marks. The one who makes up three items horizontally, vertically or diagonally will win the game
– If the playing field fills up without a win, then it’s a tie