C# WPF實現的語音播放自定義控件

原理很簡單,利用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);
    }
  }
}

以上就是C# WPF實現的語音播放自定義控件的詳細內容,更多關於WPF實現語音播放自定義控件的資料請關註WalkonNet其它相關文章!