wpf 音乐盒制作(本地音乐实现)

来源:互联网 发布:决战武林灵宠进阶数据 编辑:程序博客网 时间:2024/06/06 03:29

 最近在网上看见很多人用wpf制作的音乐盒各式各样,有些已经和酷狗,酷我等的界面类似,至于功能如何无法得知,一时心里痒痒的就自己试着边学边做了起来,确实收获不少,除了歌词滚动没有写完,和歌手头像加载没有完成,现在已经实现了本地音乐的所有功能,其他的慢慢抽时间编写,现在将实现的功能写上共需要学习的伙伴查看,需要下载源码或者讲解请去CSDN本人的博客上面查看下载,地址会连接到音乐盒的工具中的关于作者中。废话不多说,先上效果图,当然我不是美工出身,界面的色彩搭配什么不是很好:


要问桌面的美女是谁,这个真不知道,觉得背景单调,就网上随便贴了一张,哪位美女慷慨可以发张照片过来换掉,免得人家告我进犯肖像权。 
该音乐盒用.net 框架,framework 4.0要运行需先安装 framework 4.0。wpf的优点这里就不多说了,它的界面优美是winform无法比拟的,并将界面和后台代码实现分开,有利于开发。
分解界面搭建:
 大家平时看到各种优美的界面,其实wpf本身自己的窗体并不是这样的优美,跟winform类似,但是我们可以进行各种设置去修改。
<Window x:Class="MyMusic.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="我的音乐盒" Height="650" Width="825" WindowStyle="None" AllowsTransparency="True" Background="Transparent" > 
后面的三个属性很重要,如果将窗体设成无边框,将背景透明, 这时候的窗体是无法移动的,那么我们就要想办法去解决,怎么办
   <Grid>
        
        <Border CornerRadius="1"  PreviewMouseLeftButtonDown="Border_PreviewMouseLeftButtonDown">
            <Border.Background>
                <LinearGradientBrush>
                    <LinearGradientBrush.GradientStops>
                        <GradientStop Offset="0.0" Color="LightBlue"></GradientStop>
                        <GradientStop Offset="0.4" Color="LightCoral"></GradientStop>
                        <GradientStop Offset="0.6" Color="AliceBlue"></GradientStop>
                        <GradientStop Offset="1.0" Color="LightBlue"></GradientStop>
                    </LinearGradientBrush.GradientStops>
                </LinearGradientBrush>
            </Border.Background>
        </Border>
在grid中加上Border,可以渲染自己喜欢的背景色,这就是wpf的美处,它可以自由的渐变色,然后将border的鼠标事件绑定,肯定是鼠标左键点击移动,平常人们的习惯。绑定的事件很简单,就是检测到鼠标左键按下时移动执行DragMove()事件。
后台代码
 /// <summary>
        /// 监听鼠标,实现窗口移动,因为透明的窗体不能移动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Border_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            this.DragMove();
        } 
在此还可以设置窗体的四个角的圆滑度,第二个属性CornerRadius="1"就是设置角度,我设置的是1,越大角度越圆滑。 
做到此处基本的窗体已经完成,可以实现一个简单的有自己背景的不同形状的窗体,并相应鼠标移动事件。 
然后就是布局了,一般需要将grid分割这样好布局 我的分割:
  <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition MinWidth="100"  Width="315"></ColumnDefinition>
                <ColumnDefinition Width="388*" />
                <ColumnDefinition Width="100*" />
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="28"></RowDefinition>
                <RowDefinition Height="92" />
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
 然后就是控件选择一般会先选择Canvas,可以自由放置的容器,我这里选择了canvas,
然后的布局就不多讲,比较简单,直接贴代码:
  <!--写控件-->
            <Canvas Grid.RowSpan="2">
                <TextBlock Canvas.Left="85" Canvas.Top="9" Height="23" Name="txt_title" Text="My Music" Width="155" TextAlignment="Center" />
                <Label Canvas.Left="0" Canvas.Top="0" Content="音乐盒" Height="28" Name="label1" Width="46" />
                <ProgressBar Canvas.Left="11" Canvas.Top="33" Height="5" Name="progressBar1" Width="294" Foreground="Blue" Background="White" />
                <Label Canvas.Left="11" Canvas.Top="37" Content="" Height="28" Name="min" FontSize="9" />
                <Label Canvas.Left="265" Canvas.Top="37" Content="" Height="28" Name="max" FontSize="9" />
                <Label Canvas.Left="3" Canvas.Top="56" Content="播放模式" Height="23" Name="label4" Width="53" FontSize="10" />
                <ComboBox Canvas.Left="51" Canvas.Top="58" Height="20" Name="comboBox1" Width="64" Background="Transparent" FontSize="9" SelectedIndex="0" SelectionChanged="comboBox1_SelectionChanged">
                    <ComboBoxItem Content="列表循环" FontSize="10" />
                    <ComboBoxItem Content="单曲循环" FontSize="10" />
                    <ComboBoxItem Content="随机播放" FontSize="10" />
                </ComboBox>
                <Button Focusable="False" Name="before" HorizontalAlignment="Left" Margin="39,9,0,7" Style="{StaticResource RadioButton}"  Content="上一首"  BorderThickness="0"  Canvas.Left="-1" Canvas.Top="78" Height="27" Width="47" Click="btn_prev_Click" />
                <Button BorderThickness="0" Canvas.Left="142" Canvas.Top="87" Content="下一首" Focusable="False" Height="27" Name="next" Style="{StaticResource RadioButton}" Width="47" Click="btn_next_Click" />
                <Button Canvas.Left="98" Canvas.Top="85" Content="开始" Height="32" Name="startbtn" Width="35" Style="{StaticResource RadioButton}" Click="btn_start_Click" />
               
                <Image Canvas.Left="195" Canvas.Top="85" Height="29" Name="image1" Stretch="Fill" Width="30" Source="/MyMusic;component/Images/audio4.ico" />
                <MediaElement Name="myplayer" Volume="0.3" Grid.Column="2" Grid.Row="2" Height="290" HorizontalAlignment="Left" Margin="36,149,0,0"  VerticalAlignment="Top" Width="58" LoadedBehavior="Manual" MediaEnded="myplayer_MediaEnded" />
                <TextBlock Canvas.Left="255" Canvas.Top="71" Height="23" Name="textBlock1" Text="{Binding ElementName=slider1, Path=value}" Width="32"  />
                <Slider Canvas.Left="233" Canvas.Top="89" Height="27" Name="slider1" Width="75" Orientation="Horizontal" HorizontalContentAlignment="Center" VerticalContentAlignment="Top" Value="30" ValueChanged="slider1_ValueChanged" Maximum="100" SmallChange="1" />
               
            </Canvas>
            <!--按钮最小化关闭-->
            <Button Grid.Column="2" Height="22" HorizontalAlignment="Left" Margin="64,6,0,0" Name="button1" VerticalAlignment="Top" Width="30" Background="Transparent" BorderBrush="#FF490808" OpacityMask="#FF001900" Content="X" Foreground="#70000000" Click="button1_Click" />
            <Button Background="Transparent" BorderBrush="#FF490808" Content="一" Foreground="#70000000" Height="22" Margin="36,6,0,0" Name="button2" OpacityMask="#FF001900" VerticalAlignment="Top" Grid.Column="2" Click="button2_Click" HorizontalAlignment="Left" Width="30" />
            <Menu Grid.Column="1" Grid.Row="1" Height="30" HorizontalAlignment="Left" Margin="0,59,0,0" Name="menu1" VerticalAlignment="Top" Width="369" Background="Transparent" >
                <MenuItem Header="乐库" Height="30" Width="60" FontSize="13"></MenuItem>
                <MenuItem Header="电台" Height="30" Width="60" FontSize="13"></MenuItem>
                <MenuItem Header="MV" Height="30" Width="60" FontSize="13"></MenuItem>
                <MenuItem Header="直播" Height="30" Width="60" FontSize="13"></MenuItem>
                <MenuItem Header="应用" Height="30" Width="60" FontSize="13"></MenuItem>
                <MenuItem Header="歌词" Height="30" Width="60" FontSize="13"></MenuItem>
            </Menu>
            <DockPanel Grid.Row="2"  HorizontalAlignment="Stretch"  Margin="2" Name="dockPanel1" VerticalAlignment="Stretch">
                <TabControl  Name="tabControl1" TabStripPlacement="Left" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Transparent" BorderThickness="0">
                    <TabItem Header="本地列表" Name="music" Background="Transparent" Height="80" FontSize="13" FontStyle="Italic" FontWeight="Bold" FontFamily="Algerian" FontStretch="Normal">
                        <Grid>
                            <TreeView  HorizontalAlignment="Stretch"  Name="treeView1" VerticalAlignment="Stretch"   BorderThickness="0" Opacity="0.6" FontStyle="Normal" FontSize="12">
                                <TreeView.Background>
                                    <ImageBrush ImageSource="/MyMusic;component/Images/back.jpg" Stretch="Fill" TileMode="FlipXY" Viewport="0,0,480,695" ViewportUnits="Absolute" />
                                </TreeView.Background>
                                <TreeViewItem Name="itemlist" Header="默认列表" FontSize="12" FontWeight="Normal" FontStyle="Normal" PreviewMouseRightButtonDown="TreeViewItem_PreviewMouseRightButtonDown">
                                    <ListBox Name="listbox" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Background="Transparent" BorderThickness="0" SelectionChanged="listbox_SelectionChanged"> </ListBox>
                                </TreeViewItem>
                            </TreeView>
                            <Menu Height="20" HorizontalAlignment="Left" Margin="191,0,0,0" Name="menu2" VerticalAlignment="Top" Width="40" Background="Transparent">
                                <MenuItem FontWeight="Bold" Foreground="Blue" Header="二二">
                                    <MenuItem Header="添加歌曲" PreviewMouseLeftButtonDown="MenuItem_PreviewMouseLeftButtonDown" />
                                    <MenuItem Header="删除歌曲" PreviewMouseLeftButtonDown="MenuItem_PreviewMouseLeftButtonDown_1" />
                                </MenuItem>
                            </Menu>
                        </Grid>
                    </TabItem>
                    <TabItem Header="收藏" Name="save" Background="Transparent" Height="80" FontWeight="Bold" FontSize="13" />
                    <TabItem Header="电台" Name="transmiter" Background="Transparent" Height="80" FontWeight="Bold" FontSize="13" />
                </TabControl>
            </DockPanel>
            <!--右侧歌词-->
            <Grid Grid.Column="1" Grid.Row="2"  HorizontalAlignment="Stretch"  Name="gridword" VerticalAlignment="Stretch" ></Grid>
           
        </Grid>
        <ToolBarTray Height="25"  Name="toolBarTray1" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Background="Silver"  Opacity="0.5">
            <ToolBar Height="25" Width="801" FlowDirection="RightToLeft" Background="Transparent">
                <Menu>
                    <MenuItem  Header="设置" FontWeight="Bold" FontSize="10">
                        <MenuItem.Icon>
                            <Image Source="/MyMusic;component/Images/gear.ico" />
                        </MenuItem.Icon>
                        <MenuItem Background="Transparent" Header="有关作者" FlowDirection="LeftToRight" Height="20" Click="MenuItem_Click">
                            <MenuItem.Icon>
                                <Image Source="/MyMusic;component/Images/Boy.ico"></Image>
                            </MenuItem.Icon>
                        </MenuItem>
                        <MenuItem Background="Transparent" Header="音乐盒设置" FlowDirection="LeftToRight" Height="20" Click="MenuItem_Click_1">
                            <MenuItem.Icon>
                                <Image Source="/MyMusic;component/Images/exec.ico"></Image>
                            </MenuItem.Icon>
                        </MenuItem>
                    </MenuItem>
                </Menu>
            </ToolBar>
        </ToolBarTray>
    </Grid> 
 要说明的就是按钮上首暂停下首的样式,自己需要根据爱好去编写,这三个按钮具有三维立体感,每一个按下动作都会有不同的效果,所以写了一个专门的按钮样式RadioButton.xaml文件,
<ResourceDictionary
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" mc:Ignorable="d">
    <!-- Resource dictionary entries should be defined here. -->
    <Style x:Key="RadioButton" TargetType="{x:Type Button}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Grid>
                        <Ellipse x:Name="ellipse" Stroke="White">
                            <Ellipse.Fill>
                                <LinearGradientBrush EndPoint="0.443,1.22" StartPoint="0.5,0">
                                    <LinearGradientBrush.RelativeTransform>
                                        <TransformGroup>
                                            <ScaleTransform CenterX="0.5" CenterY="0.5"/>
                                            <SkewTransform CenterX="0.5" CenterY="0.5"/>
                                            <RotateTransform Angle="-52.415" CenterX="0.5" CenterY="0.5"/>
                                            <TranslateTransform/>
                                        </TransformGroup>
                                    </LinearGradientBrush.RelativeTransform>
                                    <GradientStop Color="Red"  Offset="0"/>
                                    <GradientStop Color="White" Offset="1"/>
                                </LinearGradientBrush>
                            </Ellipse.Fill>
                        </Ellipse>
                        <Ellipse Stroke="{x:Null}" Margin="5.333,4.928" RenderTransformOrigin="0.5,0.5">
                            <Ellipse.Fill>
                                <LinearGradientBrush EndPoint="0.563,-0.397" StartPoint="0.406,1.322">
                                    <LinearGradientBrush.RelativeTransform>
                                        <TransformGroup>
                                            <ScaleTransform CenterX="0.5" CenterY="0.5" ScaleX="-1" ScaleY="-1"/>
                                            <SkewTransform AngleX="0" AngleY="0" CenterX="0.5" CenterY="0.5"/>
                                            <RotateTransform Angle="-52.415" CenterX="0.5" CenterY="0.5"/>
                                            <TranslateTransform/>
                                        </TransformGroup>
                                    </LinearGradientBrush.RelativeTransform>
                                    <GradientStop Color="WhiteSmoke"  Offset="1"/>
                                    <GradientStop Color="White"/>
                                </LinearGradientBrush>
                            </Ellipse.Fill>
                        </Ellipse>
                        <ContentPresenter x:Name="contentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" RecognizesAccessKey="True"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsFocused" Value="True"/>
                        <Trigger Property="IsDefaulted" Value="True"/>
                        <Trigger Property="IsMouseOver" Value="True">
                            <Setter Property="Stroke" TargetName="ellipse" Value="#FFFFF400"/>
                            <Setter Property="StrokeThickness" TargetName="ellipse" Value="1.5"/>
                        </Trigger>
                        <Trigger Property="IsPressed" Value="True">
                            <Setter Property="Fill" TargetName="ellipse">
                                <Setter.Value>
                                    <LinearGradientBrush EndPoint="0.443,1.22" StartPoint="0.5,0">
                                        <LinearGradientBrush.RelativeTransform>
                                            <TransformGroup>
                                                <ScaleTransform CenterX="0.5" CenterY="0.5"/>
                                                <SkewTransform CenterX="0.5" CenterY="0.5"/>
                                                <RotateTransform Angle="-52.415" CenterX="0.5" CenterY="0.5"/>
                                                <TranslateTransform/>
                                            </TransformGroup>
                                        </LinearGradientBrush.RelativeTransform>
                                        <GradientStop Color="#FF434040" Offset="1"/>
                                        <GradientStop Color="White" Offset="0"/>
                                    </LinearGradientBrush>
                                </Setter.Value>
                            </Setter>
                            <Setter Property="Margin" TargetName="contentPresenter" Value="5,5,0,0"/>
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="False"/>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary> 
将其绑定到按钮样式中 Style="{StaticResource RadioButton}",当然在此之前需要去app.xaml文件中添加资源引用,要不是找不到该文件的。
歌名列表用了treemview,但是他的子集没有办法选择selectindex所以比较麻烦,造成了很多困难,后来就想了半天,用listbox他可以实现索引移动,指定索引,所以每一个treemview的子集上其实是一个listbox看代码就知道了。布局就到此为止。
下面主要说说后台功能实现:

 首先是本地音乐的加载

  #region 保存文件信息用结构
        class finfo
        {
            public finfo(string name, string path) { Filename = name; Filepath = path; }
            public string Filename { set; get; }
            public string Filepath { set; get; }
            public List<lyricinfo> Lyric = new List<lyricinfo>();
        }
        class lyricinfo
        {
            public TimeSpan time;
            public string lrc;
        }
        #endregion

        //定义绑定用的List  
        BindingList<finfo> list = new BindingList<finfo>();   
后台的歌结构定义,歌词需要滚动显示的话就需要时间和词,
每次加载歌曲在list中添加,判断是否有歌词,加载,是否在播放列表中已经存在等考虑。然后去刷新列表,播放,我的播放空间使用的事medioelement。同时需要保存,下次软件启动需要自动搜索文件加载已经存在的音乐,要不就没有音乐。在这里我使用自己定义的文件,后缀取自于我自己的名字.fsf文件保存,下次打开读取路径和名称。 


主要功能实现代码贴上 :

OpenFileDialog dlg = new OpenFileDialog();
            dlg.Filter = "音频格式(*.mp3,*.wav)|*.mp3;*.wav";
            dlg.Multiselect = true;
            if (dlg.ShowDialog() != true) return; //不为确定时返回  

            for (int i = 0; i < dlg.FileNames.Length; ++i)
            {
                finfo newfile = new finfo(dlg.SafeFileNames[i], dlg.FileNames[i]);
                //是否有歌词文件  
                string filelyric = dlg.FileNames[i].Remove(dlg.FileNames[i].Length - 4) + ".lrc";
                //加载歌词  
                if (File.Exists(filelyric)) { ReadLyric(ref newfile, filelyric); }
                //添加到列表  
                int count = 0;
                if (list.Count > 0)
                {
                    for (int n = 0; n < list.Count; n++)
                        if (newfile.Filename != list[n].Filename)
                        {
                            count++;
                        }
                    if(count == list.Count)
                       list.Add(newfile);
                }
                else
                    list.Add(newfile);
            }
            listbox.Items.Clear();
            if (list.Count > 0)
            {
                for (int j = 0; j < list.Count; j++)
                {
                    listbox.Items.Add(list[j].Filename);
                }
                itemlist.ExpandSubtree();
                listbox.SelectedIndex = listbox.Items.Count - 1;
                myplayer.Source = new Uri(list[listbox.SelectedIndex].Filepath);
               // myplayer.Play();
                txt_title.Text = list[listbox.SelectedIndex].Filename;
                myplayer.MediaOpened += new RoutedEventHandler(myplayer_MediaOpened);
               // startbtn.Content = "暂停";
            }
           ///保存列表,下次程序启动自动加载
            SaveFuction(list[listbox.SelectedIndex].Filename, list[listbox.SelectedIndex].Filepath);
        }
     
        //读取lrc文件  
        private void ReadLyric(ref finfo f, string filelyric)
        {
            string lrc = File.ReadAllText(filelyric, System.Text.Encoding.GetEncoding("GB2312"));
            Regex rx = new Regex(@"(?<=^\[)(\d+:\d+\.\d+).(.+)(?=$)", RegexOptions.Multiline);
            //匹配表达式  
            foreach (Match x in rx.Matches(lrc))
            {
                try
                {
                    //读取时间  
                    TimeSpan ti = new TimeSpan(0, int.Parse(x.Value.Substring(0, 2)),
                                            int.Parse(x.Value.Substring(3, 2)));//int.Parse(x.Value.Substring(6, 2))  
                    //读取歌词  
                    string ly = x.Value.Substring(9);
                    f.Lyric.Add(new lyricinfo() { time = ti, lrc = ly });
                }
                catch
                {
                    //continue;  
                } 
删除歌曲 
 private void MenuItem_PreviewMouseLeftButtonDown_1(object sender, MouseButtonEventArgs e)
        {
            int index = 0;
            if (listbox.SelectedIndex >= 0)
            {
                index = listbox.SelectedIndex;
               ///是否正在播放
                if (myplayer.Source.LocalPath.Contains((string)(listbox.Items[listbox.SelectedIndex])))
                {
                    if (listbox.SelectedIndex == 0)
                    {
                        myplayer.Stop();
                       
                        //歌词停止加载
                    }
                    else
                    {
                        listbox.SelectedIndex--;
                        myplayer.Source = new Uri(list[listbox.SelectedIndex].Filepath);
                        myplayer.Play();
                       
                        startbtn.Content = "暂停";
                    }

                }
                txt_title.Text = list[listbox.SelectedIndex].Filename;
              
                list.RemoveAt(index);
                listbox.Items.RemoveAt(index);
                listbox.Items.Refresh();
            }
        }
 实现列表循环播放,单曲播放,随机播放等功能,需要注意listbox的selectindex,根据他的上下移动去实现。需要注意判读最后,和最上的位置。这里就不贴代码,尽量减少空间。

进度条,和声音条的实现,需要进行比例运算。进度条需要根据每首歌加载时获取时间,使用了MediaOpened事件
 DispatcherTimer timer = new DispatcherTimer();
        private void myplayer_MediaOpened(object sender, RoutedEventArgs e)
        {
            double time = myplayer.NaturalDuration.TimeSpan.TotalSeconds/60.0;
            max.Content = time.ToString("0.00");
            progressBar1.Maximum = time;
            progressBar1.Minimum = 0;
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += timer_Tick;
            timer.Start();
        } 
算了,就到此为止吧,后面随着功能的完善在加吧。


此处是音乐盒设置和有关作者,可以去里面连接下载源码地址


歌词的滚动显示和歌手的头像显示部分:
不早了,洗洗睡觉,有时间再接着写吧。 

本人也是初学者,请勿见笑,分享一下学习经验。需要源码者请去资源上下载或者邮箱找我


0 0
原创粉丝点击