WPF开发经验-实现一种三轴机械手控件
一 引入
考虑实现一种三轴机器人控件。
三轴机器人用来将某种工件从一个位置运送到另一个位置。
其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开发经验-实现一种三轴机械手控件的更多相关文章
- Jquery如何序列化form表单数据为JSON对象 C# ADO.NET中设置Like模糊查询的参数 从客户端出现小于等于公式符号引发检测到有潜在危险的Request.Form 值 jquery调用iframe里面的方法 Js根据Ip地址自动判断是哪个城市 【我们一起写框架】MVVM的WPF框架(三)—数据控件 设计模式之简单工厂模式(C#语言描述)
jquery提供的serialize方法能够实现. $("#searchForm").serialize();但是,观察输出的信息,发现serialize()方法做的是将表单中的数 ...
- WPF自定义控件(三)の扩展控件
扩展控件,顾名思义就是对已有的控件进行扩展,一般继承于已有的原生控件,不排除继承于自定义的控件,不过这样做意义不大,因为既然都自定义了,为什么不一步到位呢,有些不同的需求也可以通过此来完成,不过类似于 ...
- WPF编程,通过Double Animation动态旋转控件的一种方法。
原文:WPF编程,通过Double Animation动态旋转控件的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/art ...
- WPF编程,通过Double Animation动态更改控件属性的一种方法。
原文:WPF编程,通过Double Animation动态更改控件属性的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/a ...
- WPF编程,通过Double Animation动态缩放控件的一种方法。
原文:WPF编程,通过Double Animation动态缩放控件的一种方法. 版权声明:我不生产代码,我只是代码的搬运工. https://blog.csdn.net/qq_43307934/art ...
- WPF 自定义ComboBox样式,自定义多选控件
原文:WPF 自定义ComboBox样式,自定义多选控件 一.ComboBox基本样式 ComboBox有两种状态,可编辑和不可编辑状态.通过设置IsEditable属性可以切换控件状态. 先看基本样 ...
- Windows Presentation Foundation(WPF)中的数据绑定(使用XmlDataProvider作控件绑定)
原文:Windows Presentation Foundation(WPF)中的数据绑定(使用XmlDataProvider作控件绑定) ------------------------------ ...
- WPF教程十一:简单了解并使用控件模板
WPF教程十一:简单了解并使用控件模板 这一章梳理控件模板,每个WPF控件都设计成无外观的,但是行为设计上是不允许改变的,比如使用Button的控件时,按钮提供了能被点击的内容,那么自由的改变控件外观 ...
- ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇
原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 前篇 第三章 为控件添加事件 好了,我们之前以前开发一个控件.而且也添加了属性,开发也很规范,但是那个控件还差最后一点:添加事件. 系列 ...
- ASP.NET自定义控件组件开发 第三章 为控件添加事件 后篇
原文:ASP.NET自定义控件组件开发 第三章 为控件添加事件 后篇 第三章 为控件添加事件 后篇 前一篇文章只是简单的说了下事件,但是大家应该方法,在ASP.NET自定义控件中只是简单那么定义事件是 ...
随机推荐
- Blog2:nchu-software-oop-2022-4+5+期中
Blog2:nchu-software-oop-2022-4+5+期中 一.前言 两次大作业是关于四边形和五边形的相关操作,类似于之前的三角形,但由于图形边数的变化,难度大大增加.对数学知识的运用考察 ...
- Istio(五):使用服务网格Istio进行流量路由
目录 一.模块概览 二.系统环境 三.简单路由 3.1 简单路由 四.Subset和DestinationRule 4.1 Subset 和 DestinationRule 4.2 Destinati ...
- Kubeadm部署k8s单点master
Kubeadm部署k8s单点master 1.环境准备: 主机名 IP 说明 宿主机系统 master 10.0.0.17 Kubernetes集群的master节点 CentOS 7.9 node1 ...
- Java开发学习(四十一)----MyBatisPlus标准数据层(增删查改分页)开发
一.标准CRUD使用 对于标准的CRUD功能都有哪些以及MyBatisPlus都提供了哪些方法可以使用呢? 我们先来看张图: 1.1 环境准备 这里用的环境就是Java开发学习(四十)----MyBa ...
- Crypto - Caesar I
原题链接:http://www.wechall.net/challenge/training/crypto/caesar/index.php 告诉我们这是个古凯撒密码,让我们解...我们百度下古凯撒密 ...
- Cacheable VS Non-Cacheable
1 基本概念 在嵌入式软件开发中,经常会碰到说某块内存是cache的,还是non-cache的,它们究竟是什么意思?分别用在什么场景?non-cache和cache的内存区域怎么配置?这篇博文将会围绕 ...
- c#winfrom通讯录管理系统
一个简单的通讯录管理系统,适合毕业设计. 主要实现以下功能 1.系统登录 2.增加联系人 3.修改和删除联系人 4.查找联系人 5.系统用户管理 首先先搭建数据库. 我这边使用的版本是sqlserve ...
- 3 c++编程-提高篇-模版
重新系统学习c++语言,并将学习过程中的知识在这里抄录.总结.沉淀.同时希望对刷到的朋友有所帮助,一起加油哦! 生命就像一朵花,要拼尽全力绽放!死磕自个儿,身心愉悦! 系列文章列表: 1 c+ ...
- 更改HTML请求方式的几种方法
以ctfhub中的请求方式题目为例,则可以有: 法一:通过burpsuite抓包修改 在burpsuite中抓包后发送到repeater模块中,对请求方式进行修改即可 法二:通过curl命令进行 cu ...
- MYSQL快速安装整理
参考教程:https://www.cnblogs.com/brad93/p/16650780.html [检查是否已安装过] find / -name mysql [快速安装开始] groupadd ...