Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Drawing a Grid-Covered Cylinder in WPF

0.00/5 (No votes)
27 Jan 2012 1  
Short program to draw, in 2D, a series of geometry that represents a grid-covered 3 dimensional cylinder
GridShapes

Introduction

This is a simple, beginner level project that takes a few dimensions from the user and then draws a 3 dimensional representation of a cylinder, using only 2D geometry. Basic concepts such as simple data binding and property change notification, as well as simple Geometry and Path objects are covered here.

The project as included in the source code is not useful for anything other than a graphical display of cylinders of various sizes. The intent of this article was to provide a starting point for basic WPF windows, and basic graphics drawing on a Canvas object. From this simple base, there are a great number of features and options that could be added to make this project more useful.

Background

This project/article served two purposes for me:

  1. It's about time that I learned C#. I've been a VB coder for years, so this was a quick and simple project with which to get started.
  2. I needed to brush up on my 2D drawing skills in WPF.

I was also intrigued by a question on StackOverflow that presented just this type of problem. It seemed interesting enough to come up with a full solution.

Using the Code

The window layout in WPF is very basic:

<Grid>
    <Grid.Background>
        <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
            <GradientStop Color="#FFBABABA" Offset="1" />
            <GradientStop Color="#FFE5E5E5" Offset="0" />
        </LinearGradientBrush>
    </Grid.Background>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="Auto"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <StackPanel Grid.Column="0" Margin="10,0">
        <TextBlock Margin="10" Text="Draw a cylinder with gridlines"
                   TextWrapping="Wrap" MaxWidth="150"
                   TextAlignment="Center"/>
        <TextBlock Text="Height:"/>
        <TextBox Text="{Binding Path=CylinderHeight}"/>
        <TextBlock Text="Radius:"/>
        <TextBox Text="{Binding Path=CylinderRadius}"/>
        <TextBlock Text="Divisions:"/>
        <TextBox Text="{Binding Path=CylinderDivisions}"/>
        <Button Margin="5,10" Click="ClickButton">
            Draw Cylinder
        </Button>
    </StackPanel>
    <Canvas Name="Canvas1" Grid.Column="1">
        <Canvas.Background>
            <LinearGradientBrush EndPoint="0,0" StartPoint="1,1">
                <GradientStop Color="#FFE2C98B" Offset="0" />
                <GradientStop Color="#FFFCEED2" Offset="1" />
            </LinearGradientBrush>
        </Canvas.Background>
            
    </Canvas>
</Grid>

You will notice in the XAML above that the only control with a name is the Canvas, because this control is passed to another class when the 'Draw Cylinder' button is clicked. This demonstrates one of the nicer features of WPF. Even the textboxes that are bound to data do not require explicit names.

The data binding is very basic and just binds the Text property of the 3 textboxes to corresponding properties of the DrawingClass Class. To complete the data binding, the constructor of the window instantiates a new instance of the DrawingClass and assigns it as the data context for the window (shown below):

public DrawingClass dc;

public MainWindow()
{
    InitializeComponent();
    dc = new DrawingClass();
    this.DataContext = dc;
} 

Shown below is an excerpt of the code used to draw the shape of the cylinder body and the arc that represents the bottom of the cylinder. The Canvas object is passed to this subroutine so that the various pieces of geometry can be added directly to it.

public void DrawCylinder(System.Windows.Controls.Canvas cnv)
        {
            //Clear the existing children from the canvas.
            cnv.Children.Clear();

The ratio of the top of the cylinder's width to its height is arbitrarily set to 0.3 in order to give the impression of viewing the cylinder in 3D from a position slightly above the cylinder.

Then 4 points describing the 'corners' of the cylinder object are calculated, as well as a point ptC that is the center of the ellipse that makes up the top surface of the cylinder.

int ellipseHeight = Convert.ToInt32( Math.Floor(cylinderRadius * 0.3));
Point ptUpperLeft = new Point(30, ellipseHeight*2);
Point ptUpperRight = new Point(30 + (cylinderRadius * 2), ptUpperLeft.Y);
Point ptLowerLeft = new Point(30, ptUpperLeft.Y + cylinderHeight);
Point ptLowerRight = new Point(ptUpperLeft.X + 
	(cylinderRadius * 2), ptUpperLeft.Y + cylinderHeight);
Point ptC = new Point(30 + cylinderRadius,ptUpperLeft.Y);

A new Path object is created that will contain the various pieces of Geometry and PathFigure Segments. This path is then added to the Canvas. The remainder of the code is not shown here, but uses similar techniques to add the rest of figures to the Canvas.

Path pth = new Path();
//Draw cylinder body.
LineSegment ln = new LineSegment(ptLowerLeft,true);
ArcSegment arc = new ArcSegment(ptLowerRight,new Size(cylinderRadius,ellipseHeight),
	0,false,System.Windows.Media.SweepDirection.Counterclockwise,true);
            
PathFigure pf = new PathFigure();
pf.StartPoint = ptUpperLeft;
//Add left side of cylinder.
pf.Segments.Add(ln);
//Add bottom arc of cylinder.
pf.Segments.Add(arc);
ln = new LineSegment(ptUpperRight,true);
//Add right side of cylinder.
pf.Segments.Add(ln);
PathGeometry pg = new PathGeometry();
pg.Figures.Add(pf);
pth.Stroke = new SolidColorBrush(Colors.Black);
pth.StrokeThickness = 2;
pth.Fill = new SolidColorBrush(Colors.White);
pth.Data = pg;
//Add path to the canvas.
cnv.Children.Add(pth);

Points of Interest

When drawing an arc, the simplest object to use is the ArcSegment object. A simple line can be represented equally easily by a LineGeometry or a LineSegment object. A complete ellipse is most easily drawn using an EllipseGeometry object.

History

  • 27th January, 2012: Initial post

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here