65.9K
CodeProject 正在变化。 阅读更多。
Home

视线 - 创建 WPF 自定义控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (13投票s)

2010年12月3日

CPOL

2分钟阅读

viewsIcon

31430

downloadIcon

955

使用 WPF 和 C# 创建自定义控件。

EyeDemo 应用程序

EyeDemo WPF Application

引言

本文演示了如何使用 WPF 和 C# 创建一个基于自定义控件的眼睛。

这个想法源自我之前的文章:Eyes (构建完美的无用控件)。

背景

创建“眼睛”的文章让我思考,使用 WPF 和 3D 对象是否更容易实现相同的功能。我尝试了一些想法,结果很快就出来了,但 XAML 代码很混乱。使用自定义控件隐藏混乱似乎很自然。

类和架构

Classes

创建应用程序涉及 3 个类。EyeBall 类将属性映射到样式,而 Sphere 类为 Eye 样式创建一个资源。

使用 Eye 控件可以通过从工具箱拖动 Eye 控件或使用以下示例 XAML 代码来完成

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

请记住设置 namespace(命名空间)

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

设计和样式

设计元素

创建 Eye 使用以下元素

Eye graphics

这些元素的实现可以在 Eye 样式中找到(包含在 /Themes/Generic.xaml 中)。

“眼睛”样式

    <!-- Eye -->
<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>
                          <!-- (Egg)White -->
                          <DiffuseMaterial Brush="#FFFFFFFF" />
                          <!-- Iris -->
                          <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>
                          <!--Pupil-->
                          <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>
                          <!-- Light reflex-->
                          <SpecularMaterial SpecularPower="100">
                            <SpecularMaterial.Brush>
                              <SolidColorBrush Color="#DBDBDB" Opacity="1.000000"/>
                            </SpecularMaterial.Brush>
                          </SpecularMaterial>
                          <EmissiveMaterial Brush="#05FF0000" />
                        </MaterialGroup>
                            
                      </GeometryModel3D.Material>
                      <!-- Transformation -->
                      <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>
 
                    <!-- Lights -->
                    <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>

看点

工具箱位图

设置工具箱位图图标非常简单,将以下属性添加到类中

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

将位图添加到资源中,文件名为:namespace.class.icon.bmp(资源名称不重要)。 在示例中,我使用文件:EyeControl.Eye.icon.bmp

toolbox bitmap icon

眼睛和眼球……眼球(哈哈)

属性分类

设置属性的类别属性与 WPF 和 Windows Forms 相同,这是一个例子

[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

已分类的属性。

哦…

我花了一些时间才弄清楚,将依赖属性的默认值转换为其值类型是完全多余的。

摘要

使用 WPF 创建 Eye 自定义控件并不容易,但可能性似乎是无限的,而且性能很好。

我面临的大多数困难源于层之间的分离,即用法(XAML)、代码隐藏(C#)和样式(XAML)都必须结合起来才能形成控件。

历史

这是第一个版本。

© . All rights reserved.