WPF开发之实现一种三轴机械手控件

.NET
267
0
0
2023-07-28
标签   WPF
目录
  • 一 引入
  • 二 RobotControl
  • 三 Style
  • 四 效果演示

一 引入

考虑实现一种三轴机器人控件。

三轴机器人用来将某种工件从一个位置运送到另一个位置。

其X轴为手臂轴,可以正向和反向运动,它处于末端,直接接触工件;

其T轴为旋转轴,可以对手臂进行旋转;

其Z轴为升降轴,可以对手臂和旋转部分进行升降。

二 RobotControl

定义出机器人的轴动作枚举,轴的动作分为回原点,正向运动,反向运动。

public enum WaferRobotZAction
{
    Z_Origin,
    Z_CW,
    Z_CCW
}

public enum WaferRobotXAction
{
    X_Origin,
    X_CW,
    X_CCW
}

public enum WaferRobotTAction
{
    T_Origin,
    T_CW,
    T_CCW
}

声明一个WaferRobotControl的自定义控件,它继承自Control类。

定义一个Wafer属性来表示WaferRobot上的工件。

定义表示X轴动作、T轴动作和Z轴动作的依赖属性,它可以被实际的业务数据源绑定。

当实际的业务数据发生改变时,轴动作属性相应改变,并VisualStateManager来转换控件的状态,以触发样式模板中的动画。

public class WaferRobotControl : Control
{
    static WaferRobotControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(WaferRobotControl), new FrameworkPropertyMetadata(typeof(WaferRobotControl)));
    }

    public static readonly DependencyProperty WaferProperty = DependencyProperty.Register("Wafer", typeof(int), typeof(WaferRobotControl));
    public int Wafer { get => (int)GetValue(WaferProperty); set => SetValue(WaferProperty, value); }

    public static readonly DependencyProperty RobotZActionProperty = DependencyProperty.Register(
       "RobotZAction",
       typeof(WaferRobotZAction),
       typeof(WaferRobotControl),
       new PropertyMetadata(WaferRobotZAction.Z_Origin, RobotZActionPropertyChangedCallback));

    public WaferRobotZAction RobotZAction
    {
        get => (WaferRobotZAction)GetValue(RobotZActionProperty);
        set => SetValue(RobotZActionProperty, value);
    }

    private static void RobotZActionPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as WaferRobotControl;
        var oldAct = (WaferRobotZAction)e.OldValue;
        var newAct = (WaferRobotZAction)e.NewValue;
        switch (newAct)
        {
            case WaferRobotZAction.Z_Origin:
                VisualStateManager.GoToState(control, newAct.ToString(), true);
                break;
            case WaferRobotZAction.Z_CW:
                if (newAct != oldAct)
                {
                    VisualStateManager.GoToState(control, newAct.ToString(), true);
                }
                break;
            case WaferRobotZAction.Z_CCW:
                if (newAct != oldAct)
                {
                    VisualStateManager.GoToState(control, newAct.ToString(), true);
                }
                break;
            default:
                break;
        }
    }

    public static readonly DependencyProperty RobotXActionProperty = DependencyProperty.Register(
        "RobotXAction",
        typeof(WaferRobotXAction),
        typeof(WaferRobotControl),
        new PropertyMetadata(WaferRobotXAction.X_Origin, RobotXActionPropertyChangedCallback));
    public WaferRobotXAction RobotXAction
    {
        get => (WaferRobotXAction)GetValue(RobotXActionProperty);
        set => SetValue(RobotXActionProperty, value);
    }

    private static void RobotXActionPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as WaferRobotControl;
        var oldAct = (WaferRobotXAction)e.OldValue;
        var newAct = (WaferRobotXAction)e.NewValue;
        switch (newAct)
        {
            case WaferRobotXAction.X_Origin:
                VisualStateManager.GoToState(control, newAct.ToString(), true);
                break;
            case WaferRobotXAction.X_CW:
                if (newAct != oldAct)
                {
                    VisualStateManager.GoToState(control, newAct.ToString(), true);
                }
                break;
            case WaferRobotXAction.X_CCW:
                if (newAct != oldAct)
                {
                    VisualStateManager.GoToState(control, newAct.ToString(), true);
                }
                break;
            default:
                break;
        }
    }

    public static readonly DependencyProperty RobotTActionProperty = DependencyProperty.Register(
        "RobotTAction",
        typeof(WaferRobotTAction),
        typeof(WaferRobotControl),
        new PropertyMetadata(WaferRobotTAction.T_Origin, RobotTActionPropertyChangedCallback));

    public WaferRobotTAction RobotTAction
    {
        get => (WaferRobotTAction)GetValue(RobotTActionProperty);
        set => SetValue(RobotTActionProperty, value);
    }

    private static void RobotTActionPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var control = d as WaferRobotControl;
        var oldAct = (WaferRobotTAction)e.OldValue;
        var newAct = (WaferRobotTAction)e.NewValue;
        switch (newAct)
        {
            case WaferRobotTAction.T_Origin:
                VisualStateManager.GoToState(control, newAct.ToString(), true);
                break;
            case WaferRobotTAction.T_CW:
                if (newAct != oldAct)
                {
                    VisualStateManager.GoToState(control, newAct.ToString(), true);
                }
                break;
            case WaferRobotTAction.T_CCW:
                if (newAct != oldAct)
                {
                    VisualStateManager.GoToState(control, newAct.ToString(), true);
                }
                break;
            default:
                break;
        }
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        VisualStateManager.GoToState(this, WaferRobotZAction.Z_Origin.ToString(), true);
        VisualStateManager.GoToState(this, WaferRobotXAction.X_Origin.ToString(), true);
        VisualStateManager.GoToState(this, WaferRobotTAction.T_Origin.ToString(), true);
    }
}

三 Style

控件模板的实现思路。

将机器人的样式分为三部分,不动的底座部分,Z轴部分,包含T轴和X轴的手臂部分。

VisualStateGroup中定义出轴动作的VisualState,编写转换动画。

<SolidColorBrush x:Key="robotBorderBrush" Color="#" />
<Style TargetType="{x:Type local:WaferRobotControl}" >
    <Setter Property="Cursor" Value="Hand" />
    <Setter Property="Width" Value=""/>
    <Setter Property="Height" Value=""/>
    <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:WaferRobotControl}">
                    <Viewbox x:Name="viewbox" Stretch="Fill">
                        <VisualStateManager.VisualStateGroups>
                            <VisualStateGroup Name="RobotActions">
                                <VisualStateGroup.Transitions>
                                    <VisualTransition To="Z_CW">
                                        <Storyboard FillBehavior="HoldEnd">
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotZAct" Storyboard.TargetProperty="Y">
                                                <LinearDoubleKeyFrame Value="-" KeyTime="0:0:1"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotUpDownAct" Storyboard.TargetProperty="Y">
                                                <LinearDoubleKeyFrame Value="-" KeyTime="0:0:1"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualTransition>
                                    <VisualTransition To="Z_CCW">
                                        <Storyboard FillBehavior="HoldEnd">
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotZAct" Storyboard.TargetProperty="Y">
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:1"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotUpDownAct" Storyboard.TargetProperty="Y">
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:1"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualTransition>
                                </VisualStateGroup.Transitions>
                                <VisualState Name="Z_Origin">
                                    <Storyboard FillBehavior="HoldEnd">
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotZAct" Storyboard.TargetProperty="Y" >
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotUpDownAct" Storyboard.TargetProperty="Y" >
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState Name="Z_CW">
                                    <Storyboard  FillBehavior="HoldEnd">
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotZAct" Storyboard.TargetProperty="Y" Duration="" >
                                            <LinearDoubleKeyFrame Value="-" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotUpDownAct" Storyboard.TargetProperty="Y" >
                                            <LinearDoubleKeyFrame Value="-" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState Name="Z_CCW">
                                    <Storyboard FillBehavior="HoldEnd">
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotZAct" Storyboard.TargetProperty="Y" >
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotUpDownAct" Storyboard.TargetProperty="Y" >
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>

                            <VisualStateGroup Name="RobotXActions">
                                <VisualStateGroup.Transitions>
                                    <VisualTransition To="X_CW">
                                        <Storyboard FillBehavior="HoldEnd" SpeedRatio="">
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTRotateAct" Storyboard.TargetProperty="Angle">
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:9"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTArmRotateAct" Storyboard.TargetProperty="Angle">
                                                <LinearDoubleKeyFrame Value="-" KeyTime="0:0:0"/>
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:9"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTAct" Storyboard.TargetProperty="X">
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:9"/>
                                                <LinearDoubleKeyFrame Value=".126" KeyTime="0:0:8"/>
                                                <LinearDoubleKeyFrame Value=".443" KeyTime="0:0:7"/>
                                                <LinearDoubleKeyFrame Value=".756" KeyTime="0:0:6"/>
                                                <LinearDoubleKeyFrame Value=".753" KeyTime="0:0:5"/>
                                                <LinearDoubleKeyFrame Value=".009" KeyTime="0:0:4"/>
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:3"/>
                                                <LinearDoubleKeyFrame Value=".117" KeyTime="0:0:2"/>
                                                <LinearDoubleKeyFrame Value=".689" KeyTime="0:0:1"/>
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualTransition>
                                    <VisualTransition To="X_CCW">
                                        <Storyboard FillBehavior="HoldEnd" SpeedRatio="">
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTRotateAct" Storyboard.TargetProperty="Angle">
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:9"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTArmRotateAct" Storyboard.TargetProperty="Angle">
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                                <LinearDoubleKeyFrame Value="-" KeyTime="0:0:9"/>
                                            </DoubleAnimationUsingKeyFrames>
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTAct" Storyboard.TargetProperty="X">
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                                <LinearDoubleKeyFrame Value=".126" KeyTime="0:0:1"/>
                                                <LinearDoubleKeyFrame Value=".443" KeyTime="0:0:2"/>
                                                <LinearDoubleKeyFrame Value=".756" KeyTime="0:0:3"/>
                                                <LinearDoubleKeyFrame Value=".753" KeyTime="0:0:4"/>
                                                <LinearDoubleKeyFrame Value=".009" KeyTime="0:0:5"/>
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:6"/>
                                                <LinearDoubleKeyFrame Value=".117" KeyTime="0:0:7"/>
                                                <LinearDoubleKeyFrame Value=".689" KeyTime="0:0:8"/>
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:9"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualTransition>
                                </VisualStateGroup.Transitions>

                                <VisualState Name="X_Origin">
                                    <Storyboard FillBehavior="HoldEnd">
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTRotateAct" Storyboard.TargetProperty="Angle">
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTArmRotateAct" Storyboard.TargetProperty="Angle">
                                            <LinearDoubleKeyFrame Value="-" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTAct" Storyboard.TargetProperty="X">
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>

                                <VisualState Name="X_CW">
                                    <Storyboard FillBehavior="HoldEnd">
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTRotateAct" Storyboard.TargetProperty="Angle">
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTArmRotateAct" Storyboard.TargetProperty="Angle">
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTAct" Storyboard.TargetProperty="X">
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState Name="X_CCW">
                                    <Storyboard FillBehavior="HoldEnd">
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTRotateAct" Storyboard.TargetProperty="Angle">
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTArmRotateAct" Storyboard.TargetProperty="Angle">
                                            <LinearDoubleKeyFrame Value="-" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXTAct" Storyboard.TargetProperty="X">
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>

                            <VisualStateGroup Name="RobotTActions">
                                <VisualStateGroup.Transitions>
                                    <VisualTransition To="T_Origin">
                                        <Storyboard FillBehavior="HoldEnd">
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotRotateAct" Storyboard.TargetProperty="Angle">
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:1"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualTransition>
                                    <VisualTransition To="T_CW">
                                        <Storyboard FillBehavior="HoldEnd">
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotRotateAct" Storyboard.TargetProperty="Angle">
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:1"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualTransition>
                                    <VisualTransition To="T_CCW">
                                        <Storyboard FillBehavior="HoldEnd">
                                            <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotRotateAct" Storyboard.TargetProperty="Angle">
                                                <LinearDoubleKeyFrame Value="" KeyTime="0:0:1"/>
                                            </DoubleAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualTransition>
                                </VisualStateGroup.Transitions>

                                <VisualState Name="T_Origin">
                                    <Storyboard FillBehavior="HoldEnd">
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotRotateAct" Storyboard.TargetProperty="Angle">
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState Name="T_CCW">
                                    <Storyboard FillBehavior="HoldEnd">
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotRotateAct" Storyboard.TargetProperty="Angle">
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                                <VisualState Name="T_CW">
                                    <Storyboard FillBehavior="HoldEnd">
                                        <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotRotateAct" Storyboard.TargetProperty="Angle">
                                            <LinearDoubleKeyFrame Value="" KeyTime="0:0:0"/>
                                        </DoubleAnimationUsingKeyFrames>
                                    </Storyboard>
                                </VisualState>
                            </VisualStateGroup>
                        </VisualStateManager.VisualStateGroups>
                        <Canvas Width="" Height="300" >
                            <Canvas x:Name="robotZ" Width="" Height="120" Canvas.Top="170" Canvas.Left="80" >
                                <Canvas.RenderTransform>
                                    <TransformGroup>
                                        <TranslateTransform x:Name="robotZAct"></TranslateTransform>
                                    </TransformGroup>
                                </Canvas.RenderTransform>
                                <Path Stroke="#" StrokeStartLineCap="Round" StrokeEndLineCap="Round">
                                    <Path.Fill>
                                        <LinearGradientBrush  EndPoint=",0.5" StartPoint="0,0.5">
                                            <GradientStop Color="#B696A" Offset="0" />
                                            <GradientStop Color="#B696A" Offset="1" />
                                            <GradientStop Color="#AA7BE" Offset="0.5" />
                                        </LinearGradientBrush>
                                    </Path.Fill>
                                    <Path.Data>
                                        <PathGeometry>
                                            <PathFigure StartPoint=" 20" IsClosed="True">
                                                <LineSegment Point=" 2"/>
                                                <LineSegment Point=" 2"/>
                                                <LineSegment Point=" 5"/>
                                                <LineSegment Point=" 5" />
                                                <LineSegment Point=" 2"/>
                                                <LineSegment Point=" 2"/>
                                                <LineSegment Point=" 20" />
                                                <LineSegment Point=" 20" />
                                            </PathFigure>

                                            <PathFigure StartPoint=" 20" >
                                                <LineSegment Point=" 24"/>
                                                <LineSegment Point=" 24" />
                                                <LineSegment Point=" 20"/>
                                            </PathFigure>

                                            <PathFigure StartPoint=" 24" >
                                                <LineSegment Point=" 24"/>
                                                <LineSegment Point=" 28"/>
                                                <LineSegment Point=" 28"/>
                                                <LineSegment Point=" 28" />
                                                <LineSegment Point=" 28" />
                                                <LineSegment Point=" 24"/>
                                                <LineSegment Point=" 24"/>
                                            </PathFigure>
                                        </PathGeometry>
                                    </Path.Data>
                                </Path>
                                <Path Stroke="#"  StrokeStartLineCap="Round" StrokeEndLineCap="Round">
                                    <Path.Fill>
                                        <LinearGradientBrush  EndPoint=",0.5" StartPoint="0,0.5">
                                            <GradientStop Color="#B696A" Offset="0" />
                                            <GradientStop Color="#B696A" Offset="1" />
                                            <GradientStop Color="#AA7BE" Offset="0.5" />
                                        </LinearGradientBrush>
                                    </Path.Fill>
                                    <Path.Data>
                                        <PathGeometry>
                                            <PathFigure StartPoint=" 28.5" >
                                                <LineSegment Point=" 120"/>
                                                <LineSegment Point=" 120"/>
                                                <LineSegment Point=" 28.5"/>
                                            </PathFigure>
                                        </PathGeometry>
                                    </Path.Data>
                                </Path>
                            </Canvas>

                            <Canvas x:Name="dizuo" Width="" Height="100" Canvas.Top="200" Canvas.Left="60">
                                <Path  Stroke="#" Fill="#A1A7BE" >
                                    <Path.Data>
                                        <PathGeometry>
                                            <PathFigure StartPoint=" 0" IsClosed="True">
                                                <LineSegment Point=" 0"/>
                                                <LineSegment Point=" 92"/>
                                                <LineSegment Point=" 92"/>
                                            </PathFigure>
                                            <PathFigure StartPoint=" 92" IsClosed="True">
                                                <LineSegment Point=" 92"/>
                                                <LineSegment Point=" 100"/>
                                                <LineSegment Point=" 100"/>
                                            </PathFigure>
                                        </PathGeometry>
                                    </Path.Data>
                                </Path>
                                <Path  Stroke="#" Fill="#7A7E90"  Canvas.Left="20">
                                    <Path.Data>
                                        <PathGeometry>
                                            <PathFigure StartPoint=" 0" IsClosed="True">
                                                <LineSegment Point=" 0"/>
                                                <LineSegment Point=" 92"/>
                                                <LineSegment Point=" 92"/>
                                            </PathFigure>
                                            <PathFigure StartPoint=" 92" IsClosed="True">
                                                <LineSegment Point="- 92"/>
                                                <LineSegment Point="- 100"/>
                                                <LineSegment Point=" 100"/>
                                                <LineSegment Point=" 92"/>
                                            </PathFigure>
                                        </PathGeometry>
                                    </Path.Data>
                                </Path>
                                <Path  Stroke="#" Fill="#585368" Canvas.Left="60">
                                    <Path.Data>
                                        <PathGeometry>
                                            <PathFigure StartPoint=" 0" IsClosed="True">
                                                <LineSegment Point=" 0"/>
                                                <LineSegment Point=" 92"/>
                                                <LineSegment Point=" 92"/>
                                            </PathFigure>
                                            <PathFigure StartPoint=" 92" IsClosed="True">
                                                <LineSegment Point=" 92"/>
                                                <LineSegment Point=" 100"/>
                                                <LineSegment Point=" 100"/>
                                            </PathFigure>
                                        </PathGeometry>
                                    </Path.Data>
                                </Path>
                            </Canvas>

                            <Canvas x:Name="robot" Width="" Height="150" RenderTransformOrigin="1 1" >
                                <Canvas.RenderTransform>
                                    <TransformGroup>
                                        <RotateTransform  x:Name="robotRotateAct"/>
                                        <TranslateTransform  x:Name="robotUpDownAct"></TranslateTransform>
                                    </TransformGroup>
                                </Canvas.RenderTransform>
                                <Canvas x:Name="armXT" Width="200" Height="100"  Canvas.Top="100" RenderTransformOrigin="0.5 0.5">
                                    <Canvas.RenderTransform>
                                        <RotateTransform  x:Name="armXTRotateAct"/>
                                    </Canvas.RenderTransform>

                                    <Canvas x:Name="armXTArm" Width="70" Height="30"  Canvas.Left="30" Canvas.Top="35" RenderTransformOrigin="1 0.5">
                                        <Path  Stroke="{StaticResource robotBorderBrush}" Fill="#FFF50" StrokeThickness="1" StrokeEndLineCap="Round" >
                                            <Path.Data>
                                                <PathGeometry>
                                                    <PathFigure StartPoint=" 5" IsClosed="True">
                                                        <LineSegment Point=" 0"/>
                                                        <LineSegment Point=" 30" IsStroked="False"/>
                                                        <LineSegment Point=" 25"/>
                                                        <LineSegment Point=" 5" IsStroked="False"/>
                                                    </PathFigure>
                                                </PathGeometry>
                                            </Path.Data>
                                        </Path>
                                        <Path Stroke="{StaticResource robotBorderBrush}" StrokeThickness="" Canvas.Left="0" 
                                                StrokeEndLineCap="Round" StrokeStartLineCap="Round" Fill="#FFF50"
                                                Data="M,5 A 10,10 0 0 0 0,25">
                                        </Path>
                                    </Canvas>

                                    <Canvas x:Name="armXTCenter"  Width="40" Height="40" Canvas.Left="80" Canvas.Top="30" >
                                        <Path  Stroke="{StaticResource robotBorderBrush}"  Fill="#FFF50" StrokeThickness="1" StrokeEndLineCap="Round"  >
                                            <Path.Data>
                                                <PathGeometry>
                                                    <PathFigure StartPoint=" 6" IsClosed="True">
                                                        <LineSegment Point=" 0"/>
                                                        <LineSegment Point=" 0"/>
                                                        <LineSegment Point=" 6"/>
                                                        <LineSegment Point=" 34"/>
                                                        <LineSegment Point=" 40"/>
                                                        <LineSegment Point=" 40"/>
                                                        <LineSegment Point=" 34"/>
                                                    </PathFigure>
                                                </PathGeometry>
                                            </Path.Data>
                                        </Path>
                                    </Canvas>
                                </Canvas>

                                <Canvas x:Name="armXT" Width="120" Height="40" Canvas.Left="-90" Canvas.Top="130">
                                    <Canvas.RenderTransform>
                                        <TransformGroup>
                                            <TranslateTransform x:Name="armXTAct"></TranslateTransform>
                                        </TransformGroup>
                                    </Canvas.RenderTransform>
                                    <Canvas x:Name="armXTArm" Width="70" Height="20"  Canvas.Left="50" Canvas.Top="10" RenderTransformOrigin="0 0.5" Background="#6495ED">
                                        <Canvas.RenderTransform>
                                            <RotateTransform x:Name="armXTArmRotateAct"/>
                                        </Canvas.RenderTransform>
                                        <Path Stroke="{StaticResource robotBorderBrush}" StrokeThickness="" Canvas.Left="70" 
                                        StrokeEndLineCap="Round" StrokeStartLineCap="Round" Fill="#ED"
                                        Data="M,0 A 10,10 0 0 1 0,20">
                                        </Path>
                                        <Path Stroke="{StaticResource robotBorderBrush}" StrokeThickness="" Canvas.Left="0" 
                                        StrokeEndLineCap="Round" StrokeStartLineCap="Round" Fill="#ED"
                                        Data="M,0 A 10,10 0 0 0 0,20">
                                        </Path>

                                        <Path  Stroke="{StaticResource robotBorderBrush}" Fill="#ED" StrokeThickness="1" StrokeEndLineCap="Round" >
                                            <Path.Data>
                                                <PathGeometry>
                                                    <PathFigure StartPoint=" 0" >
                                                        <LineSegment Point=" 0" />
                                                        <LineSegment Point=" 20" IsStroked="False"/>
                                                        <LineSegment Point=" 20"/>
                                                        <LineSegment Point=" 0" IsStroked="False"/>
                                                    </PathFigure>
                                                </PathGeometry>
                                            </Path.Data>
                                        </Path>
                                        <Ellipse Width="" Height="12" Stroke="#030303" StrokeThickness="2"  Fill="Transparent" 
                                             Canvas.Top="" Canvas.Left="62"/>
                                    </Canvas>

                                    <Canvas x:Name="armGripper" Height="" Width="50"  Canvas.Left="0" Canvas.Top="0">
                                        <Path  Stroke="{StaticResource robotBorderBrush}"  StrokeThickness="" StrokeEndLineCap="Round" >
                                            <Path.Data>
                                                <PathGeometry>
                                                    <PathFigure StartPoint=" 14" >
                                                        <LineSegment Point=" 14" />
                                                        <LineSegment Point=" 8" />
                                                        <LineSegment Point="- 8" />
                                                    </PathFigure>

                                                    <PathFigure StartPoint=" 26" >
                                                        <LineSegment Point=" 26" />
                                                        <LineSegment Point=" 32" />
                                                        <LineSegment Point="- 32" />
                                                    </PathFigure>
                                                </PathGeometry>
                                            </Path.Data>
                                        </Path>
                                        <Path  Stroke="{StaticResource robotBorderBrush}" Fill="#A7E90"  StrokeThickness="1" StrokeEndLineCap="Round" >
                                            <Path.Data>
                                                <PathGeometry>
                                                    <PathFigure StartPoint=" 0" >
                                                        <LineSegment Point=" 0" />
                                                        <LineSegment Point=" 40" />
                                                        <LineSegment Point=" 40" />
                                                        <LineSegment Point=" 30" />
                                                        <LineSegment Point=" 10" />
                                                        <LineSegment Point=" 0" />
                                                    </PathFigure>
                                                </PathGeometry>
                                            </Path.Data>
                                        </Path>
                                        <Path  Stroke="{StaticResource robotBorderBrush}" Fill="#A7E90" StrokeThickness="1" StrokeEndLineCap="Round" >
                                            <Path.Data>
                                                <PathGeometry>
                                                    <PathFigure StartPoint=" 10" >
                                                        <LineSegment Point=" 10" />
                                                        <LineSegment Point=" 30" />
                                                        <LineSegment Point=" 30" />
                                                        <LineSegment Point=" 10" IsStroked="False"/>
                                                    </PathFigure>
                                                </PathGeometry>
                                            </Path.Data>
                                        </Path>

                                        <Ellipse Width="" Height="12" Stroke="#030303" StrokeThickness="2"  Fill="Transparent" 
                                             Canvas.Top="" Canvas.Left="44"/>
                                        <Ellipse x:Name="wafer" Width="" Height="40" StrokeThickness="1" Stroke="Black"  Canvas.Left="-24"
                                                 Visibility="{Binding Wafer,Converter={StaticResource WaferIntToVisibilityConverter},
                                                    RelativeSource={RelativeSource TemplatedParent}}"
                                                 Fill="{Binding Wafer,Converter={StaticResource WaferIntToColorConverter},
                                                     RelativeSource={RelativeSource TemplatedParent}}"/>
                                    </Canvas>
                                </Canvas>
                            </Canvas>
                        </Canvas>
                    </Viewbox>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
</Style>

四 效果演示

界面代码如下:

<Window x:Class="WpfApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx//xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx//xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/" 
        xmlns:wpfapp="clr-namespace:WpfApp1"
        mc:Ignorable="d" Background="WhiteSmoke"
        Title="MainWindow" Height="" Width="1200">
    <Canvas>
        <wpfapp:WaferRobotControl Canvas.Left="472" Canvas.Top="171" x:Name="robot"/>
        <Button Content="Z CW" Canvas.Left="" Canvas.Top="338" Width="60" Height="30" Click="ZCWButton_Click"   />
        <Button Content="Z CCW" Canvas.Left="" Canvas.Top="388" Width="60" Height="30" Click="ZCCWButton_Click"/>
        <Button Content="X CW" Canvas.Left="" Canvas.Top="338" Width="60" Height="30" Click="XCWButton_Click"/>
        <Button Content="X CCW" Canvas.Left="" Canvas.Top="389" Width="60" Height="30" Click="XCCWButton_Click"/>
        <Button Content="T CW" Canvas.Left="" Canvas.Top="338" Width="60" Height="30" Click="TCWButton_Click"/>
        <Button Content="T CCW" Canvas.Left="" Canvas.Top="389" Width="60" Height="30" Click="TCCWButton_Click"/>
        <Button Content="Auto" Canvas.Left="" Canvas.Top="439" Width="60" Height="30" Click="AutoButton_Click"/>
    </Canvas>
</Window>

后台代码如下:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }

    private void ZCWButton_Click(object sender, RoutedEventArgs e)
    {
        robot.RobotZAction = WaferRobotZAction.Z_CW;
    }

    private void ZCCWButton_Click(object sender, RoutedEventArgs e)
    {
        robot.RobotZAction = WaferRobotZAction.Z_CCW;
    }

    private void XCWButton_Click(object sender, RoutedEventArgs e)
    {
        robot.RobotXAction = WaferRobotXAction.X_CW;
    }

    private void XCCWButton_Click(object sender, RoutedEventArgs e)
    {
        robot.RobotXAction = WaferRobotXAction.X_CCW;
    }

    private void TCWButton_Click(object sender, RoutedEventArgs e)
    {
        robot.RobotTAction = WaferRobotTAction.T_CW;
    }

    private void TCCWButton_Click(object sender, RoutedEventArgs e)
    {
        robot.RobotTAction = WaferRobotTAction.T_CCW;
    }

    private void AutoButton_Click(object sender, RoutedEventArgs e)
    {
        Task.Run(async () =>
        {
            Application.Current.Dispatcher?.Invoke
            (
               () => { this.robot.RobotTAction = WaferRobotTAction.T_CCW; }
            );
            await Task.Delay();

            Application.Current.Dispatcher?.Invoke
            (
               () => { this.robot.RobotXAction = WaferRobotXAction.X_CW; }
            );
            await Task.Delay();

            Application.Current.Dispatcher?.Invoke(
                () => { this.robot.Wafer =; }
                );
            await Task.Delay();

            Application.Current.Dispatcher?.Invoke(
               () => { this.robot.RobotXAction = WaferRobotXAction.X_CCW; }
               );
            await Task.Delay();

            Application.Current.Dispatcher?.Invoke(
             () => { this.robot.RobotZAction = WaferRobotZAction.Z_CW; }
             );
            await Task.Delay();

            Application.Current.Dispatcher?.Invoke
            (
               () => { this.robot.RobotTAction = WaferRobotTAction.T_CW; }
            );
            await Task.Delay();

            Application.Current.Dispatcher?.Invoke
            (
             () => { this.robot.RobotXAction = WaferRobotXAction.X_CW; }
            );
            await Task.Delay();

            Application.Current.Dispatcher?.Invoke(
                () => { this.robot.Wafer =; }
                );
            await Task.Delay();

            Application.Current.Dispatcher?.Invoke(
             () => { this.robot.RobotXAction = WaferRobotXAction.X_CCW; }
             );
            await Task.Delay();
            Application.Current.Dispatcher?.Invoke
            (
              () => { this.robot.RobotTAction = WaferRobotTAction.T_Origin; }
            );
            await Task.Delay();
            Application.Current.Dispatcher?.Invoke
            (
              () => { this.robot.RobotZAction = WaferRobotZAction.Z_CCW; }
            );
            await Task.Delay();
        });
    }
}