Drawing on a WPF Canvas

This series of steps requires the use of Oxygene within Visual Studio. WPF has powerful drawing capabilities, and this example demonstrates paths, arcs, text, borders, rounded corners, rotation, translation and scale. You can produce graphics with XAML and/or Oxygene code. The XAML code is often neater but with Oxygene code you have additional capabilities such as conditional statements and loops.

The result will be a program in which displays this graphic:

Output

Output

  1. Select menu item File > New > Project (or type Ctrl+Shift+N), highlight the WPF Application and name the project WPF_DrawDemo. Click the OK button.
  2. Open the Toolbox and double click on Canvas to add it to the form.
    Adding the Canvas

    Adding the Canvas

  3. Use the Properties window to name the Canvas cnv.
  4. Edit the Window1.xaml file. Change the title to WPF Draw Demo and the Width to 400.
  5. Drag the sides of the canvas to enlarge it. Our Window1.xaml file was as follows.
    <?xml version='1.0' encoding='utf-8' ?>
    <Window x:Class="WPF_DrawDemo.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="WPF Draw Demo" Height="300" Width="400">
        <Grid>
            <Canvas x:Name="cnv" HorizontalAlignment="Left" Height="290" Margin="0,0,0,0" VerticalAlignment="Top" Width="380">          
            </Canvas>
        </Grid>
    </Window>
  6. Paste the following code to replace the existing code in Window1.xaml.pas:
    namespace WPF_DrawDemo;
    
    interface
    
    uses
      System.Windows, System.Windows.Controls, System.Windows.Media,
      System.Windows.Shapes;
    
    type
      Window1 = public partial class(System.Windows.Window)
      public
        constructor;
      end;
      
    implementation
    
    constructor Window1;
    begin
      InitializeComponent();
      // Blue rounded rectangle
      var r := new Rectangle;    
      r.Fill := new SolidColorBrush(Colors.Blue);
      r.Height := 100;
      r.Width := 200;
      r.RadiusX := 20;
      r.RadiusY := 20;
      cnv.SetLeft(r, 50);
      cnv.SetTop(r, 140);
      cnv.Children.Add(r);
    
      // Red ellipse
      var e := new Ellipse;
      e.Fill := new SolidColorBrush(Colors.Red);
      e.Height := 68;
      e.Width := 200;
      cnv.SetLeft(e, 20);
      cnv.SetTop(e, 55);
      cnv.Children.Add(e);
    
      // Orange text
      var tb := new TextBlock;
      tb.Text := 'pp4s.co.uk';
      // Brushes.Navy;
      tb.Foreground := new SolidColorBrush(Color.FromRgb(255, 120, 0));
      tb.FontFamily := new FontFamily('Arial');
      tb.FontSize := 24;
      cnv.SetLeft(tb, 160);
      cnv.SetTop(tb, 10);
      cnv.Children.Add(tb);
    
      // Filled arcs joined by line
      var pathGeom: PathGeometry := new PathGeometry;
      var pathFig: PathFigure := new PathFigure;
      pathFig.StartPoint := new Point(20, 45);
      pathFig.IsClosed := true;
      pathGeom.Figures.Add(pathFig);
      var arcSeg1 := new ArcSegment; 
      arcSeg1.Point := new Point (60, 45); // end of ellipse (the other end being the previous point)
      arcSeg1.Size := new Size(30, 20); // x and y radii of ellipse 
      arcSeg1.SweepDirection := SweepDirection.Clockwise;
      arcSeg1.IsLargeArc := true;
      arcSeg1.RotationAngle := 30;
      pathFig.Segments.Add(arcSeg1);
      var lineSeg := new LineSegment;
      lineSeg.Point := new Point(80, 45);
      pathFig.Segments.Add(lineSeg);
      var arcSeg2 := new ArcSegment; 
      arcSeg2.Point := new Point (120, 45); 
      arcSeg2.Size := arcSeg1.Size; 
      arcSeg2.SweepDirection := SweepDirection.Clockwise;
      arcSeg2.IsLargeArc := true;
      arcSeg2.RotationAngle := -30;
      pathFig.Segments.Add(arcSeg2);
      var path1: Path := new Path;
      path1.Stroke := new SolidColorBrush(Colors.Green);
      path1.Fill := new SolidColorBrush(Colors.Yellow);
      path1.StrokeThickness := 2;
      path1.Data := pathGeom;
      cnv.Children.Add(path1);
    
      // Rotated, scaled and translated pentagon
      var path2: Path := new Path;
      path2.Stroke := path1.Stroke;
      path2.Fill := path1.Fill;
      // Take pentagon data from pp4s.co.uk/main/tu-vector-graphics-svg.html
      path2.Data := Geometry.Parse("m 212.6, 141.7 l 0,-35.4 l 26.5,-35.4 l 26.6,35.4 l 0,35.4 l -53.2,0");
      var rt: RotateTransform := new RotateTransform; 
      rt.Angle := 25; 
      var st: ScaleTransform := new ScaleTransform; 
      st.ScaleY := 2; 
      st.ScaleX := 2; 
      var tt: TranslateTransform := new TranslateTransform; 
      tt.Y := -320;
      tt.X := - 30; 
      // Create TransformGroup and add 3 transforms to it. 
      var ttg: TransformGroup := new TransformGroup;  
      ttg.Children.Add(rt); 
      ttg.Children.Add(st);
      ttg.Children.Add(tt);   
      path2.RenderTransform := ttg;
      cnv.Children.Add(path2);
    end;
      
    end.
    
  7. Press F5 or the green triangle to compile and run the application.
    The output should be similar to the screenshot near the top of the page.
  8. Experiment!
    WPF is thoroughly documented. See, for example, pages on brushes, ArcSegments and transforms.
Programming - a skill for life!

How to write programs in the Oxygene for .Net dialect of Pascal