Avalonia跨平台上位机控件开发之水泵
随着国产化的推进,越来越多的开发者选择使用跨平台的框架来创建上位机应用,而Avalonia正是一个优秀的选择。本文将探讨如何利用Avalonia框架进行水泵控件的开发,并重点记录在开发的过程中所碰到的一些问题。
控件的构成
水泵控件主要在控件的内部需要创建一个旋转的动画来表示水泵当前的运行状态,在没有工作时为静止状态没有任何动画,当水泵启动时会对中间的部分进行连续的旋转。运行效果如下:
动画的创建
在控件中为了方便控制旋转动画的编写,我这边使用了一个Canvas将需要旋转的部分包裹在其中,这样在制作动画的时候直接对这个Canvas进行旋转就可以了,否则要旋转部分的Path的坐标数据为相对于最外层Canvas的坐标进行绘制,此时要找到旋转中心是一个很困难的事情。同时为这个Canvas指定了一个名称,方便后续在制作动画时的Selector处理。
<Canvas Canvas.Left="19.5" Canvas.Top="39.5" Height="34.5" Name="BumpCircle" UseLayoutRounding="False" Width="34.2"> <Path Fill="#ffffffff" Stroke="#ff6c6c6c" StrokeJoin="Miter" StrokeThickness="2.05166"> <Path.Data> <PathGeometry Figures="M 11.3573 15.4557 C 6.78011 13.3704 4.04416 9.6096 3.14943 4.1733 M 11.3573 19.7314 C 6.82241 21.3797 2.37649 20.6383 -1.98048 17.5071 M 15.4612 22.6354 C 13.3753 27.2112 9.61334 29.9464 4.17541 30.8408 M 19.7381 22.6354 c 1.64889 4.53351 0.907239 8.97809 -2.22494 13.3338 m 5.12991 -17.4365 c 4.57717 2.08531 7.31313 5.84611 8.20786 11.2824 M 22.6431 14.2571 c 4.53487 -1.64839 8.98079 -0.906966 13.3378 2.22428 M 18.5392 11.353 c 2.08594 -4.5758 5.84787 -7.31093 11.2858 -8.20539 m -15.5627 8.20539 c -1.64889 -4.53351 -0.907239 -8.97809 2.22494 -13.3338 m 4.50314 14.9864 c 2.20369 2.20302 2.20369 5.77484 0 7.97786 c -2.20369 2.20302 -5.77658 2.20302 -7.98027 0 c -2.20369 -2.20302 -2.20369 -5.77484 0 -7.97786 c 2.20369 -2.20302 5.77658 -2.20302 7.98027 0 m 9.43124 -9.4284 c 7.41241 7.41018 7.41241 19.4245 0 26.8347 c -7.41241 7.41019 -19.4304 7.41019 -26.8428 0 c -7.41241 -7.41018 -7.41241 -19.4245 0 -26.8347 c 7.41241 -7.41019 19.4304 -7.41019 26.8428 0" /> </Path.Data> </Path> </Canvas>
动画的部分定义了一个伪类running,用来表示当前水泵已经处在了运行状态。因此在进行动画编写的时候先使用一下Selector选择伪类为running
<Style Selector="controls|Pump.running">
然后需要选择到我们需要需要进行动画的部分,使用如下的Selector进行选择
<Style Selector="^ /template/ Canvas#BumpCircle">
^ : 继承上一层Selector
/template/ :进入到控件内部的Template
Canvas#BumpCircle:选择名字为BumpCircle的Canvas
使用Selector选择到需要动画的控件部分后就可以进行动画的编写,动画的完整代码如下:
<Style Selector="controls|Pump.running"> <Style Selector="^ /template/ Canvas#BumpCircle"> <Style.Animations> <Animation Duration="0:0:5" IterationCount="INFINITE"> <KeyFrame Cue="0%"> <Setter Property="RotateTransform.Angle" Value="0" /> </KeyFrame> <KeyFrame Cue="100%"> <Setter Property="RotateTransform.Angle" Value="360" /> </KeyFrame> </Animation> </Style.Animations> </Style> </Style>
以上代码创建了一个0到360度的旋转动画。
踩到的坑
创建伪类时的一个小知识点
当在avalonia中使用伪类的时候经常会碰到:pointerover等内置的一些伪类,而要使用Selector来选择这些伪类的时候通常我们会使用
<Style Selector="Button:pointerover"/>
但是当我们定义了一个自定义的伪类时,比如上面控件用到的running,此时我们在使用Selector的时候需要使用如下格式
<Style Selector="Pump.running"/>
在平时的开发中要注意以上的一些不同
在属性改变时为控件添加伪类
为了表示控件的运行状态,我在控件的C#代码中添加了一个名为Running的StyledProperty,用这个属性来区分水泵的运行状态,因此我需要在Running属性进行修改的时候为控件添加或移除running伪类。而为了方便测试我将这个Running绑定至了Checkbox的IsChecked属性,代码如下:
<CheckBox Canvas.Left="600" Canvas.Top="250" Name="CheckBox"> 运行状态 </CheckBox> <controls:Pump Canvas.Left="600" Canvas.Top="100" Height="100" Running="{Binding #CheckBox.IsChecked}" Width="100" />
开始时我将伪类的创建与移除代码编写在Runing属性的set中进行处理,代码如下:
public bool? Running { get => GetValue(RunningProperty); set { SetValue(RunningProperty, value); if (value == true) { PseudoClasses.Add("running"); } else { PseudoClasses.Remove("running"); } } }
但是以上的代码在CheckBox的IsChecked属性变更时并没有办法触发到set方法,导致动画无法被触发。
经过一段时间的摸索正确的做法为在OnPropertyChanged方法中进行相关的处理,代码如下:
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) { base.OnPropertyChanged(change); if (change.Property == RunningProperty) { if (change.NewValue != null && (bool)change.NewValue) { PseudoClasses.Add("running"); } else { PseudoClasses.Remove("running"); } } }
总结
在本文中,我们探讨了如何利用Avalonia框架开发水泵控件,并详细介绍了控件构成、动画创建及开发过程中的一些问题和解决方案。通过使用Canvas包裹可旋转部分,我们简化了动画实现,同时利用伪类管理水泵的运行状态,使得控件的交互性和视觉效果得到了有效提升。
我们还分享了在创建自定义伪类时的一些注意事项,以及如何在属性变更时动态更新伪类的实现。这个过程强调了Avalonia框架在构建灵活、跨平台的用户界面方面的强大能力。
希望这篇文章能够为开发者在使用Avalonia进行控件开发时提供一些实用的参考和启发,助力他们更好地实现工业自动化中的各类应用。无论是初学者还是经验丰富的开发者,都可以在这个过程中获得新的见解和技巧。让我们共同期待在Avalonia上实现更多的创意与创新!
控件的完整代码由于内容较多,请至文章底部的控件代码链接自行获取。
欢迎关注我的公众号“nodered-co”,原创技术文章第一时间推送。