WPF 仿语音播放 自定义控件

这个博客介绍了一个自定义的VoiceAnimeButton控件,该控件使用Path绘制图形并通过动画展示声音播放过程。控件具备播放时间和状态显示功能,但作者指出由于其简单性,可能不适合用于实际项目。在XAML中定义了控件样式和内容模板,通过事件触发动画的开始和结束。此外,提供了C#代码展示了控件的逻辑,包括鼠标点击启动和停止播放的逻辑,以及播放事件的处理。
摘要由CSDN通过智能技术生成

原理很简单,利用Path画一个图,然后用动画进行播放,播放时间由依赖属性输入赋值与控件内部维护的一个计时器进行控制。

控件基本是玩具,无法作为真实项目使用。

因为没有设置播放源,所以编写异步播放源或者实际播放时候要将事件引发,是否播放等属性,事件移到真实播放事件

非专业UI,即使知道怎么画图也是画的不如意,到底是眼睛会了,手不行啊。

 

主界面xaml

   <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

<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代码

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值