| WPF UserControl library: 3D-Cube |
| Section: WPF | Rating: Not rated yet! |
 | CodeGod submitted this resource The member's homepage is http://www.codegod.de Visit the profile here |
Attachment
Introduction
By reading this article you will learn how to create UserControls with WPF in a Control-library (Assembly) and use them in a WPF Application. In this example we will implement a UserControl that renders a 3D-Cube.
Figure 1The idea
We will create a WPF-UserControl and use it in a WPF Application, the UserControl renders a 3D-Cube. In the Control the environment for rendering 3D-models is defined via XAML. For adding the cube to the scene we implement some helper-classes that provide methods to create the cube, enlight, position, colorize and rotate it. For that approach we use the WPF-Objectmodel. In the WPF Application we add a reference to the Assembly in which the UserControl is defined in. After that, we can drag the UserControl from the toolbox into the Window of the WPF-App.
The Cube-UserControl
The first step is to create a WPF UserControl-library with VS 2008 Beta2. Just choose File->New->Project and then WPF User Control Library:
Figure 2In our project a UserControl UserControl1 has been created for us, rename it to CubeControl. The XAML for our UserControl looks like this:
<UserControl x:Class="WpfCubeControl.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<Grid>
</Grid>
</UserControl>
We want to display a 3D-Model in it, so we need to define a ViewPort3D. A ViewPort3D defines the area, where our 3D models are displayed. A Viewport3D has a positioned Camera with a Vector that defines the direction in which we look at the displayed objects. Here the XAML:
<UserControl x:Class="WpfCubeControl.CubeControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Viewport3D Name="mainViewport" ClipToBounds="True">
<Viewport3D.Camera>
<PerspectiveCamera
Position="10,8,9"
LookDirection="-11,-9,-10"
FarPlaneDistance="100"
UpDirection="0,1,0"
NearPlaneDistance="1"
FieldOfView="70" >
<PerspectiveCamera.Transform>
<Transform3DGroup>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D x:Name="rotY"
Axis="0 1 0"
Angle="0" />
</RotateTransform3D.Rotation>
</RotateTransform3D>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D x:Name="rotX"
Axis="1 0 0"
Angle="0" />
</RotateTransform3D.Rotation>
</RotateTransform3D>
<RotateTransform3D>
<RotateTransform3D.Rotation>
<AxisAngleRotation3D x:Name="rotZ"
Axis="0 0 1"
Angle="0" />
</RotateTransform3D.Rotation>
</RotateTransform3D>
</Transform3DGroup>
</PerspectiveCamera.Transform>
</PerspectiveCamera>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<DirectionalLight
Color="White"
Direction="-2,-3,-1" />
</ModelVisual3D.Content>
</ModelVisual3D>
</Viewport3D>
</UserControl>
To enhance it a little bit we define a Transform3DGroup to rotate the cube around the X-, Y- and Z-axis. We give the AxisAngleRotation3D-objects a name, because we want to manipulate the instances later. In addition, we define a directed light in white color to get a nice light-effect on the cube.
In the Codebehind of the UserControl we add some methods to manipulate the rotation and the color of the cube:
public Color CubeColor
{
get { return _cubeColor; }
set { _cubeColor = value; }
}
public void RotateX(double angle)
{
rotX.Angle = angle;
}
public void RotateY(double angle)
{
rotY.Angle = angle;
}
public void RotateZ(double angle)
{
rotZ.Angle = angle;
}
To generate the cube, a class CubeBuilder is used:
namespace WpfCubeControl
{
public class CubeBuilder : ModelBuilder
{
public CubeBuilder(Color color)
: base(color)
{
}
public ModelVisual3D Create()
{
Model3DGroup cube = new Model3DGroup();
Point3D p0 = new Point3D(0, 0, 0);
Point3D p1 = new Point3D(5, 0, 0);
Point3D p2 = new Point3D(5, 0, 5);
Point3D p3 = new Point3D(0, 0, 5);
Point3D p4 = new Point3D(0, 5, 0);
Point3D p5 = new Point3D(5, 5, 0);
Point3D p6 = new Point3D(5, 5, 5);
Point3D p7 = new Point3D(0, 5, 5);
//front
cube.Children.Add(CreateTriangle(p3, p2, p6));
cube.Children.Add(CreateTriangle(p3, p6, p7));
//right
cube.Children.Add(CreateTriangle(p2, p1, p5));
cube.Children.Add(CreateTriangle(p2, p5, p6));
//back
cube.Children.Add(CreateTriangle(p1, p0, p4));
cube.Children.Add(CreateTriangle(p1, p4, p5));
//left
cube.Children.Add(CreateTriangle(p0, p3, p7));
cube.Children.Add(CreateTriangle(p0, p7, p4));
//top
cube.Children.Add(CreateTriangle(p7, p6, p5));
cube.Children.Add(CreateTriangle(p7, p5, p4));
//bottom
cube.Children.Add(CreateTriangle(p2, p3, p0));
cube.Children.Add(CreateTriangle(p2, p0, p1));
ModelVisual3D model = new ModelVisual3D();
model.Content = cube;
return model;
}
}
}
As you can see it derives from a class called ModelBuilder. This class holds the methods to create the triangles the cube is made of:
namespace WpfCubeControl
{
public class ModelBuilder
{
private Color _color;
public ModelBuilder(Color color)
{
_color = color;
}
public Model3DGroup CreateTriangle(Point3D p0, Point3D p1, Point3D p2)
{
MeshGeometry3D mesh = new MeshGeometry3D();
mesh.Positions.Add(p0);
mesh.Positions.Add(p1);
mesh.Positions.Add(p2);
mesh.TriangleIndices.Add(0);
mesh.TriangleIndices.Add(1);
mesh.TriangleIndices.Add(2);
Vector3D normal = VectorHelper.CalcNormal(p0, p1, p2);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
mesh.Normals.Add(normal);
Material material = new DiffuseMaterial(
new SolidColorBrush(_color));
GeometryModel3D model = new GeometryModel3D(
mesh, material);
Model3DGroup group = new Model3DGroup();
group.Children.Add(model);
return group;
}
}
}
The last thing we need is a method to render our cube which we define in the CubeControl:
public void Render()
{
CubeBuilder cubeBuilder = new CubeBuilder(_cubeColor);
mainViewport.Children.Add(cubeBuilder.Create());
}
Using the Control
To use the control in a WPF application we add a WPF Aplication-project to the solution. After that we add a project-reference to the Control-library in the WPF Application. Of course you can also add the CubeControl to the Toolbox of Visual Studio and drag it to the WPF Window of our WPF Application. The code of the XAML-Window looks like this:
<Window x:Class="Wpf3DApp.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="3DCube Demo" Height="388" Width="410"
xmlns:my="clr-namespace:WpfCubeControl;assembly=WpfCubeControl">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="247*" />
<RowDefinition Height="66" />
</Grid.RowDefinitions>
<my:CubeControl Name="cubeControl1" CubeColor="Thistle" Grid.RowSpan="1" />
<StackPanel Background="#FFEEEEEE" Grid.Row="1">
<StackPanel FlowDirection="LeftToRight" Orientation="Horizontal">
<Label Name="label1" Width="120" Height="23" VerticalAlignment="Top">
Rotations (X,Y,Z)</Label>
</StackPanel>
<StackPanel Margin="5" FlowDirection="LeftToRight"
Grid.RowSpan="2" VerticalAlignment="Bottom" Orientation="Horizontal">
<Slider Name="rotateX" Width="80" Value="0" Maximum="360" Minimum="0"
ValueChanged="rotateX_ValueChanged" />
<Slider Maximum="360" Name="rotationY" Value="0"
Grid.Row="1" Width="80" Minimum="0" ValueChanged="rotationY_ValueChanged"/>
<Slider Maximum="360" Name="rotationZ" Value="0" Width="80"
Minimum="0" ValueChanged="rotationZ_ValueChanged" />
<ComboBox Height="21" Name="cbColors" Width="120"
SelectionChanged="cbColors_SelectionChanged" />
</StackPanel>
</StackPanel>
</Grid>
</Window>
We added some sliders to manipulate the rotation of the cube and a ComboBox with all known colors to colorize the cube with the selected color.
Last but not least the code for displaying the cube and manipulating it's properties:
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
KnownColor t = new KnownColor();
foreach (KnownColor kc in System.Enum.GetValues(t.GetType()))
{
System.Drawing.ColorConverter cc = new System.Drawing.ColorConverter();
System.Drawing.Color c = System.Drawing.Color.FromName(kc.ToString());
if( !c.IsSystemColor )
cbColors.Items.Add( c );
}
cbColors.SelectedIndex = 0;
cubeControl1.Render();
}
private void rotateX_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
cubeControl1.RotateX(e.NewValue);
}
private void rotationY_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
cubeControl1.RotateY(e.NewValue);
}
private void rotationZ_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
cubeControl1.RotateZ(e.NewValue);
}
private void cbColors_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
System.Drawing.Color color = ( System.Drawing.Color ) cbColors.SelectedItem;
cubeControl1.CubeColor = System.Windows.Media.Color.FromRgb(color.R, color.G, color.B);
cubeControl1.Render();
}
}
I know, this is very much for the beginning. But the project is attached and you will fully understand it while studying the code. Have fun!