Click here to Skip to main content
Click here to Skip to main content

Right in the Eye - Creating a WPF Custom Control

, 3 Dec 2010
Rate this:
Please Sign up or sign in to vote.
Creating a custom control using WPF and C#

EyeDemo Application

EyeDemo WPF Application

Introduction

This article demonstrates how to make an Eye implemented as a custom controls based on WPF & C#.

The idea is from one of my previous articles: Eyes (Building the perfect useless control).

Background

Creating the Eyes article made me wonder if it would be easier to do the same using WPF and 3D objects. I went ahead and tried some ideas, the results came fairly quickly but the XAML was messy. Hiding the mess using a custom control felt natural.

Classes & Architecture

Classes

There are 3 classes involved in creating the application. The Eye & Ball class map the properties to the style and the Sphere class creates a resource for the Eye style.

Using the Eye control can be done by dragging the Eye control from the toolbox or using the following example XAML code:

<EyeControl:Eye  RotationYAxis="180" RotationXAxis="0" PupilSize="1" IrisSize="0.7" />

Remember to set the namespace:

xmlnss:EyeControl="clr-namespace:EyeControl;assembly=EyeControl"

Design & Style

Design Elements

Creating the Eye is done with the following elements:

Eye graphics

The implementation of the elements is found in the Eye style (contained in /Themes/Generic.xaml).

"Eye" Style

    <!--<span class="code-comment"> Eye --></span>
<Style TargetType="{x:Type local:Eye}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:Eye}">
        <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
          <Viewport3D x:Name="viewPort" 
          	Margin="4" Grid.Column="0">
            <Viewport3D.Camera>
        ...
            </Viewport3D.Camera>
 
            <Viewport3D.Children>
              <ModelVisual3D>
                <ModelVisual3D.Content>
                  <Model3DGroup>
                    <GeometryModel3D Geometry="{Binding Source=
                    	{StaticResource sphere}, Path=Geometry}">
                      <GeometryModel3D.Material>
                        <MaterialGroup>
                          <!--<span class="code-comment"> (Egg)White --></span>
                          <DiffuseMaterial Brush="#FFFFFFFF" />
                          <!--<span class="code-comment"> Iris --></span>
                          <DiffuseMaterial>
                            <DiffuseMaterial.Brush>
                              <VisualBrush  Stretch="None" Opacity="1">
                                <VisualBrush.Visual>
                                  <Ellipse Width=".16" 
                                  	Height=".29" StrokeThickness=".005" 
                                  	Stroke="{Binding Path=IrisRimColor, 
                                  	RelativeSource={RelativeSource TemplatedParent}}">
                                    <Ellipse.Fill>
                                      <RadialGradientBrush GradientOrigin=".5,.5">
                                        <GradientStop Color="{Binding Path=IrisInnerColor, 
                                        RelativeSource={RelativeSource TemplatedParent}}" 
                                        Offset="0.0" />
                                        <GradientStop Color=
					"{Binding Path=IrisMiddleColor, 
                                        RelativeSource={RelativeSource TemplatedParent}}" 
                                        Offset="0.5" />
                                        <GradientStop Color=
					"{Binding Path=IrisOuterColor, 
                                        RelativeSource={RelativeSource TemplatedParent}}" 
                                        Offset="1.0" />
                                      </RadialGradientBrush>
                                    </Ellipse.Fill>
                                  </Ellipse>
                                </VisualBrush.Visual>
                                <VisualBrush.Transform>
                                  <ScaleTransform ScaleX="{Binding Path=IrisSize, 
                                  RelativeSource={RelativeSource TemplatedParent}}" 
                                  ScaleY="{Binding Path=IrisSize, RelativeSource=
                                  {RelativeSource TemplatedParent}}" CenterX=".5" 
                                  CenterY=".5"/>
                                </VisualBrush.Transform>
                              </VisualBrush>
                            </DiffuseMaterial.Brush>
                          </DiffuseMaterial>
                          <!--<span class="code-comment">Pupil--></span>
                          <DiffuseMaterial>
                            <DiffuseMaterial.Brush>
                              <VisualBrush  Stretch="None" Opacity="1">
                                <VisualBrush.Visual>
                                  <Ellipse Width=".016" Height="0.029" 
                                  Fill="{Binding Path=PupilColor, 
                                  RelativeSource={RelativeSource TemplatedParent}}"/>
                                </VisualBrush.Visual>
                                <VisualBrush.Transform>
                                  <ScaleTransform ScaleX="{Binding Path=PupilSize, 
                                  RelativeSource={RelativeSource TemplatedParent}}" 
                                  ScaleY="{Binding Path=PupilSize, RelativeSource=
                                  {RelativeSource TemplatedParent}}" CenterY=".5" 
                                  CenterX=".5" />
                                </VisualBrush.Transform>
                              </VisualBrush>
                            </DiffuseMaterial.Brush>
                          </DiffuseMaterial>
                          <!--<span class="code-comment"> Light reflex--></span>
                          <SpecularMaterial SpecularPower="100">
                            <SpecularMaterial.Brush>
                              <SolidColorBrush Color="#DBDBDB" Opacity="1.000000"/>
                            </SpecularMaterial.Brush>
                          </SpecularMaterial>
                          <EmissiveMaterial Brush="#05FF0000" />
                        </MaterialGroup>
                            
                      </GeometryModel3D.Material>
                      <!--<span class="code-comment"> Transformation --></span>
                      <GeometryModel3D.Transform>
                        <Transform3DGroup>
                          <RotateTransform3D>
                            <RotateTransform3D.Rotation>
                              <AxisAngleRotation3D Axis="1,0,0" 
                              Angle="{Binding Path=RotationXAxis, 
                              RelativeSource={RelativeSource TemplatedParent}}"/>
                            </RotateTransform3D.Rotation>
                          </RotateTransform3D>
                          <RotateTransform3D>
                            <RotateTransform3D.Rotation>
                              <AxisAngleRotation3D Axis="0,1,0" 
                              Angle="{Binding Path=RotationYAxis, 
                              RelativeSource={RelativeSource TemplatedParent}}"/>
                            </RotateTransform3D.Rotation>
                          </RotateTransform3D>
                        </Transform3DGroup>
                      </GeometryModel3D.Transform>
                    </GeometryModel3D>
 
                    <!--<span class="code-comment"> Lights --></span>
                    <AmbientLight Color="#FF646464" />
                    <SpotLight InnerConeAngle="29" 
                    OuterConeAngle="31" Color="#666666" 
                    Direction=".94,1.2,3.1" Position="-2,-0.8,-8" 
                    Range="20"/>
                    <DirectionalLight Color="#CC666666" 
                    Direction="1,-1,1"/>
                    <DirectionalLight Color="#FF444444"  
                    Direction="0,1,5"/>
                  </Model3DGroup>
                </ModelVisual3D.Content>
              </ModelVisual3D>
            </Viewport3D.Children>
          </Viewport3D>
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

Point of Interest

ToolboxBitmaps

Setting up a toolbox bitmap icon is quite easy, add the following attribute to the class:

namespace EyeControl
{
  [ToolboxBitmap(typeof(Eye))]
  public class Eye : Control
  {...

Add a bitmap to the Resources with a file name like: namespace.class.icon.bmp (the resource name is not important). In the example, I use the file: EyeControl.Eye.icon.bmp.

toolbox bitmap icon

Eye & Ball ... eyeball (haha)

Categories on Properties

Setting up the category attribute for the properties is the same with WFP as with Windows Forms, here is an example:

[Category("Eye"), Browsable(true)]
[Description("The size of the pupil (0-5).")]
public double PupilSize
{
  get { return (double)GetValue(PupilSizeProperty); }
  set { SetValue(PupilSizeProperty, value); }
}

Properties with Eye category

Categorized properties.

And oh...

I spent some time figuring out that it was necessary to cast the default value of a dependency property to its value type which seemed totally redundant.

Summary

Creating the Eye custom controls using WPF was not exactly easy, but the possibilities seems endless and performance is great.

Most difficulties I faced derived from the separation of layers where usage (XAML), code behind (C#) & styles (XAML) all must be combined to form the control.

History

This is the first version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Niel M.Thomas
Software Developer (Senior)
Denmark Denmark
Name: Niel Morgan Thomas
Born: 1970 in Denmark
Education:
Dataengineer from Odense Technical University.
More than 20 years in IT-business.
Current employment:
Working with application development in a major Danish company that produce medical equipment.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberPeterFJorgensen23-Oct-12 22:14 
GeneralGreat stuff PinmemberAeroSign3-Dec-10 23:10 
GeneralYes you are correct PinmvpSacha Barber3-Dec-10 21:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140721.1 | Last Updated 3 Dec 2010
Article Copyright 2010 by Niel M.Thomas
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid