一 引入
考慮實現一種三軸機器人控制元件。
三軸機器人用來將某種工件從一個位置運送到另一個位置。
其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="#030303" /> <Style TargetType="{x:Type local:WaferRobotControl}" > <Setter Property="Cursor" Value="Hand" /> <Setter Property="Width" Value="200"/> <Setter Property="Height" Value="300"/> <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="-90" KeyTime="0:0:1"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotUpDownAct" Storyboard.TargetProperty="Y"> <LinearDoubleKeyFrame Value="-90" KeyTime="0:0:1"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualTransition> <VisualTransition To="Z_CCW"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotZAct" Storyboard.TargetProperty="Y"> <LinearDoubleKeyFrame Value="0" KeyTime="0:0:1"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotUpDownAct" Storyboard.TargetProperty="Y"> <LinearDoubleKeyFrame Value="0" 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="0" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotUpDownAct" Storyboard.TargetProperty="Y" > <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState Name="Z_CW"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotZAct" Storyboard.TargetProperty="Y" Duration="0" > <LinearDoubleKeyFrame Value="-90" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotUpDownAct" Storyboard.TargetProperty="Y" > <LinearDoubleKeyFrame Value="-90" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState Name="Z_CCW"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotZAct" Storyboard.TargetProperty="Y" > <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotUpDownAct" Storyboard.TargetProperty="Y" > <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup Name="RobotXActions"> <VisualStateGroup.Transitions> <VisualTransition To="X_CW"> <Storyboard FillBehavior="HoldEnd" SpeedRatio="6"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT1RotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="90" KeyTime="0:0:0"/> <LinearDoubleKeyFrame Value="0" KeyTime="0:0:9"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT2ArmRotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="-90" KeyTime="0:0:0"/> <LinearDoubleKeyFrame Value="0" KeyTime="0:0:9"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT2Act" Storyboard.TargetProperty="X"> <LinearDoubleKeyFrame Value="0" KeyTime="0:0:9"/> <LinearDoubleKeyFrame Value="2.126" KeyTime="0:0:8"/> <LinearDoubleKeyFrame Value="8.443" KeyTime="0:0:7"/> <LinearDoubleKeyFrame Value="18.756" KeyTime="0:0:6"/> <LinearDoubleKeyFrame Value="32.753" KeyTime="0:0:5"/> <LinearDoubleKeyFrame Value="50.009" KeyTime="0:0:4"/> <LinearDoubleKeyFrame Value="70" KeyTime="0:0:3"/> <LinearDoubleKeyFrame Value="92.117" KeyTime="0:0:2"/> <LinearDoubleKeyFrame Value="115.689" KeyTime="0:0:1"/> <LinearDoubleKeyFrame Value="140" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualTransition> <VisualTransition To="X_CCW"> <Storyboard FillBehavior="HoldEnd" SpeedRatio="6"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT1RotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/> <LinearDoubleKeyFrame Value="90" KeyTime="0:0:9"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT2ArmRotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/> <LinearDoubleKeyFrame Value="-90" KeyTime="0:0:9"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT2Act" Storyboard.TargetProperty="X"> <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/> <LinearDoubleKeyFrame Value="2.126" KeyTime="0:0:1"/> <LinearDoubleKeyFrame Value="8.443" KeyTime="0:0:2"/> <LinearDoubleKeyFrame Value="18.756" KeyTime="0:0:3"/> <LinearDoubleKeyFrame Value="32.753" KeyTime="0:0:4"/> <LinearDoubleKeyFrame Value="50.009" KeyTime="0:0:5"/> <LinearDoubleKeyFrame Value="70" KeyTime="0:0:6"/> <LinearDoubleKeyFrame Value="92.117" KeyTime="0:0:7"/> <LinearDoubleKeyFrame Value="115.689" KeyTime="0:0:8"/> <LinearDoubleKeyFrame Value="140" KeyTime="0:0:9"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualTransition> </VisualStateGroup.Transitions> <VisualState Name="X_Origin"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT1RotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="90" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT2ArmRotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="-90" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT2Act" Storyboard.TargetProperty="X"> <LinearDoubleKeyFrame Value="140" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState Name="X_CW"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT1RotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT2ArmRotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT2Act" Storyboard.TargetProperty="X"> <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState Name="X_CCW"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT1RotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="90" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT2ArmRotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="-90" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="armXT2Act" Storyboard.TargetProperty="X"> <LinearDoubleKeyFrame Value="140" 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="90" KeyTime="0:0:1"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualTransition> <VisualTransition To="T_CW"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotRotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="180" KeyTime="0:0:1"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualTransition> <VisualTransition To="T_CCW"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotRotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="0" 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="90" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState Name="T_CCW"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotRotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="0" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState Name="T_CW"> <Storyboard FillBehavior="HoldEnd"> <DoubleAnimationUsingKeyFrames Storyboard.TargetName="robotRotateAct" Storyboard.TargetProperty="Angle"> <LinearDoubleKeyFrame Value="180" KeyTime="0:0:0"/> </DoubleAnimationUsingKeyFrames> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Canvas Width="200" Height="300" > <Canvas x:Name="robotZ" Width="40" Height="120" Canvas.Top="170" Canvas.Left="80" > <Canvas.RenderTransform> <TransformGroup> <TranslateTransform x:Name="robotZAct"></TranslateTransform> </TransformGroup> </Canvas.RenderTransform> <Path Stroke="#030303" StrokeStartLineCap="Round" StrokeEndLineCap="Round"> <Path.Fill> <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5"> <GradientStop Color="#6B696A" Offset="0" /> <GradientStop Color="#6B696A" Offset="1" /> <GradientStop Color="#A1A7BE" Offset="0.5" /> </LinearGradientBrush> </Path.Fill> <Path.Data> <PathGeometry> <PathFigure StartPoint="0 20" IsClosed="True"> <LineSegment Point="0 2"/> <LineSegment Point="2 2"/> <LineSegment Point="5 5"/> <LineSegment Point="35 5" /> <LineSegment Point="38 2"/> <LineSegment Point="40 2"/> <LineSegment Point="40 20" /> <LineSegment Point="0 20" /> </PathFigure> <PathFigure StartPoint="4 20" > <LineSegment Point="4 24"/> <LineSegment Point="36 24" /> <LineSegment Point="36 20"/> </PathFigure> <PathFigure StartPoint="4 24" > <LineSegment Point="2 24"/> <LineSegment Point="2 28"/> <LineSegment Point="4 28"/> <LineSegment Point="36 28" /> <LineSegment Point="38 28" /> <LineSegment Point="38 24"/> <LineSegment Point="36 24"/> </PathFigure> </PathGeometry> </Path.Data> </Path> <Path Stroke="#030303" StrokeStartLineCap="Round" StrokeEndLineCap="Round"> <Path.Fill> <LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5"> <GradientStop Color="#6B696A" Offset="0" /> <GradientStop Color="#6B696A" Offset="1" /> <GradientStop Color="#A1A7BE" Offset="0.5" /> </LinearGradientBrush> </Path.Fill> <Path.Data> <PathGeometry> <PathFigure StartPoint="2 28.5" > <LineSegment Point="2 120"/> <LineSegment Point="38 120"/> <LineSegment Point="38 28.5"/> </PathFigure> </PathGeometry> </Path.Data> </Path> </Canvas> <Canvas x:Name="dizuo" Width="80" Height="100" Canvas.Top="200" Canvas.Left="60"> <Path Stroke="#030303" Fill="#A1A7BE" > <Path.Data> <PathGeometry> <PathFigure StartPoint="0 0" IsClosed="True"> <LineSegment Point="20 0"/> <LineSegment Point="20 92"/> <LineSegment Point="0 92"/> </PathFigure> <PathFigure StartPoint="0 92" IsClosed="True"> <LineSegment Point="12 92"/> <LineSegment Point="12 100"/> <LineSegment Point="0 100"/> </PathFigure> </PathGeometry> </Path.Data> </Path> <Path Stroke="#030303" Fill="#7A7E90" Canvas.Left="20"> <Path.Data> <PathGeometry> <PathFigure StartPoint="0 0" IsClosed="True"> <LineSegment Point="40 0"/> <LineSegment Point="40 92"/> <LineSegment Point="0 92"/> </PathFigure> <PathFigure StartPoint="0 92" IsClosed="True"> <LineSegment Point="-8 92"/> <LineSegment Point="-8 100"/> <LineSegment Point="48 100"/> <LineSegment Point="48 92"/> </PathFigure> </PathGeometry> </Path.Data> </Path> <Path Stroke="#030303" Fill="#585368" Canvas.Left="60"> <Path.Data> <PathGeometry> <PathFigure StartPoint="0 0" IsClosed="True"> <LineSegment Point="20 0"/> <LineSegment Point="20 92"/> <LineSegment Point="0 92"/> </PathFigure> <PathFigure StartPoint="8 92" IsClosed="True"> <LineSegment Point="20 92"/> <LineSegment Point="20 100"/> <LineSegment Point="8 100"/> </PathFigure> </PathGeometry> </Path.Data> </Path> </Canvas> <Canvas x:Name="robot" Width="100" Height="150" RenderTransformOrigin="1 1" > <Canvas.RenderTransform> <TransformGroup> <RotateTransform x:Name="robotRotateAct"/> <TranslateTransform x:Name="robotUpDownAct"></TranslateTransform> </TransformGroup> </Canvas.RenderTransform> <Canvas x:Name="armXT1" Width="200" Height="100" Canvas.Top="100" RenderTransformOrigin="0.5 0.5"> <Canvas.RenderTransform> <RotateTransform x:Name="armXT1RotateAct"/> </Canvas.RenderTransform> <Canvas x:Name="armXT1Arm" Width="70" Height="30" Canvas.Left="30" Canvas.Top="35" RenderTransformOrigin="1 0.5"> <Path Stroke="{StaticResource robotBorderBrush}" Fill="#FF7F50" StrokeThickness="1" StrokeEndLineCap="Round" > <Path.Data> <PathGeometry> <PathFigure StartPoint="0 5" IsClosed="True"> <LineSegment Point="51 0"/> <LineSegment Point="51 30" IsStroked="False"/> <LineSegment Point="0 25"/> <LineSegment Point="0 5" IsStroked="False"/> </PathFigure> </PathGeometry> </Path.Data> </Path> <Path Stroke="{StaticResource robotBorderBrush}" StrokeThickness="1" Canvas.Left="0" StrokeEndLineCap="Round" StrokeStartLineCap="Round" Fill="#FF7F50" Data="M 0,5 A 10,10 0 0 0 0,25"> </Path> </Canvas> <Canvas x:Name="armXT1Center" Width="40" Height="40" Canvas.Left="80" Canvas.Top="30" > <Path Stroke="{StaticResource robotBorderBrush}" Fill="#FF7F50" StrokeThickness="1" StrokeEndLineCap="Round" > <Path.Data> <PathGeometry> <PathFigure StartPoint="0 6" IsClosed="True"> <LineSegment Point="6 0"/> <LineSegment Point="34 0"/> <LineSegment Point="40 6"/> <LineSegment Point="40 34"/> <LineSegment Point="34 40"/> <LineSegment Point="6 40"/> <LineSegment Point="0 34"/> </PathFigure> </PathGeometry> </Path.Data> </Path> </Canvas> </Canvas> <Canvas x:Name="armXT2" Width="120" Height="40" Canvas.Left="-90" Canvas.Top="130"> <Canvas.RenderTransform> <TransformGroup> <TranslateTransform x:Name="armXT2Act"></TranslateTransform> </TransformGroup> </Canvas.RenderTransform> <Canvas x:Name="armXT2Arm" Width="70" Height="20" Canvas.Left="50" Canvas.Top="10" RenderTransformOrigin="0 0.5" Background="#6495ED"> <Canvas.RenderTransform> <RotateTransform x:Name="armXT2ArmRotateAct"/> </Canvas.RenderTransform> <Path Stroke="{StaticResource robotBorderBrush}" StrokeThickness="1" Canvas.Left="70" StrokeEndLineCap="Round" StrokeStartLineCap="Round" Fill="#6495ED" Data="M 0,0 A 10,10 0 0 1 0,20"> </Path> <Path Stroke="{StaticResource robotBorderBrush}" StrokeThickness="1" Canvas.Left="0" StrokeEndLineCap="Round" StrokeStartLineCap="Round" Fill="#6495ED" Data="M 0,0 A 10,10 0 0 0 0,20"> </Path> <Path Stroke="{StaticResource robotBorderBrush}" Fill="#6495ED" StrokeThickness="1" StrokeEndLineCap="Round" > <Path.Data> <PathGeometry> <PathFigure StartPoint="70 0" > <LineSegment Point="0 0" /> <LineSegment Point="0 20" IsStroked="False"/> <LineSegment Point="70 20"/> <LineSegment Point="70 0" IsStroked="False"/> </PathFigure> </PathGeometry> </Path.Data> </Path> <Ellipse Width="12" Height="12" Stroke="#030303" StrokeThickness="2" Fill="Transparent" Canvas.Top="4" Canvas.Left="62"/> </Canvas> <Canvas x:Name="armGripper" Height="40" Width="50" Canvas.Left="0" Canvas.Top="0"> <Path Stroke="{StaticResource robotBorderBrush}" StrokeThickness="2" StrokeEndLineCap="Round" > <Path.Data> <PathGeometry> <PathFigure StartPoint="30 14" > <LineSegment Point="10 14" /> <LineSegment Point="4 8" /> <LineSegment Point="-6 8" /> </PathFigure> <PathFigure StartPoint="30 26" > <LineSegment Point="10 26" /> <LineSegment Point="4 32" /> <LineSegment Point="-6 32" /> </PathFigure> </PathGeometry> </Path.Data> </Path> <Path Stroke="{StaticResource robotBorderBrush}" Fill="#7A7E90" StrokeThickness="1" StrokeEndLineCap="Round" > <Path.Data> <PathGeometry> <PathFigure StartPoint="40 0" > <LineSegment Point="60 0" /> <LineSegment Point="60 40" /> <LineSegment Point="40 40" /> <LineSegment Point="30 30" /> <LineSegment Point="30 10" /> <LineSegment Point="40 0" /> </PathFigure> </PathGeometry> </Path.Data> </Path> <Path Stroke="{StaticResource robotBorderBrush}" Fill="#7A7E90" StrokeThickness="1" StrokeEndLineCap="Round" > <Path.Data> <PathGeometry> <PathFigure StartPoint="30 10" > <LineSegment Point="20 10" /> <LineSegment Point="20 30" /> <LineSegment Point="30 30" /> <LineSegment Point="30 10" IsStroked="False"/> </PathFigure> </PathGeometry> </Path.Data> </Path> <Ellipse Width="12" Height="12" Stroke="#030303" StrokeThickness="2" Fill="Transparent" Canvas.Top="14" Canvas.Left="44"/> <Ellipse x:Name="wafer" Width="40" 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="WpfApp1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:wpfapp1="clr-namespace:WpfApp1" mc:Ignorable="d" Background="WhiteSmoke" Title="MainWindow" Height="800" Width="1200"> <Canvas> <wpfapp1:WaferRobotControl Canvas.Left="472" Canvas.Top="171" x:Name="robot"/> <Button Content="Z CW" Canvas.Left="757" Canvas.Top="338" Width="60" Height="30" Click="ZCWButton_Click" /> <Button Content="Z CCW" Canvas.Left="757" Canvas.Top="388" Width="60" Height="30" Click="ZCCWButton_Click"/> <Button Content="X CW" Canvas.Left="838" Canvas.Top="338" Width="60" Height="30" Click="XCWButton_Click"/> <Button Content="X CCW" Canvas.Left="838" Canvas.Top="389" Width="60" Height="30" Click="XCCWButton_Click"/> <Button Content="T CW" Canvas.Left="919" Canvas.Top="338" Width="60" Height="30" Click="TCWButton_Click"/> <Button Content="T CCW" Canvas.Left="919" Canvas.Top="389" Width="60" Height="30" Click="TCCWButton_Click"/> <Button Content="Auto" Canvas.Left="757" 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(1000); Application.Current.Dispatcher?.Invoke ( () => { this.robot.RobotXAction = WaferRobotXAction.X_CW; } ); await Task.Delay(2000); Application.Current.Dispatcher?.Invoke( () => { this.robot.Wafer = 1; } ); await Task.Delay(200); Application.Current.Dispatcher?.Invoke( () => { this.robot.RobotXAction = WaferRobotXAction.X_CCW; } ); await Task.Delay(2000); Application.Current.Dispatcher?.Invoke( () => { this.robot.RobotZAction = WaferRobotZAction.Z_CW; } ); await Task.Delay(1000); Application.Current.Dispatcher?.Invoke ( () => { this.robot.RobotTAction = WaferRobotTAction.T_CW; } ); await Task.Delay(1000); Application.Current.Dispatcher?.Invoke ( () => { this.robot.RobotXAction = WaferRobotXAction.X_CW; } ); await Task.Delay(2000); Application.Current.Dispatcher?.Invoke( () => { this.robot.Wafer = 0; } ); await Task.Delay(200); Application.Current.Dispatcher?.Invoke( () => { this.robot.RobotXAction = WaferRobotXAction.X_CCW; } ); await Task.Delay(2000); Application.Current.Dispatcher?.Invoke ( () => { this.robot.RobotTAction = WaferRobotTAction.T_Origin; } ); await Task.Delay(1000); Application.Current.Dispatcher?.Invoke ( () => { this.robot.RobotZAction = WaferRobotZAction.Z_CCW; } ); await Task.Delay(1000); }); } }