在本文中,将探讨如何使用Windows Presentation Foundation (WPF) 和C#语言创建一个简单的3D圆柱体绘制程序。这个项目不仅作为学习C#的起点,同时也帮助复习了WPF中的2D绘图技能。此外,这个项目也是对StackOverflow上一个相关问题的完整解决方案。
对于作者来说,这个项目有两个目的:
1. 学习C#语言。作者多年来一直使用VB编程,因此选择这个简单项目作为开始。
3. 对StackOverflow上的一个问题感到好奇,这个问题正是关于如何绘制3D圆柱体的。
WPF中的窗口布局非常简单,使用XML格式定义。以下是窗口布局的示例代码:
<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>
在上述XAML代码中,唯一命名的控件是Canvas,因为它在点击"Draw Cylinder"按钮时被传递到另一个类中。这展示了WPF的一个特性:即使是绑定数据的文本框也不需要显式命名。
数据绑定非常基础,它将三个文本框的Text属性绑定到DrawingClass类的相应属性。为了完成数据绑定,窗口的构造函数实例化了DrawingClass的一个新实例,并将其设置为窗口的数据上下文,如下所示:
public DrawingClass dc;
public MainWindow()
{
InitializeComponent();
dc = new DrawingClass();
this.DataContext = dc;
}
以下是用于绘制圆柱体主体和表示圆柱体底部的弧的代码片段。Canvas对象被传递到这个子程序中,以便可以直接将各种几何图形添加到它上面。
public void DrawCylinder(System.Windows.Controls.Canvas cnv)
{
// 清除画布上现有的子元素。
cnv.Children.Clear();
// 圆柱体顶部宽度与高度的比例任意设置为0.3,以便从稍微高于圆柱体的位置观看时,给人一种3D的视觉效果。
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);
Path pth = new Path();
// 绘制圆柱体主体。
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;
// 添加圆柱体左侧。
pf.Segments.Add(ln);
// 添加圆柱体底部弧。
pf.Segments.Add(arc);
ln = new LineSegment(ptUpperRight, true);
// 添加圆柱体右侧。
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;
// 将路径添加到画布。
cnv.Children.Add(pth);
}