WPF实现一个播放音乐和视频的应用

一、项目准备

  1. ​创建WPF项目​

    • 在Visual Studio中新建WPF App (.NET Framework)项目
    • 命名为"MediaPlayerApp"
  2. ​添加必要的NuGet包​

    Install-Package Microsoft.WindowsAPICodePack-Shell
    Install-Package TagLibSharp

二、界面设计

1. 主窗口XAML (MainWindow.xaml)

<Window x:Class="MediaPlayerApp.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:MediaPlayerApp"
        mc:Ignorable="d"
        Title="媒体播放器" Height="600" Width="800">
    <Grid>
        <!-- 播放器控制区 -->
        <StackPanel Orientation="Horizontal" VerticalAlignment="Bottom" Height="80" Background="#FF2D2D30" Margin="0,0,0,10">
            <Button x:Name="btnPlayPause" Content="▶" Width="50" Height="50" Margin="10" Click="btnPlayPause_Click" FontSize="20"/>
            <Button x:Name="btnStop" Content="◼" Width="50" Height="50" Margin="10" Click="btnStop_Click" FontSize="20"/>
            <Slider x:Name="timelineSlider" Width="400" Margin="10" ValueChanged="timelineSlider_ValueChanged"/>
            <TextBlock x:Name="txtTime" Margin="10" VerticalAlignment="Center" Foreground="White" FontSize="14"/>
            <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="10">
                <TextBlock Text="音量:" Foreground="White" FontSize="14"/>
                <Slider x:Name="volumeSlider" Width="80" Margin="5" ValueChanged="volumeSlider_ValueChanged" Value="50"/>
            </StackPanel>
            <ComboBox x:Name="mediaTypeCombo" Width="100" Margin="10" SelectionChanged="mediaTypeCombo_SelectionChanged"/>
            <TextBox x:Name="txtMediaPath" Width="200" Margin="10"/>
            <Button x:Name="btnOpen" Content="打开" Width="60" Margin="10" Click="btnOpen_Click"/>
        </StackPanel>
        
        <!-- 媒体显示区 -->
        <MediaElement x:Name="mediaElement" LoadedBehavior="Manual" UnloadedBehavior="Stop" MediaOpened="mediaElement_MediaOpened" MediaEnded="mediaElement_MediaEnded"/>
        
        <!-- 视频控制区(仅在播放视频时显示) -->
        <StackPanel x:Name="videoControls" Orientation="Vertical" VerticalAlignment="Top" Height="40" Background="#CC000000" Visibility="Collapsed" Margin="10">
            <ToggleButton x:Name="fullscreenToggle" Content="全屏" Width="80" Height="30" Margin="5" Click="fullscreenToggle_Click"/>
        </StackPanel>
    </Grid>
</Window>

三、代码实现

1. 主窗口代码 (MainWindow.xaml.cs)

using Microsoft.WindowsAPICodePack.Dialogs;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;

namespace MediaPlayerApp
{
    public partial class MainWindow : Window
    {
        private DispatcherTimer timelineSliderTimer;
        private bool isDraggingSlider = false;
        private bool isFullscreen = false;

        public MainWindow()
        {
            InitializeComponent();
            InitializeTimer();
            LoadMediaTypes();
        }

        private void InitializeTimer()
        {
            timelineSliderTimer = new DispatcherTimer();
            timelineSliderTimer.Interval = TimeSpan.FromMilliseconds(100);
            timelineSliderTimer.Tick += TimelineSliderTimer_Tick;
        }

        private void LoadMediaTypes()
        {
            mediaTypeCombo.Items.Add("音频");
            mediaTypeCombo.Items.Add("视频");
            mediaTypeCombo.SelectedIndex = 0;
        }

        private void btnOpen_Click(object sender, RoutedEventArgs e)
        {
            var dialog = new CommonOpenFileDialog
            {
                Title = "选择媒体文件",
                IsFolderPicker = false,
                Multiselect = false
            };

            if (mediaTypeCombo.SelectedIndex == 0) // 音频
            {
                dialog.Filters.Add(new CommonFileDialogFilter("音频文件", "*.mp3;*.wav;*.aac;*.flac"));
            }
            else // 视频
            {
                dialog.Filters.Add(new CommonFileDialogFilter("视频文件", "*.mp4;*.avi;*.mkv;*.wmv"));
            }

            if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
            {
                txtMediaPath.Text = dialog.FileName;
                PlayMedia(dialog.FileName);
            }
        }

        private void PlayMedia(string filePath)
        {
            if (!File.Exists(filePath))
            {
                MessageBox.Show("文件不存在!", "错误", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            mediaElement.Source = new Uri(filePath);
            mediaElement.Play();

            // 更新时间显示
            UpdateTimeDisplay();

            // 显示/隐藏视频控制区
            if (mediaElement.NaturalVideoHeight > 0)
            {
                videoControls.Visibility = Visibility.Visible;
            }
            else
            {
                videoControls.Visibility = Visibility.Collapsed;
            }
        }

        private void btnPlayPause_Click(object sender, RoutedEventArgs e)
        {
            if (mediaElement.CanPause)
            {
                if (mediaElement.CurrentState == System.Windows.Media.MediaElementState.Paused || 
                    mediaElement.CurrentState == System.Windows.Media.MediaElementState.Stopped)
                {
                    mediaElement.Play();
                    btnPlayPause.Content = "❚❚";
                }
                else
                {
                    mediaElement.Pause();
                    btnPlayPause.Content = "▶";
                }
            }
        }

        private void btnStop_Click(object sender, RoutedEventArgs e)
        {
            mediaElement.Stop();
            btnPlayPause.Content = "▶";
            timelineSlider.Value = 0;
            txtTime.Text = "00:00:00 / 00:00:00";
        }

        private void timelineSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (!isDraggingSlider)
            {
                isDraggingSlider = true;
                mediaElement.Position = TimeSpan.FromSeconds(timelineSlider.Value);
                isDraggingSlider = false;
            }
        }

        private void timelineSliderTimer_Tick(object sender, EventArgs e)
        {
            if (!isDraggingSlider)
            {
                timelineSlider.Value = mediaElement.Position.TotalSeconds;
            }
        }

        private void TimelineSliderTimer_Tick(object sender, EventArgs e)
        {
            if (!isDraggingSlider && mediaElement.NaturalDuration.HasTimeSpan)
            {
                timelineSlider.Maximum = mediaElement.NaturalDuration.TimeSpan.TotalSeconds;
                timelineSlider.Value = mediaElement.Position.TotalSeconds;

                UpdateTimeDisplay();
            }
        }

        private void UpdateTimeDisplay()
        {
            if (mediaElement.NaturalDuration.HasTimeSpan)
            {
                txtTime.Text = $"{mediaElement.Position.ToString(@"hh\:mm\:ss")} / {mediaElement.NaturalDuration.TimeSpan.ToString(@"hh\:mm\:ss")}";
            }
            else
            {
                txtTime.Text = "00:00:00 / --:--:--";
            }
        }

        private void volumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            mediaElement.Volume = volumeSlider.Value / 100;
        }

        private void mediaElement_MediaOpened(object sender, RoutedEventArgs e)
        {
            timelineSlider.Maximum = mediaElement.NaturalDuration.TimeSpan.TotalSeconds;
            timelineSliderTimer.Start();
            
            // 根据媒体类型设置默认音量
            if (mediaTypeCombo.SelectedIndex == 0) // 音频
            {
                volumeSlider.Value = 70;
            }
            else // 视频
            {
                volumeSlider.Value = 50;
            }
        }

        private void mediaElement_MediaEnded(object sender, RoutedEventArgs e)
        {
            btnPlayPause.Content = "▶";
            timelineSlider.Value = 0;
            txtTime.Text = "00:00:00 / 00:00:00";
        }

        private void fullscreenToggle_Click(object sender, RoutedEventArgs e)
        {
            if (isFullscreen)
            {
                this.WindowState = WindowState.Normal;
                this.WindowStyle = WindowStyle.SingleBorderWindow;
                fullscreenToggle.Content = "全屏";
            }
            else
            {
                this.WindowState = WindowState.Maximized;
                this.WindowStyle = WindowStyle.None;
                fullscreenToggle.Content = "退出";
            }
            isFullscreen = !isFullscreen;
        }

        private void mediaElement_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            // 视频大小自适应
            if (mediaElement.NaturalVideoHeight > 0)
            {
                double aspectRatio = mediaElement.NaturalVideoWidth / (double)mediaElement.NaturalVideoHeight;
                mediaElement.Width = this.ActualWidth - 20; // 减去边距
                mediaElement.Height = mediaElement.Width / aspectRatio;
            }
        }
    }
}

2. 添加必要的引用

在项目中添加以下引用:

  • PresentationCore
  • WindowsBase
  • Microsoft.WindowsAPICodePack.Shell
  • TagLibSharp (用于获取媒体元数据)

四、功能扩展

1. 添加播放列表功能

 
<!-- 在MainWindow.xaml中添加 -->
<ListBox x:Name="playlist" Margin="10" SelectionChanged="playlist_SelectionChanged"/>
<Button Content="添加到播放列表" Click="AddToPlaylist_Click" Margin="10"/>
 
// 在MainWindow.xaml.cs中添加
private void AddToPlaylist_Click(object sender, RoutedEventArgs e)
{
    var dialog = new CommonOpenFileDialog
    {
        Title = "选择媒体文件",
        IsFolderPicker = false,
        Multiselect = true
    };

    if (mediaTypeCombo.SelectedIndex == 0) // 音频
    {
        dialog.Filters.Add(new CommonFileDialogFilter("音频文件", "*.mp3;*.wav;*.aac;*.flac"));
    }
    else // 视频
    {
        dialog.Filters.Add(new CommonFileDialogFilter("视频文件", "*.mp4;*.avi;*.mkv;*.wmv"));
    }

    if (dialog.ShowDialog() == CommonFileDialogResult.Ok)
    {
        foreach (var file in dialog.FileNames)
        {
            playlist.Items.Add(file);
        }
    }
}

private void playlist_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    if (playlist.SelectedItem != null)
    {
        PlayMedia(playlist.SelectedItem.ToString());
    }
}

2. 添加媒体元数据显示

 
<!-- 在MainWindow.xaml中添加 -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Height="60" Background="#FF2D2D30" Margin="0,0,0,10">
    <Image x:Name="albumArt" Width="50" Height="50" Margin="10"/>
    <StackPanel Margin="10">
        <TextBlock x:Name="txtTitle" FontSize="16" Foreground="White"/>
        <TextBlock x:Name="txtArtist" FontSize="12" Foreground="#CCCCCB"/>
    </StackPanel>
</StackPanel>
 
// 在PlayMedia方法中添加
try
{
    var file = TagLib.File.Create(filePath);
    albumArt.Source = GetAlbumArt(file);
    txtTitle.Text = file.Tag.Title ?? Path.GetFileNameWithoutExtension(filePath);
    txtArtist.Text = file.Tag.Performers.Length > 0 ? file.Tag.Performers[0] : "未知艺术家";
}
catch
{
    // 如果无法读取元数据,使用默认值
    albumArt.Source = null;
    txtTitle.Text = Path.GetFileNameWithoutExtension(filePath);
    txtArtist.Text = "未知艺术家";
}

// 添加获取专辑封面的方法
private ImageSource GetAlbumArt(TagLib.File file)
{
    if (file.Tag.Pictures.Length > 0)
    {
        var ms = new MemoryStream(file.Tag.Pictures[0].Data.Data);
        return BitmapFrame.Create(ms);
    }
    return null;
}

五、样式美化

1. 添加资源字典 (Styles.xaml)

 
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <!-- 按钮样式 -->
    <Style TargetType="Button">
        <Setter Property="Background" Value="#FF3A3A3C"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="BorderBrush" Value="#FF5D5D60"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Padding" Value="8,4"/>
        <Setter Property="Margin" Value="5"/>
        <Setter Property="HorizontalAlignment" Value="Center"/>
        <Setter Property="VerticalAlignment" Value="Center"/>
        <Setter Property="Cursor" Value="Hand"/>
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Background" Value="#FF5D5D60"/>
            </Trigger>
            <Trigger Property="IsPressed" Value="True">
                <Setter Property="Background" Value="#FF2D2D30"/>
            </Trigger>
        </Style.Triggers>
    </Style>

    <!-- 滑块样式 -->
    <Style TargetType="Slider">
        <Setter Property="Background" Value="#FF3A3A3C"/>
        <Setter Property="Foreground" Value="#FF5D5D60"/>
        <Setter Property="BorderBrush" Value="#FF5D5D60"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Margin" Value="5"/>
    </Style>

    <!-- 文本框样式 -->
    <Style TargetType="TextBox">
        <Setter Property="Background" Value="#FF3A3A3C"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="BorderBrush" Value="#FF5D5D60"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Padding" Value="5"/>
        <Setter Property="Margin" Value="5"/>
    </Style>

    <!-- 列表框样式 -->
    <Style TargetType="ListBox">
        <Setter Property="Background" Value="#FF3A3A3C"/>
        <Setter Property="Foreground" Value="White"/>
        <Setter Property="BorderBrush" Value="#FF5D5D60"/>
        <Setter Property="BorderThickness" Value="1"/>
        <Setter Property="Margin" Value="5"/>
    </Style>
</ResourceDictionary>

2. 在App.xaml中引用

 
<Application x:Class="MediaPlayerApp.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary Source="Styles.xaml"/>
    </Application.Resources>
</Application>

六、高级功能

1. 添加均衡器

 
<!-- 在MainWindow.xaml中添加 -->
<Expander Header="均衡器" Margin="10" Width="200">
    <StackPanel>
        <Slider x:Name="eqBass" Minimum="-15" Maximum="15" Value="0" TickFrequency="1" IsSnapToTickEnabled="True"/>
        <Slider x:Name="eqMid" Minimum="-15" Maximum="15" Value="0" TickFrequency="1" IsSnapToTickEnabled="True"/>
        <Slider x:Name="eqTreble" Minimum="-15" Maximum="15" Value="0" TickFrequency="1" IsSnapToTickEnabled="True"/>
    </StackPanel>
</Expander>
 
// 在MainWindow.xaml.cs中添加
private void InitializeEqualizer()
{
    // 这里需要使用音频处理库如NAudio来实现实际的均衡器效果
    // 示例代码仅显示UI
}

// 在构造函数中调用
InitializeEqualizer();

2. 添加播放速度控制

 
<!-- 在MainWindow.xaml中添加 -->
<StackPanel Orientation="Horizontal" Margin="10">
    <TextBlock Text="速度:" VerticalAlignment="Center"/>
    <Slider x:Name="playbackSpeed" Minimum="0.5" Maximum="2" Value="1" TickFrequency="0.25" IsSnapToTickEnabled="True" Width="100"/>
    <TextBlock Text="{Binding ElementName=playbackSpeed, Path=Value, StringFormat={}x{0:F2}}" VerticalAlignment="Center"/>
</StackPanel>
 
// 在MainWindow.xaml.cs中添加
private void playbackSpeed_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
    // 注意:MediaElement不支持直接改变播放速度
    // 可以使用MediaElement的Rate属性(仅部分系统支持)
    try
    {
        mediaElement.SpeedRatio = playbackSpeed.Value;
    }
    catch
    {
        MessageBox.Show("您的系统不支持播放速度调整", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
    }
}

七、打包发布

1. 创建安装程序

使用Visual Studio Installer Projects扩展创建安装包:

  1. 安装扩展:Visual Studio Installer Projects
  2. 新建项目:Setup Project
  3. 添加主程序文件和依赖项
  4. 配置快捷方式和文件关联

2. 发布为单文件

使用.NET Core的发布功能:

 
dotnet publish -c Release -r win-x64 --self-contained true /p:PublishSingleFile=true

八、总结

这个WPF媒体播放器实现了以下功能:

  1. 播放本地音频和视频文件
  2. 播放控制(播放/暂停/停止)
  3. 进度条和时间显示
  4. 音量控制
  5. 播放列表管理
  6. 媒体元数据显示
  7. 全屏模式
  8. 美观的UI界面

可以根据需要进一步扩展功能,如:

  • 网络流媒体支持
  • 字幕加载
  • 播放历史记录
  • 云同步播放列表
  • 更高级的音频处理(均衡器、音效)

这个项目适合作为学习WPF和多媒体开发的良好起点,同时也可以作为实际应用的基础版本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

code_shenbing

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值