一 引入

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

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

其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);
});
}
}

WPF开发经验-实现一种三轴机械手控件的更多相关文章

  1. Jquery如何序列化form表单数据为JSON对象 C# ADO.NET中设置Like模糊查询的参数 从客户端出现小于等于公式符号引发检测到有潜在危险的Request.Form 值 jquery调用iframe里面的方法 Js根据Ip地址自动判断是哪个城市 【我们一起写框架】MVVM的WPF框架(三)—数据控件 设计模式之简单工厂模式(C#语言描述)

    jquery提供的serialize方法能够实现. $("#searchForm").serialize();但是,观察输出的信息,发现serialize()方法做的是将表单中的数 ...

  2. WPF自定义控件(三)の扩展控件

    扩展控件,顾名思义就是对已有的控件进行扩展,一般继承于已有的原生控件,不排除继承于自定义的控件,不过这样做意义不大,因为既然都自定义了,为什么不一步到位呢,有些不同的需求也可以通过此来完成,不过类似于 ...

  3. WPF编程,通过Double Animation动态旋转控件的一种方法。

    原文:WPF编程,通过Double Animation动态旋转控件的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/art ...

  4. WPF编程,通过Double Animation动态更改控件属性的一种方法。

    原文:WPF编程,通过Double Animation动态更改控件属性的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/a ...

  5. WPF编程,通过Double Animation动态缩放控件的一种方法。

    原文:WPF编程,通过Double Animation动态缩放控件的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/art ...

  6. WPF 自定义ComboBox样式,自定义多选控件

    原文:WPF 自定义ComboBox样式,自定义多选控件 一.ComboBox基本样式 ComboBox有两种状态,可编辑和不可编辑状态.通过设置IsEditable属性可以切换控件状态. 先看基本样 ...

  7. Windows Presentation Foundation(WPF)中的数据绑定(使用XmlDataProvider作控件绑定)

    原文:Windows Presentation Foundation(WPF)中的数据绑定(使用XmlDataProvider作控件绑定) ------------------------------ ...

  8. WPF教程十一:简单了解并使用控件模板

    WPF教程十一:简单了解并使用控件模板 这一章梳理控件模板,每个WPF控件都设计成无外观的,但是行为设计上是不允许改变的,比如使用Button的控件时,按钮提供了能被点击的内容,那么自由的改变控件外观 ...

  9. ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇

    原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇 第三章 为控件添加事件 好了,我们之前以前开发一个控件.而且也添加了属性,开发也很规范,但是那个控件还差最后一点:添加事件. 系列 ...

  10. ASP.NET自定义控件组件开发 第三章 为控件添加事件 后篇

    原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 后篇 第三章 为控件添加事件 后篇 前一篇文章只是简单的说了下事件,但是大家应该方法,在ASP.NET自定义控件中只是简单那么定义事件是 ...

随机推荐

  1. 事件循环Event Loop

    在 事件循环 期间的某个时刻,运行时会从最先进入队列的消息开始处理队列中的消息.被处理的消息会被移出队列,并作为输入参数来调用与之关联的函数.正如前面所提到的,调用一个函数总是会为其创造一个新的栈帧. ...

  2. Win环境安装Protobuf 2.0 版本

    转载请注明出处: 安装步骤 下载 protobuf-2.5.0.zip 与 protoc-2.5.0-win32.zip 下载链接 : https://github.com/protocolbuffe ...

  3. Element基本组件

    Element按钮组件: <el-row> <el-button>默认按钮</el-button> <el-button type="primary ...

  4. 【深入浅出 Yarn 架构与实现】2-4 Yarn 基础库 - 状态机库

    当一个服务拥有太多处理逻辑时,会导致代码结构异常的混乱,很难分辨一段逻辑是在哪个阶段发挥作用的. 这时就可以引入状态机模型,帮助代码结构变得清晰. 一.状态机库概述 一)简介 状态机由一组状态组成: ...

  5. DHorse系列文章之maven打包

    插件打包 这种方式是平时最常用的,首先要下载并安装maven环境,然后在被打包的项目中引入插件,有各种各样的打包插件,比如springboot自带插件: <plugin> <grou ...

  6. Navicat mysql创建数据库、用户、授权、连接

    一.数据库的创建 调出命令窗口并创建数据库: create database itcast_oa default character set utf8;----创建数据库 二.数案件用户 create ...

  7. html网页图片加载失败的友好处理方式

    网络环境总是多样且复杂的,一张网页图片可能会因为网路状况差而加载失败或加载超长时间,也可能因为权限不足或者资源不存在而加载失败,这些都会导致用户体验变差,所以我们需要对图片加载失败时的情况进行一个弥补 ...

  8. JUC面试点汇总

    JUC面试点汇总 我们会在这里介绍我所涉及到的JUC相关的面试点内容,本篇内容持续更新 我们会介绍下述JUC的相关面试点: 线程状态 线程池 Wait和Sleep Synchronized和Lock ...

  9. ArcObjects SDK开发 011 RasterLayer

    1.RasterLayer的结构 图层的话,除了FeatureLayer外,用的最多的就是RasterLayer了.较FeatureLayer而言,RasterLayer比较简单,这点可以从栅格图层的 ...

  10. python3中的常见知识点2

    python3中的常见知识点2 列表与栈和队列 map()函数 python列表遍历的4种方式 参考链接 列表栈和队列 1.列表作为栈使用 栈:先进后出,First In Last Out 使用 ap ...