原理很简单,利用Path画一个图,然后用动画进行播放,播放时间由依赖属性输入赋值与控件内部维护的一个计时器进行控制。
控件基本是玩具,无法作为真实项目使用。
因为没有设置播放源,所以编写异步播放源或者实际播放时候要将事件引发,是否播放等属性,事件移到真实播放事件
非专业UI,即使知道怎么画图也是画的不如意,到底是眼睛会了,手不行啊。
主界面xaml
1
2
3
4
5
6
7
8
9
10
11
12
13
|
< local:VoiceAnimeButton Height = "40" Width = "200" IconMargin = "5,0,-8,0" HorizontalContentAlignment = "Center" CornerRadius = "15" VerticalContentAlignment = "Center" BorderBrush = "Black" IconFill = "Black" BorderThickness = "1" Background = "Transparent" VoicePlayTime = "0:0:1" > < local:VoiceAnimeButton.ContentTemplate > < DataTemplate > < TextBlock FontSize = "10" > < Run Text = "播放时间" /> < Run Text = "{Binding RelativeSource={RelativeSource AncestorLevel=1,AncestorType=local:VoiceAnimeButton,Mode=FindAncestor}, Path=VoicePlayTime}" /> < Run Text = " " /> < Run Text = "状态: " /> < Run Text = "{Binding RelativeSource={RelativeSource AncestorLevel=1,AncestorType=local:VoiceAnimeButton,Mode=FindAncestor}, Path=IsVoicePlay}" /> </ TextBlock > </ DataTemplate > </ local:VoiceAnimeButton.ContentTemplate > </ local:VoiceAnimeButton > |
控件设计XAML
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
|
< ResourceDictionary xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local = "clr-namespace:声音播放动画" > < Style TargetType = "{x:Type local:VoiceAnimeButton}" > < Setter Property = "Template" > < Setter.Value > < ControlTemplate TargetType = "{x:Type local:VoiceAnimeButton}" > < Border Background = "{TemplateBinding Background}" BorderBrush = "{TemplateBinding BorderBrush}" BorderThickness = "{TemplateBinding BorderThickness}" CornerRadius = "{TemplateBinding CornerRadius}" Padding = "1" > < Grid > < Grid.ColumnDefinitions > < ColumnDefinition Width = "auto" /> < ColumnDefinition Width = "*" /> </ Grid.ColumnDefinitions > < Border Margin = "{TemplateBinding IconMargin}" > < Viewbox > < Path x:Name = "VoicePath" Height = "{TemplateBinding IconHieght}" Width = "{TemplateBinding IconWidth}" Fill = "{TemplateBinding IconFill}" > < Path.Data > < PathGeometry > < PathFigureCollection > M20 20 Q12 45 20 85 l7 -4 Q18 48 27 23 l-7 -3Z M32 29 Q22 45 32 75 l7 -4 Q29 50 38 33 l-6.5 -4 M45 35 Q38 48 45 68 l7 -4 Q45 50 52 39 l-7.5 -4 M58 41 Q55 49 58 61 l17 -11Z </ PathFigureCollection > </ PathGeometry > </ Path.Data > </ Path > </ Viewbox > </ Border > < ContentPresenter Grid.Column = "1" HorizontalAlignment = "{TemplateBinding HorizontalContentAlignment}" VerticalAlignment = "{TemplateBinding VerticalContentAlignment}" /> </ Grid > </ Border > < ControlTemplate.Triggers > < EventTrigger RoutedEvent = "VoicePlayStart" > < BeginStoryboard x:Name = "bs1" > < Storyboard Storyboard.TargetProperty = "Data" Storyboard.TargetName = "VoicePath" RepeatBehavior = "Forever" Duration = "0:0:0.4" BeginTime = "0" > < ObjectAnimationUsingKeyFrames > < DiscreteObjectKeyFrame KeyTime = "0:0:0.1" > < DiscreteObjectKeyFrame.Value > < PathGeometry > < PathFigureCollection > M45 35 Q38 48 45 68 l7 -4 Q45 50 52 39 l-7.5 -4 M58 41 Q55 49 58 61 l17 -11Z </ PathFigureCollection > </ PathGeometry > </ DiscreteObjectKeyFrame.Value > </ DiscreteObjectKeyFrame > < DiscreteObjectKeyFrame KeyTime = "0:0:0.2" > < DiscreteObjectKeyFrame.Value > < PathGeometry > < PathFigureCollection > M32 29 Q22 45 32 75 l7 -4 Q29 50 38 33 l-6.5 -4 M45 35 Q38 48 45 68 l7 -4 Q45 50 52 39 l-7.5 -4 M58 41 Q55 49 58 61 l17 -11Z </ PathFigureCollection > </ PathGeometry > </ DiscreteObjectKeyFrame.Value > </ DiscreteObjectKeyFrame > < DiscreteObjectKeyFrame KeyTime = "0:0:0.3" > < DiscreteObjectKeyFrame.Value > < PathGeometry > < PathFigureCollection > M20 20 Q12 45 20 85 l7 -4 Q18 48 27 23 l-7 -3Z M32 29 Q22 45 32 75 l7 -4 Q29 50 38 33 l-6.5 -4 M45 35 Q38 48 45 68 l7 -4 Q45 50 52 39 l-7.5 -4 M58 41 Q55 49 58 61 l17 -11Z </ PathFigureCollection > </ PathGeometry > </ DiscreteObjectKeyFrame.Value > </ DiscreteObjectKeyFrame > </ ObjectAnimationUsingKeyFrames > </ Storyboard > </ BeginStoryboard > </ EventTrigger > < EventTrigger RoutedEvent = "VoicePlayEnd" > < RemoveStoryboard BeginStoryboardName = "bs1" /> </ EventTrigger > </ ControlTemplate.Triggers > </ ControlTemplate > </ Setter.Value > </ Setter > </ Style > </ ResourceDictionary > |
控件CS代码
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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.Windows.Threading; namespace 声音播放动画 { public class VoiceAnimeButton : ContentControl { static VoiceAnimeButton() { DefaultStyleKeyProperty.OverrideMetadata( typeof (VoiceAnimeButton), new FrameworkPropertyMetadata( typeof (VoiceAnimeButton))); } private DispatcherTimer Timer; public VoiceAnimeButton() { Timer = new DispatcherTimer(); Timer.Tick += Timer_Tick; Timer.Interval = TimeSpan.FromSeconds(1); } private void Timer_Tick( object sender, EventArgs e) { Timer.Stop(); IsVoicePlay = false ; this .RaiseEvent( new RoutedEventArgs(VoicePlayEndEvent, this )); } public static readonly RoutedEvent VoicePlayStartEvent = EventManager.RegisterRoutedEvent( "VoicePlayStart" , RoutingStrategy.Bubble, typeof (RoutedEventHandler), typeof (VoiceAnimeButton)); /// <summary> /// 声音播放开始事件 /// </summary> public event RoutedEventHandler VoicePlayStart { add { this .AddHandler(VoicePlayStartEvent, value); } remove { RemoveHandler(VoicePlayStartEvent, value); } } public static readonly RoutedEvent VoicePlayEndEvent= EventManager.RegisterRoutedEvent( "VoicePlayEnd" , RoutingStrategy.Bubble, typeof (RoutedEventHandler), typeof (VoiceAnimeButton)); /// <summary> /// 声音播放结束事件 /// </summary> public event RoutedEventHandler VoicePlayEnd { add { AddHandler(VoicePlayEndEvent, value); } remove { RemoveHandler(VoicePlayEndEvent, value); } } protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e) { base .OnMouseLeftButtonDown(e); IsMouseLeftClick = true ; Timer.Interval = VoicePlayTime; Timer.Start(); IsVoicePlay = true ; this .RaiseEvent( new RoutedEventArgs(VoicePlayStartEvent, this )); } protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e) { base .OnMouseLeftButtonUp(e); IsMouseLeftClick = false ; } public static readonly DependencyProperty VoicePlayTimeProperty = DependencyProperty.Register( "VoicePlayTime" , typeof (TimeSpan), typeof (VoiceAnimeButton), new PropertyMetadata(TimeSpan.FromMilliseconds(1000))); public TimeSpan VoicePlayTime { get => (TimeSpan)GetValue(VoicePlayTimeProperty); set => SetValue(VoicePlayTimeProperty, value); } public static readonly DependencyProperty IsMouseLeftClickProperty = DependencyProperty.Register( "IsMouseLeftClick" , typeof ( bool ), typeof (VoiceAnimeButton), new PropertyMetadata( false )); public bool IsMouseLeftClick { get => ( bool )GetValue(IsMouseLeftClickProperty); set => SetValue(IsMouseLeftClickProperty, value); } public static readonly DependencyProperty IconWidthProperty = DependencyProperty.Register( "IconWidth" , typeof ( double ), typeof (VoiceAnimeButton), new PropertyMetadata(100.0)); public double IconWidth { get => Convert.ToDouble(IconWidthProperty); set => SetValue(IconWidthProperty, value); } public static readonly DependencyProperty IconHieghtProperty = DependencyProperty.Register( "IconHieght" , typeof ( double ), typeof (VoiceAnimeButton), new PropertyMetadata(100.0)); public double IconHieght { get => Convert.ToDouble(IconHieghtProperty); set => SetValue(IconHieghtProperty, value); } public static readonly DependencyProperty IconFillProperty= DependencyProperty.Register( "IconFill" , typeof (SolidColorBrush), typeof (VoiceAnimeButton), new PropertyMetadata( new SolidColorBrush(Colors.Black))); public SolidColorBrush IconFill { get => GetValue(IconFillProperty) as SolidColorBrush; set => SetValue(IconFillProperty, value); } public static readonly DependencyProperty CornerRadiusProperty = DependencyProperty.Register( "CornerRadius" , typeof (CornerRadius), typeof (VoiceAnimeButton), new PropertyMetadata( new CornerRadius(0))); public CornerRadius CornerRadius { get => (CornerRadius)GetValue(CornerRadiusProperty); set => SetValue(CornerRadiusProperty, value); } public static readonly DependencyProperty IconMarginProperty = DependencyProperty.Register( "IconMargin" , typeof (Thickness), typeof (VoiceAnimeButton), new PropertyMetadata( new Thickness(0.0))); public Thickness IconMargin { get => (Thickness)GetValue(IconMarginProperty); set => SetValue(IconMarginProperty, value); } public static readonly DependencyProperty IsVoicePlayProperty = DependencyProperty.Register( "IsVoicePlay" , typeof ( bool ), typeof (VoiceAnimeButton)); public bool IsVoicePlay { get => ( bool )GetValue(IsVoicePlayProperty); set => SetValue(IsVoicePlayProperty, value); } } } |
以上就是C# WPF实现的语音播放自定义控件的详细内容,更多关于WPF实现语音播放自定义控件的资料请关注服务器之家其它相关文章!
原文链接:https://www.cnblogs.com/T-ARF/p/12727925.html