WPF开发较为完整的音乐播放器(三) ---数据读取类以及界面的构建和所有代码
先向大家阐述下,由于我装的Windows8系统不含WMP,因此不能再继续正常开发下去了,我会将我已经写好的悉数公开来,并且在最后给个后边的思路,大家可以修改代码继续写下去,并且可以在评论中交流。
先附上之前的xml样本:
<?xml version="1.0" encoding="utf-8" ?> <MusicList name="{Name}"> <Music url="{URl}" singer="{singer}">{title}</Music> <Music url="{URl}" singer="{singer}">{title}</Music> </MusicList>
废话不多说,继续写。
数据读取类用来和ListBox绑定,这里由于是依赖项属性,可以用更改通知,但是笔者并没有使用,而是使用笨拙的DispatcherTimer,大家可以尝试下更简洁的数据更改通知。
由于类是比较简单的,直接上代码:
public class XmlListProduct { public XmlListProduct(string url,string name,string singer) { Url = url; Title = name; Singer = singer; } public string Url { get; set; } public string Title { get; set; } public string Singer { get; set; } }
其中属性依次是音乐文件地址,音乐名,歌手名,(别问我为什么没有持续时间,我倒是想加)。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WpfApplication10 { public class Product { public Product(TimeSpan musicposition) { MusicPosition = musicposition; } public TimeSpan MusicPosition { set; get; } } }
这一部分则是绑定播放器的播放时间,实现播放控制Slider移动,C#代码鉴下边的主界面代码。
此前说过工程中列表和播放控制是两个控件,就先发下播放控制的xaml:
<UserControl x:Class="WpfApplication10.PlayControls" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:WpfApplication10" mc:Ignorable="d" d:DesignHeight="100" d:DesignWidth="300"> <UserControl.Resources> <local:MusicTimeConverter x:Key="musictimeconverter"></local:MusicTimeConverter> <local:TimeSpanConverter x:Key="dateconverter"></local:TimeSpanConverter> <local:SecondsConverter x:Key="secondsconverter"></local:SecondsConverter> </UserControl.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <TextBlock Name="musictitle"></TextBlock> <Grid Grid.Row="1"> <Grid.ColumnDefinitions> <ColumnDefinition Width="8*"></ColumnDefinition> <ColumnDefinition Width="2*"></ColumnDefinition> </Grid.ColumnDefinitions> <Slider Name="timecontrol" Focusable="False" FocusVisualStyle="{x:Null}" VerticalAlignment="Center" Margin="5" Value="{Binding MusicPosition,Converter={StaticResource secondsconverter}}"/> <TextBlock Grid.Column="1" VerticalAlignment="Center" Margin="5" Text="{Binding MusicPosition,Converter={ StaticResource dateconverter}}"></TextBlock> </Grid> <Grid Grid.Row="2"> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <Button Grid.Column="0" Content="上一曲" Name="lastKey"></Button> <Button Grid.Column="1" Content="播放" Name="playKey"> </Button> <Button Grid.Column="2" Content="下一曲" Name="nextKey"></Button> <Slider Grid.Column="4" Maximum="100" Name="volume"></Slider> </Grid> </Grid> </UserControl>
using System; using System.Collections.Generic; using System.Linq; using System.Text; 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; namespace WpfApplication10 { /// <summary> /// PlayControls.xaml 的交互逻辑 /// </summary> public partial class PlayControls : UserControl { public PlayControls() { InitializeComponent(); this.Loaded += PlayControls_Loaded; } void PlayControls_Loaded(object sender, RoutedEventArgs e) { } /// <summary> /// 设置播放控制区 的标题 /// </summary> /// <param name="title"></param> public void SetTitle(string title) { musictitle.Text = title; } /// <summary> /// 设置播放控制Slider的最大值 /// </summary> /// <param name="num"></param> public void SetMaxNum(double num) { timecontrol.Maximum = num; } } }
列表文件的xaml:
<UserControl x:Class="WpfApplication10.PlayLists" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300"> <Grid> <ListBox Name="list" Focusable="False" Width="300" Padding="0" Style="{StaticResource listStyle}" HorizontalAlignment="Stretch" MouseDoubleClick="item_MouseDoubleClick"> </Grid> </UserControl>
c#code:
using System; using System.Collections.Generic; using System.Linq; using System.Text; 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; namespace WpfApplication10 { /// <summary> /// PlayLists.xaml 的交互逻辑 /// </summary> public partial class PlayLists : UserControl { public PlayLists() { InitializeComponent(); InitList(); } private void InitList() { List<XmlListProduct> musiclist = new List<XmlListProduct>(); MusicListsReader mlr = new MusicListsReader("PlayLists.xml"); //加载播放列表的文件 for (int i = 0; i < mlr.GetXmlCount(); i++) { XmlListProduct xlp= mlr.GetXmlMusicItemInformation(i); //读取xml中的信息 ListBoxItem item = new ListBoxItem(); item.Content= xlp; musiclist.Add(xlp); } list.ItemsSource = musiclist; //绑定到ItemSource } } }
在其中还用到了值转换器:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace WpfApplication10 { public class TimeSpanConverter:System.Windows.Data.IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { TimeSpan ts = (TimeSpan)value; string result = String.Format("{0:mm\\:ss}", ts); return result; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value; } } public class SecondsConverter : System.Windows.Data.IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { TimeSpan ts = (TimeSpan)value; double seconds = ts.TotalSeconds; return seconds; } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value; } } }
主界面的xaml:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication10" x:Class="WpfApplication10.MainWindow" Title="音乐播放器" Height="400" Width="300"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="Auto"/> <RowDefinition Height="Auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBlock><Run Text="音乐播放器"/></TextBlock> <local:PlayControls x:Name="playcontrols" HorizontalAlignment="Stretch" Grid.Row="1" VerticalAlignment="Center" Loaded="PlayControls_Loaded_1"/> <local:PlayLists x:Name="playlists" HorizontalAlignment="Stretch" Grid.Row="2" VerticalAlignment="Stretch"/> </Grid> </Window>
主界面的code:
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; 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 WpfApplication10; namespace WpfApplication10 { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { public MusicPlay play = null; public string nowTitle; public int count=0; public MainWindow() { InitializeComponent(); play = new MusicPlay(); } static MainWindow() { } private void PlayControls_Loaded_1(object sender, RoutedEventArgs e) { playcontrols.playKey.Click += playKey_Click; playcontrols.nextKey.Click += nextKey_Click; playcontrols.lastKey.Click += lastKey_Click; playcontrols.volume.Loaded += volume_Loaded; playcontrols.volume.ValueChanged += volume_ValueChanged; AddHandler(System.Windows.Controls.Primitives.Thumb.DragStartedEvent, new DragStartedEventHandler(DragStarted)); AddHandler(System.Windows.Controls.Primitives.Thumb.DragCompletedEvent, new DragCompletedEventHandler(DragCompleted)); play.dt = new System.Windows.Threading.DispatcherTimer(); play.dt.Interval = TimeSpan.FromSeconds(1); play.dt.Tick += dt_Tick; } void volume_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e) { play.SetVolume(e.NewValue); } void volume_Loaded(object sender, RoutedEventArgs e) { playcontrols.volume.Value = play.GetVolume(); } private void ThumbMouseClick(object sender, MouseEventArgs e) { } private void DragCompleted(object sender, DragCompletedEventArgs e) { TimeSpan ts = TimeSpan.FromSeconds(playcontrols.timecontrol.Value); Debug.WriteLine(ts); play.SetPosition(ts); Product product = new Product(ts); playcontrols.DataContext = product; play.dt.Start(); } /// <summary> /// 音乐控制滑块开始拖动事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void DragStarted(object sender, DragStartedEventArgs e) { play.dt.Stop(); if (count == 0) { playcontrols.timecontrol.ClearValue(Slider.ValueProperty); count++; } } /// <summary> /// 播放上一曲音乐 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void lastKey_Click(object sender, RoutedEventArgs e) { ischange = true; //如果上一曲存在 if (playlists.list.SelectedIndex >= 1) { playlists.list.SelectedIndex = playlists.list.SelectedIndex - 1;//选中上一个音乐 PlayMusic(0);//播放选择的音乐 } } void nextKey_Click(object sender, RoutedEventArgs e) { ischange = true; //如果下一曲存在 if (playlists.list.SelectedIndex < playlists.list.Items.Count) { playlists.list.SelectedIndex = playlists.list.SelectedIndex + 1;//选中下一曲音乐 PlayMusic(0);//播放选中的音乐 } } /// <summary> /// 播放列表中选中的音乐 /// </summary> void PlayMusic(int type) { if (type == 0) { XmlListProduct item = (XmlListProduct)playlists.list.SelectedItem; play.Load(new Uri(item.Url)); nowTitle = item.Title + "--" + item.Singer; Product p = new Product(play.GetPosition()); playcontrols.SetTitle(nowTitle); play.Play(); playcontrols.SetMaxNum(play.GetMusicDuringTime().TotalSeconds); playcontrols.DataContext = p; System.Threading.Thread.Sleep(100); } play.Play(); } //播放/暂停按钮 void playKey_Click(object sender, RoutedEventArgs e) { if (ischange) { if (play.GetPlayState() == MusicPlay.PlayState.playing) { play.Pause(); playcontrols.playKey.Content = "播放"; } else { PlayMusic(1); playcontrols.playKey.Content = "暂停"; } } else { if (playlists.list.SelectedIndex < 0) { playlists.list.SelectedIndex = 0; } PlayMusic(0); playcontrols.playKey.Content = "暂停"; } ischange = true; } void dt_Tick(object sender, EventArgs e) { if (play.GetPlayState() == MusicPlay.PlayState.playing) { Product product=new Product(play.GetPosition()); playcontrols.DataContext = product; } } public bool ischange=false; } }
值得提醒的是
AddHandler(System.Windows.Controls.Primitives.Thumb.DragStartedEvent, new DragStartedEventHandler(DragStarted));
AddHandler(System.Windows.Controls.Primitives.Thumb.DragCompletedEvent, new DragCompletedEventHandler(DragCompleted));
这两段代码实现了关联Slider的滑块拖动事件,前者是开始拖动,后者是完成拖动,其余代码大家看下。
还有就是空间模板和数据模板,没学过的可以百度下:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WpfApplication10"> <Style TargetType="{x:Type ListBox}" x:Key="listStyle"> <Style.Setters> <Setter Property="ListBox.ItemTemplate"> <Setter.Value> <DataTemplate DataType="ListBox"> <Grid Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Width="250"> <Grid.RowDefinitions> <RowDefinition></RowDefinition> <RowDefinition></RowDefinition> </Grid.RowDefinitions> <Grid Grid.Row="0"> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock HorizontalAlignment="Left" Text="{Binding Title}" Grid.Column="0"></TextBlock> <TextBlock HorizontalAlignment="Right" Text="45242" Grid.Column="1"></TextBlock> </Grid> <Grid Grid.Row="1" Margin="0"> <Grid.ColumnDefinitions> <ColumnDefinition></ColumnDefinition> <ColumnDefinition></ColumnDefinition> </Grid.ColumnDefinitions> <TextBlock HorizontalAlignment="Left" Text="{Binding Singer}" Name="singer"></TextBlock> </Grid> </Grid> </DataTemplate> </Setter.Value> </Setter> <Setter Property="Background" Value="#FFF4F9EF"></Setter> </Style.Setters> </Style> <Style TargetType="{x:Type ListBoxItem}"> <Style.Setters> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <Border Background="{TemplateBinding Background}" Name="root"> <VisualStateManager.VisualStateGroups> <VisualStateGroup Name="fgfg"> <VisualStateGroup.Transitions> <VisualTransition From="Normal" To="MouseOver" GeneratedDuration="0:00:0.1"></VisualTransition> </VisualStateGroup.Transitions> <VisualState x:Name="Normal"></VisualState> <VisualState x:Name="MouseOver"> <Storyboard> <ColorAnimation Storyboard.TargetName="root" Storyboard.TargetProperty="Background.Color" To="#FFDEEDCE"></ColorAnimation> </Storyboard> </VisualState> <VisualState x:Name="Selected"> <Storyboard> <ColorAnimation Storyboard.TargetName="root" Storyboard.TargetProperty="Background.Color" To="#FFCDE3B4"></ColorAnimation> </Storyboard> </VisualState> <VisualState x:Name="MouseDouble"> <Storyboard> <ColorAnimation Storyboard.TargetName="root" Storyboard.TargetProperty="Background.Color" To="#FF9BC669"></ColorAnimation> </Storyboard> </VisualState> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <ContentPresenter Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="{TemplateBinding ContentControl.HorizontalAlignment}" VerticalAlignment="{TemplateBinding ContentControl.VerticalAlignment }" ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}" SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}"/> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style.Setters> </Style> </ResourceDictionary>
VisualStateManager实现了空间状态外观的快速切换,可用VisualStateManager.GoToState实现跳转到指定的状态。
再次提醒大家,代码仅仅是给大家作参考的,如果有时间,我会做出一个完整版的给大家展示。
阶段性的完结
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库