Avalonia小抄

最近计划用AvaloniaUI写某种跨平台的GUI软件,由于这个框架刚刚发布正式版,网络上的相关资源比较少,所以写起来比WPF是困难多了(也可能是我太菜)。

借着这个机会,我想把过程中碰到的一些问题和解决方法记录下来,以备自己后来查询,也为其他人提供可能的答案参考。

控件拖拽窗口

不太会概括这个功能,总之就是让一个控件可以有和标题栏相同的拖拽窗口功能,实际上和WPF的DragMove()相同。

1
2
3
4
private void Control_OnPointerPressed(object? sender, PointerPressedEventArgs e)
{
BeginMoveDrag(e);
}

可在官方References中找到:Avalonia UI Framework - API - Window.BeginMoveDrag(PointerPressedEventArgs) Method

顺带吐槽一下,虽然AvaloniaUI确实有和WPF非常相似的开发体验,但是有些东西悄悄给你换个名就会让人很难受。本人在找这个非常简单的功能的实现方法时一直把DragMove作为搜索关键词,但是Avalonia中把这个称为BeginMoveDrag,导致我半天没搜到。

关于渐变画刷动画

渐变画刷的动画涉及到GradientStopOffsetColor的补间问题,Avalonia使用了一种虽然不是很智能但是比较可控的实现方式,但是官方文档没有明说,我在这里碰壁之后总结出以下不完整的经验。

补间实现特点

所谓“虽然不是很智能但是比较可控的实现方式”就是把画刷中相同索引处的GradientStop对应起来,然后进行补间。虽然说GradientStop本身包含Offset属性,但是补间时的对象选择只看索引。

所以当你调换渐变节点的顺序时,静态画面不会产生效果的变化,但是一旦加入KeyframeAnimation,你就会得到一些鬼畜效果。

GradientStop.Offset移动(跑马灯效果)

注意以下几点:

  • 每个KeyframeGradientStop数量要一样
  • 每个Keyframe的相同索引的GradientStopColor要一样
  • 每个Keyframe的相同索引的GradientStopOffset要均匀变化(如果你想让跑马灯匀速移动)

你可能想问为什么不画好到200%的GradientStop然后修改StartPointEndPoint实现平移效果,实际上这种是我第一个碰壁的方案,因为我发现这个画刷只会处理0%-100%内的GradientStop,超过这个范围就会变成纯色了。

如果觉得我上面的描述比较抽象,可以看一下下面的例子,是一个类似于RGB机械键盘跑马灯效果的动画:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
<Animation Duration="0:0:5"
IterationCount="Infinite"
PlaybackDirection="Normal">
<KeyFrame Cue="0%">
<Setter Property="Background">
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Offset="0.0" Color="#ff0000" />
<GradientStop Offset="0.0" Color="#ffff00" />
<GradientStop Offset="0.0" Color="#00ff00" />
<GradientStop Offset="0.0" Color="#00ffff" />
<GradientStop Offset="0.0" Color="#0000ff" />
<GradientStop Offset="0.0" Color="#ff00ff" />
<GradientStop Offset="0.0" Color="#ff0000" />
<GradientStop Offset="0.16666666666666666" Color="#ffff00" />
<GradientStop Offset="0.3333333333333333" Color="#00ff00" />
<GradientStop Offset="0.5" Color="#00ffff" />
<GradientStop Offset="0.6666666666666666" Color="#0000ff" />
<GradientStop Offset="0.8333333333333334" Color="#ff00ff" />
<GradientStop Offset="1.0" Color="#ff0000" />
</LinearGradientBrush>
</Setter>
</KeyFrame>
<KeyFrame Cue="16.666666666666668%">
<Setter Property="Background">
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Offset="0.0" Color="#ff0000" />
<GradientStop Offset="0.0" Color="#ffff00" />
<GradientStop Offset="0.0" Color="#00ff00" />
<GradientStop Offset="0.0" Color="#00ffff" />
<GradientStop Offset="0.0" Color="#0000ff" />
<GradientStop Offset="0.0" Color="#ff00ff" />
<GradientStop Offset="0.16666666666666666" Color="#ff0000" />
<GradientStop Offset="0.3333333333333333" Color="#ffff00" />
<GradientStop Offset="0.5" Color="#00ff00" />
<GradientStop Offset="0.6666666666666666" Color="#00ffff" />
<GradientStop Offset="0.8333333333333334" Color="#0000ff" />
<GradientStop Offset="1.0" Color="#ff00ff" />
<GradientStop Offset="1.16666666666666666" Color="#ff0000" />
</LinearGradientBrush>
</Setter>
</KeyFrame>
<KeyFrame Cue="33.333333333333336%">
<Setter Property="Background">
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Offset="0.0" Color="#ff0000" />
<GradientStop Offset="0.0" Color="#ffff00" />
<GradientStop Offset="0.0" Color="#00ff00" />
<GradientStop Offset="0.0" Color="#00ffff" />
<GradientStop Offset="0.0" Color="#0000ff" />
<GradientStop Offset="0.16666666666666666" Color="#ff00ff" />
<GradientStop Offset="0.3333333333333333" Color="#ff0000" />
<GradientStop Offset="0.5" Color="#ffff00" />
<GradientStop Offset="0.6666666666666666" Color="#00ff00" />
<GradientStop Offset="0.8333333333333334" Color="#00ffff" />
<GradientStop Offset="1.0" Color="#0000ff" />
<GradientStop Offset="1.16666666666666666" Color="#ff00ff" />
<GradientStop Offset="1.3333333333333333" Color="#ff0000" />
</LinearGradientBrush>
</Setter>
</KeyFrame>
<KeyFrame Cue="50.0%">
<Setter Property="Background">
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Offset="0.0" Color="#ff0000" />
<GradientStop Offset="0.0" Color="#ffff00" />
<GradientStop Offset="0.0" Color="#00ff00" />
<GradientStop Offset="0.0" Color="#00ffff" />
<GradientStop Offset="0.16666666666666666" Color="#0000ff" />
<GradientStop Offset="0.3333333333333333" Color="#ff00ff" />
<GradientStop Offset="0.5" Color="#ff0000" />
<GradientStop Offset="0.6666666666666666" Color="#ffff00" />
<GradientStop Offset="0.8333333333333334" Color="#00ff00" />
<GradientStop Offset="1.0" Color="#00ffff" />
<GradientStop Offset="1.16666666666666666" Color="#0000ff" />
<GradientStop Offset="1.3333333333333333" Color="#ff00ff" />
<GradientStop Offset="1.5" Color="#ff0000" />
</LinearGradientBrush>
</Setter>
</KeyFrame>
<KeyFrame Cue="66.66666666666667%">
<Setter Property="Background">
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Offset="0.0" Color="#ff0000" />
<GradientStop Offset="0.0" Color="#ffff00" />
<GradientStop Offset="0.0" Color="#00ff00" />
<GradientStop Offset="0.16666666666666666" Color="#00ffff" />
<GradientStop Offset="0.3333333333333333" Color="#0000ff" />
<GradientStop Offset="0.5" Color="#ff00ff" />
<GradientStop Offset="0.6666666666666666" Color="#ff0000" />
<GradientStop Offset="0.8333333333333334" Color="#ffff00" />
<GradientStop Offset="1.0" Color="#00ff00" />
<GradientStop Offset="1.16666666666666666" Color="#00ffff" />
<GradientStop Offset="1.3333333333333333" Color="#0000ff" />
<GradientStop Offset="1.5" Color="#ff00ff" />
<GradientStop Offset="1.6666666666666666" Color="#ff0000" />
</LinearGradientBrush>
</Setter>
</KeyFrame>
<KeyFrame Cue="83.33333333333333%">
<Setter Property="Background">
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Offset="0.0" Color="#ff0000" />
<GradientStop Offset="0.0" Color="#ffff00" />
<GradientStop Offset="0.16666666666666666" Color="#00ff00" />
<GradientStop Offset="0.3333333333333333" Color="#00ffff" />
<GradientStop Offset="0.5" Color="#0000ff" />
<GradientStop Offset="0.6666666666666666" Color="#ff00ff" />
<GradientStop Offset="0.8333333333333334" Color="#ff0000" />
<GradientStop Offset="1.0" Color="#ffff00" />
<GradientStop Offset="1.16666666666666666" Color="#00ff00" />
<GradientStop Offset="1.3333333333333333" Color="#00ffff" />
<GradientStop Offset="1.5" Color="#0000ff" />
<GradientStop Offset="1.6666666666666666" Color="#ff00ff" />
<GradientStop Offset="1.8333333333333334" Color="#ff0000" />
</LinearGradientBrush>
</Setter>
</KeyFrame>
<KeyFrame Cue="100.0%">
<Setter Property="Background">
<LinearGradientBrush StartPoint="0%,0%" EndPoint="100%,100%">
<GradientStop Offset="0.0" Color="#ff0000" />
<GradientStop Offset="0.16666666666666666" Color="#ffff00" />
<GradientStop Offset="0.3333333333333333" Color="#00ff00" />
<GradientStop Offset="0.5" Color="#00ffff" />
<GradientStop Offset="0.6666666666666666" Color="#0000ff" />
<GradientStop Offset="0.8333333333333334" Color="#ff00ff" />
<GradientStop Offset="1.0" Color="#ff0000" />
<GradientStop Offset="1.16666666666666666" Color="#ffff00" />
<GradientStop Offset="1.3333333333333333" Color="#00ff00" />
<GradientStop Offset="1.5" Color="#00ffff" />
<GradientStop Offset="1.6666666666666666" Color="#0000ff" />
<GradientStop Offset="1.8333333333333334" Color="#ff00ff" />
<GradientStop Offset="2.0" Color="#ff0000" />
</LinearGradientBrush>
</Setter>
</KeyFrame>
</Animation>

写的比较丑,用后台代码而不是axaml可能会好点。

GradientStop.Color渐变

实际上注意事项和前者是几乎一样的,鉴于上面有一堆难看的代码还是不要让读者拖滚动条了:

  • 每个KeyframeGradientStop数量要一样
  • 每个Keyframe的相同索引的GradientStopOffset要一样
  • 每个Keyframe的相同索引的GradientStopColor要变化

不过我没想出这样的动画的应用场景,其实这是我本来用于实现跑马灯的第二个失败方案,最后做成了类似于毛毛虫的效果。

GradientStop增减

没有试验这一部分,如果后续有需求可能会补充。

非主线程操作UI

如果直接操作会InvalidOperationException: “Call from invalid thread”,只需要用下面的方式即可解决。

1
Dispatcher.UIThread.InvokeAsync(() => { /* Do something here */ });

留言评论

0条搜索结果。