(21)C#传智:文件操作File,Path,Directory,流FileStream,StreamReader,StreamWriter,序列化与反序列化,播放器项目。

    
    深入学习TimeSpan


一、文件操作常用相关类


    File //操作文件,静态类,对文件整体操作。拷贝,删除,剪切等
    
    Directory //操作目录(文件夹),静态类
    
    Path  //对文件或目录的路径进行操作(很方便),字符串
    
    Stream //文件流。抽象类
        FileStream //文件流。MemoryStream(内存流),NetworkStream(网络流)
        StreamReader //快速读取文本文件
        StreamWriter //快速写入文本文件
    
    
    1、神奇的Dictionary
    
        Dictionary<string,string>性能测试中,为什么速度快?
        Dictionary中有一个存储键值对的区域,这个区域的每个存储单元有地址编号,根据
        hashCode算法,计算key的值的键值对应该存储的地址,将键值对放入指定的地址即可.
        查找的时候首先计算key的地址,就可以找到数据了。
        即:根据key直接房间号,而不是逐个房间找.
        
        当把一个KeyValuePair采用一个固定算法(散列算法)根据key来计算这个KeyValuePair
        存储的地址。取的时候也是根据要找的key可以快速算出kvp存放的地址。
    
    
    2、Path类(对字符串的操作)
    
        玩的就是字符串,本身路径,文件名,扩展名,没有发生改变。
    using System.IO;
    
    string ChangeExtension(string path,string extension) 修改文件的后缀名.
        修改支持字符串层面的,没有真的给文件改名。

        string s=Path.ChangeExtension(@"C:\temp\F3.png","jpg")


        
    string Combine(string path1,string path2) 将两路径合并成一个路径。
        比+好用,可以方便解决中间不加斜线的问题,自动处理分隔符。

        string s=Path.Combine(@"C:\temp","a.jpg");//自动添加\


        
    string GetDirectoryName(string path) 获取路径(除驱动器外,最后无\)

        string s=Path.GetDirectoryName(@"C:\temp\a.jpg");


        
    string GetExtension(string path) 获取扩展名(带点号,如.txt)
    
    string GetFileName(string path) 获取文件名部分(不带路径)
    
    string GetFileNameWithoutExtension(string path)获取不带扩展名的文件名
    
    string GetFullPath(string path) 取得文件的全路径。可由相对路径获取绝对路径。

        string p1 = Path.GetFullPath(@"1.txt");
        //D:\OneDrive\CSharp\Test\Test\bin\Debug\1.txt
        string p2 = Path.GetFullPath(@"\1.txt");
        //D:\1.txt


        相对路径由程序的位置确定:
        1)前面没有\,则直接使用程序当前路径。如1.txt,1\1.txt等.
        2)前面有\,则直接使用当前驱动器.如\1.txt,\1\1.txt等.(D:\1.txt,D:\1\1.txt)
        
    Path.Combine(string path1,string path2)
        如果路径之一是零长度字符串,则该方法返回其他路径。如果path2包含绝对路径,
        则该方法返回path2.
        
        如果path1不是以分隔符结束,并且不是C:或D:等驱动器引用,则自动在串联前为
        path1增加\分隔符。
        
        分隔符:与操作系统平台有关:
        Path.DirectorySeperatorChar是\
        Path.PathSEparator是;
        Path.ValumeSeparatorChar是:
    
    Path.GetFileName() 获取文件名
        当目录为C:\windows\test时,可获取最后一个目录名,但是当目录路径为C:\windows
        \test\时,不可以。reflector查看。
    
    
    获取当前exe文件执行的路径:
    1)assembly.GetExecutingAssembly().Location;//全路径文件名,全适用
    2)Appication.StartPath;//仅路径无文件名。适用winform
    3)AppDomain.CurrentDomain.BaseDirectory;//仅路径无文件名。全适用.
    
    不要用: Directory.GetCurrentDirectory();//获取应用程序的当前工作目录。
        因为这个可能会变,通过使用OpenFileDialog或者我去设置
        Directory.SetCurrentDirectory();
        这样当前程序目录可能就不是正在使用的目录。
    上面常应用于WinForm程序。
    
    
    3、File类
    
        File.Create(string path);
        File.Delete(string path);删除指定文件。若不存在不异常。但只读/正在使用则异常
        File.Copy(string src, string des);(重载第3参true表示覆盖)
        File.Move(string src, string des);
    
        File.ReadAllBytes(string path); 返回字节数组.(修改字节数组长度为内容长度)
        File.ReadAllLines(string path); 返回字串数组
        File.ReadAllText(string path);  返回全部文本文件中的字符.
        上面三个读完,关闭文件。不用人工再关.
        一次性读取,费资源,适用于小文件。大文件用FileStream
        
        File.ReadLines(string path);    返回集合
        
        File.Exists(string path) 判断文件是否存在.返回bool

        byte[] buffer = new byte[1024];
        buffer = File.ReadAllBytes(@"E:\1.txt");
        string s = Encoding.UTF8.GetString(buffer);
        Console.WriteLine(buffer.Length);//30内容长度,不再是1024
        Console.WriteLine(s);//内容


        
        或者改成下面:

        byte[] b= File.ReadAllBytes(@"E:\1.txt");
        string s = Encoding.UTF8.GetString(b);//解码全部.GetString(b,1,4)
        Console.WriteLine(s);//内容


    
    
    
        编码:把字符串以怎样的形式存储为二进制。ASC,GBK
        
        File.WriteAllBytes(string path, byte[] bytes);
        File.WriteAllText(string path, string contents);
        File.WriteAllLines(string path, string[] contents);        
        上面写入都是覆盖式,操作完成后自动关闭.
        

        File.WriteAllLines(@"E:\4.txt", new string[4], Encoding.UTF8);//写入四个回车


        
        
        File.AppendAllLines(string path, IEnumerable<string>contents); //按行
        File.AppendAllText(string path, string contents);
        
        
        
        下面用得比较少,都是返回的是流:
            File.CreateText,File.AppendText,File.OpenText
        一般用StreamReader或StreamWriter即可。
        技巧:注意看智能提示中方法的返回类型,即可确定大概的使用方向。
        

        string p = @"E:\4.txt";
        if (!File.Exists(p))
        {
            using (StreamWriter sw = File.CreateText(p))
            {
                sw.Write("第一次写入");
                sw.WriteLine("再写入一行");//与上面紧贴
                sw.WriteLine("这才是第二行");
            }
        }
        using (StreamWriter sw = File.AppendText(p))
        {
            sw.Write("紧贴第二行");
            sw.Write('\n');
            sw.WriteLine("进入第三行");
        }

        using (StreamReader sr = File.OpenText(p))
        {
            string s = "";
            while ((s = sr.ReadLine()) != null)
            {
                Console.WriteLine(s);
            }
        }


        
        
        用File类快速得到文件流的还有:
        FileStream fs=File.Open();//返回FileStream
        FileStream fs=File.OpenRead();//返回只读FileStream
        FileStream fs=File.OpenWrite();//返回只读FileStream
        上面都可以用下面替代:
        FileStream fs=new FileStream(参数)
        
        Stream是所有流的父类,是一个抽象类。
        文件操作的类都在System.IO.*;
        
        
    ReadLines和ReadAllLines方法的区别:
        当你使用ReadLines时,你可以在整个字符串集合被返回之前开始枚举该集合;
        当你使用ReadAllLines时,你必须等待整个字符串数组被返回之后才能访问该数组。
        因此,当你处理非常大的文件时,ReadLines会更有效率。
        
    你可以使用ReadLines方法来做以下事情:
        1)在一个文件上执行LINQ to Objects查询,以获得其行的过滤集合。
        
        2)用File.WriteAllLines(String, IEnumerable<String>)方法将返回的行集合写入文件,
        或者用File.AppendAllLines(String, IEnumerable<String>)方法将它们追加到现有文件中。
        
        3)创建一个立即填充的集合实例,该集合的构造函数需要一个IEnumerable<T>的字符串集合,
        例如IList<T>或Queue<T>。
        
        该方法使用UTF8作为编码值。
    
    例子:获取指定目录(含子目录)下所有txt文件中含"x"字符的行。

    private static void Main(string[] args)
    {
        try
        {
            string docPath = @"E:\BaiduNetdiskDownload";
            var files = from file in Directory.EnumerateFiles(docPath, "*.txt", SearchOption.AllDirectories)
                        from line in File.ReadLines(file)
                        where line.Contains("x")
                        select new
                        {
                            File = file,
                            Line = line
                        };
            foreach (var f in files)
            {
                Console.WriteLine($"{f.File}\t{f.Line}");
            }
            Console.WriteLine($"{files.Count().ToString()} files found.");
        }
        catch (UnauthorizedAccessException uAEx)
        {
            Console.WriteLine(uAEx.Message);
        }
        catch (PathTooLongException pathEx)
        {
            Console.WriteLine(pathEx.Message);
        }
        Console.ReadKey();
    }


        
        
        
    
    4、Directory类
    
        创建文件夹
        
        Directory.CreateDirectory(string path) 创建文件夹.末尾与是否有\无关.
        
        Directory.Delete(string path)  删除文件夹。
            1)无该文件夹时,会异常。用下面判断,
                Directory.Exists(string path)  //存在目录为真,否则为假
            2)该文件夹内有文件或子目录时会异常。确实要删除。用下面第二参数为真:
                Directory.Delete(string path, bool recursive)
                
        Directory.Move(string src,string des) 将源目录(含文件及子目录)移动到目标目录
        
        Directory.GetFiles(string p[,string sPattern[,SearchOption sOption]])
            返回指定目录中的文件(含路径)的数组。
            1)第二参数指定过滤。例如:"*.txt"指明返回文本文件.
            2)第三参数指定是否包含子目录文件文件,否则仅当前目录。
        
            指定过滤的通配符SearchPattern为:
            *(星号) 该位置零个或多个字符。
            ?(问题) 该位置恰好是一个字符。
            
            注意路径path不区别大小写.
            
            
        系统文件夹的指定:
            获取我的文档目录。参数为枚举,
        string docPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        
        参数Environment.SpecialFolder.MyDocuments,枚举类型,其中Mydocument指
        的是“我的文档”,类似有:
            CommonProgramFilesX86    “Program Files”文件夹。
            Cookies          用作 Internet Cookie 的公共储存库的目录。
            Fonts            包含字体的虚拟文件夹。
            ProgramFilesX86    x86 “Program Files”文件夹。
            Programs       包含用户程序组的目录。
            MyMusic       “我的音乐”文件夹。
            MyPictures    “我的图片”文件夹。
            ...
        方法Environment.GetFolderPath是获取对应目录。
            
            
            
    5、流
        
        FileStream操作的是字节。
        StreamReader与StreamWriter操作的是字符。
            对于字符最好的操作用本类,对于中文,一个字节无法显示什么,但对字符可以
            显示出中文
        
        下面显示6个汉字转为utf-8编码,共18个字节,因此一个字节无法显示什么内容。

        string p = @"E:\4.txt";
        using (FileStream fs = new FileStream(p, FileMode.OpenOrCreate, FileAccess.Write))
        {
            byte[] b = Encoding.UTF8.GetBytes("常练常记常乐");
            fs.Write(b, 0, b.Length);
        }
        using (FileStream fs = new FileStream(p, FileMode.Open, FileAccess.Read))
        {
            byte[] b = new byte[1];
            int n, m = 0;
            do
            {
                n = fs.Read(b, 0, b.Length);
                if (n == 0)
                {
                    break;
                }
                Console.WriteLine(b[0]);
                m++;
            } while (n > 0);
            Console.WriteLine(m);//18
        }


        
        
        
        
        
        Stream把所有内容当成二进制来看待,如果是文本内容,则需要程序员来处理文本和
        二进制之间的转换。
        
        用StreamWriter是辅助Stream进行处理的。
        
        StreamWriter是辅助Stream进行处理的
        

        string p = @"E:\4.txt";
        Stream s = File.OpenRead(p);
        using (StreamReader sr = new StreamReader(s, Encoding.UTF8))
        {
            Console.WriteLine(sr.ReadLine());//第一行
            Console.WriteLine(sr.ReadToEnd());//第二行到最后
        }


        
        由上可知:
        ReadToEnd 从当前位置读到最后。内容大的话会占内存,每次调用都往下走,不能
                无意中调用两次。
        ReadLine 读取一行。如果到了末尾,返回null
        
        注意:文件流有一个游标,表示当前在流的位置,当读取一次,游标对应移动。
        
        创建流的几种方法:file有6种,还有:
        
        
    练习:对职工工资文件处理,所有人工资加倍.
        原文件:
        马大哈|3000
        宋江|8000
        韩立|9000
        白话魔法师|4000

        
        string p = @"E:\4.txt";
        using (StreamWriter sw = new StreamWriter(File.OpenWrite(p), Encoding.UTF8))
        {
            sw.WriteLine("马大哈|3000");
            sw.WriteLine("宋江|8000");
            sw.WriteLine("韩立|9000");
            sw.WriteLine("白话魔法师|4000");
        }
        string[] s = File.ReadAllLines(p);
        for (int i = 0; i < s.Length; i++)
        {
            if (s[i].Contains("|"))//防止非工资条进入而异常
            {
                string[] s2 = s[i].Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
                s[i] = s2[0] + "|" + int.Parse(s2[1]) * 2;
            }
        }
        File.WriteAllLines(p, s);


        
        注意:流的的写入是覆盖式,但它是内容的覆盖式,不是文件的覆盖式。
            比如原文件长度是30,如果写入长度是20,则覆盖的是内容中前面
            长度20的内容,后面的10个长度内容是原样。
    
    using语句
        1)不是任何类型的对象都可以写在using()的小括号里。
        2)只有实现了IDisposable接口的类型的对象,才能写在using小括号里.
        3)当using{}执行完毕时,会自动调用对象的Dispose()方法来释放资源。
        
  


        
二、对象序列化(二进制序列化)


    对象序列化是将对象(比如Person对象)转换为二进制数据(字节流),反序列化是将二进制
    数据还原为对象。
    
    对象是稍纵即逝的,不仅程序重启、操作系统重启会造成对象的消失,就是退出函数范围
    等都可能造成对象的消失。
    
    序列化/反序列化就是为了保持对象的持久化。就好像用DV录像(序列化)和用播放器(反序列
    化)一样。

    
    
    对象序列化,只能针对对象的字段进行序列化。
    
    BinaryFomatter类有两个方法:
        void Serialize(Stream stream,object graph)   对象graph序列化到stream中
        object.Deserialize(Stream stream) 将对象从stream中反序列化,返回值为反
                                        序列化得到的对象。
    
    练习:将几个int,字符串添加到ArrayList中,然后序列化到文件中,再反序列化回来。
    
    
    不是所有对象都能序列化,只有可序列化的对象才能序列化。
    
    在类添加[Serializeble],对象的属性、字段的类型也必须可序列化。
    
    
    关于二进制序列化需要注意的事项:
    1)要序列化的类型必须标记为:[Serializable]
    2)该类型的父类也必须标记为:[Serializable]
    3)该类型中的所有成员的类型也必须标记为:[Serializable]
    4)序列化只会对类中的字段序列化。(只能序列化一些状态信息)
    
    为什么要序列化?
        将一个复杂的对象转换流,方便我们的存储与信息交换。
    
    
    序列化的步骤:
    1)创建一个二进制序列化器(BinaryFormatter);
    2)创建文件流
    3)bf.Serialize(stream,对象)
    
    反序列化的步骤
    1)创建一个二进制序列化器(BinaryFormatter);
    2)创建文件流;
    3)执行反序列化。object obj=bf.Deserialize(stream);
    注意:反序列化返回类型是object,所以要进行类型转换
    
    练习:序列化一个对象,保存到文件中。再读出到另一个对象中。

    private static void Main(string[] args)
    {
        Student s = new Student();
        s.Name = "韩立"; s.Age = 1000; s.Gender = '男';

        string p = @"E:\4.txt";
        using (FileStream fs = new FileStream(p, FileMode.OpenOrCreate, FileAccess.Write))
        {
            BinaryFormatter bf = new BinaryFormatter();
            bf.Serialize(fs, s);
        }

        Student s1;
        using (FileStream fs = new FileStream(p, FileMode.Open, FileAccess.Read))
        {
            BinaryFormatter bf = new BinaryFormatter();
            s1 = (Student)bf.Deserialize(fs);
        }
        Console.WriteLine(s.Name + "-" + s.Age);
        Console.ReadKey();
    }

    [Serializable]
    public class Student
    {
        public string Name { get; set; }
        public int Age { get; set; }
        public char Gender { get; set; }
    }


三、练习项目:播放器    


    添加Windows Media Player到组件,项目中要使用这个组件。
    
    如何给工具箱添加新的控件或组件:
        建立一个winForm项目,切换到窗体界面,对左侧工具箱的"组件"右击,选择“选择
    项”.稍等几秒,加载完成。切换到"COM组件"标签,向下查找到"Windwos Media 
    Player",选择它.
        这样Windows Media Player组件就被添加到工具箱的“组件”栏中。如果是对"打印"
    右击添加的,则控件或组件会添加到“打印”栏中。已经添加的控件或组件,选中它,左
    键按住不动,拖拽到其它栏松开鼠标,可将控件或组件移动到目标栏中。
        
    Windows Media Player属性
    Url    歌曲的地址或目录
    
    一些方法
    musicPlay.Ctlcontrols 控制相关
    musicPlay.Ctlcontrols.Play()  播放。类似有,Pause()暂停,Stop()停止,Next()下一曲
                                Previous上一曲,PlayItem
    musicPlay.Ctlcontrols.currentItem  类似的有: currentPosition,CurrentPositionString
    
    musicPlay.settings.autoStart 设置控制是否自动开始播放(bool)
            它只能设置在开始,然后有了url才生效。若已经在播放了,该设置失效无用。
    
    listBox可以设置多选。
    listBox1.SelectionMode = SelectionMode.MultiExtended;//多选有两咱,这里用扩展
    多选后,listBox1.SelectedIndex为第一个按下鼠标选择的项。(可能在选定范围的开始
    或者结尾)
    多选的范围的索引在集合listBox1.SelectedIndices中。比如,选定第3项到第8项,那么这
    个集合就是{2,3,4,5,6,7}。如果从上向下拖动选定,则listBox1.SelectedInex为2;如果
    是从下向上拖动选定的上面范围,则listBox1.SelectedIndex为7.
        注意:无论从上往下选择,还是从下向上选择,listBox1.SelectedIndices集合的排列
    顺序都是从小到大。
    
    contextMenuStrip1绑定到相关控件(属性ContextMenuStrip)中,两者产生关联,右击将
    弹出这个菜单。通过contextMenuStrip1.Items[0].Enabled = false;定位设置相关属性。
    
    标签自适应图片,没找到,自己用属性适应。
        思路:让label1属性AutoSize设置为false,即人为手工来设置大小,不自动。
                然后让label1的大小与图片的大小刚好相等。
                label1.Size = label1.Image.Size;
        对于显示图片,没有文字的标签无法判断是什么图片。
        两个或多个图片,可以用label1.tag来设置标签,判断是什么图片。这时可以把tag
        当作是text来对待。
    
    为了统一控制,很多方法可以直接调用控制的事件,比如btnPlay.Click事件,在代码中
    用btnPlay.PerformClick()来代码。其实也可以写成单独的方法,来统一调用,这里直接
    用控件的事件中代码。
    
    播放中的地址musicPlay.Url如果重新赋值,会从头开始播放。因此设置一个标志b(bool)
    只准它使用一次,这样下次点的时候,播放会从暂停处重新开始。这个b会在切换listBox1
    的SelectedIndex时会重新为true,因为这表明歌曲发生更改。
    
    添加的歌曲,会在添加时,同步对应到泛型mList中,它与listBox1的序列一一对应。这
    样选择歌曲时会用索引对应到泛型中,取得真实的歌曲目录。
    
    上一曲和下一曲,注意判断索引的极限进行加或减,然后调用播放事件。
    
    双击时,比较当前歌曲与选中歌曲是否相同。不同则播放;相同则在播放与暂停间切换。
    
    timer1计时器用来判断播放器状态(播放中,停止,其它)从来决定是否自动下一曲
    及显示当前播放的时间。
    
    所有歌曲删除时,所有状态应重置。
    
    显示歌曲:搜索对应的Lrc歌词文件,有则清空原的时间泛型timelist和歌词泛型lrclist
    .将原歌词拆分后有效部分(时间与歌词)添加到上面两个集合中。同时重置索引,开启
    tmer2,逐个与timeList[index]比较,大于就显示歌曲。歌曲分成五句,逐个滚动显示,注
    意,索引的限制范围。
    
    为了显示制作痕迹,一些控件就不重命名了。
    
    源代码地址:   
        https://download.csdn.net/download/dzweather/87610734
        
        由于本人有时也要积分下载,所以上面链接加了积分。
        您也可直接由下面代码自己免费制作。


    界面:
    

 


    歌词格式:
    

    
    代码:

 

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.IO;
    using System.Windows.Forms;

 

    namespace Play
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            private List<string> mlist = new List<string>();//歌曲集合
            private List<double> timeList = new List<double>();//时间集合
            private List<string> lrcList = new List<string>();//歌词集合
            private bool b = true;//是否第一次进入播放。
            private bool bAuto = false;//是否自动播放
            private int index = 0;//当前词条索引

            private void Form1_Load(object sender, EventArgs e)
            {
                //禁止程序开打就播放
                //musicPlay.URL = @"E:\红枣树.mp3";//不能放前面,状态设置不能控制播放。
                musicPlay.settings.autoStart = false;//只能先设置状态,后面才不播放。
                //musicPlay.URL = @"E:\红枣树.mp3";
                //AxWMPLib.AxWindowsMediaPlayer w=new AxWMPLib.AxWindowsMediaPlayer();
                //上句是动态生成组件

                //设置listbox多选属性
                listBox1.SelectionMode = SelectionMode.MultiExtended;
                //播放无效
                btnPlay.Enabled = false;
                lblMute.Image = Image.FromFile(@"E:\放音.jpg");
                lblMute.Size = lblMute.Image.Size;
                lblMute.Tag = "放音";
            }

            private void btnPlay_Click(object sender, EventArgs e)
            {
                if (listBox1.SelectedIndex == -1) //无歌曲
                {
                    MessageBox.Show("未选定歌曲");
                }
                else
                {
                    if (b)
                    {//第一次进入,从头开始播放。否则第二次进入,b为假不需要再用url,继续前面位置播放
                        musicPlay.URL = mlist[listBox1.SelectedIndex];
                        ShowLrc(musicPlay.URL);
                        b = false;
                    }

                    if (btnPlay.Text == "播放")
                    {
                        musicPlay.Ctlcontrols.play();
                        btnPlay.Text = "暂停";
                    }
                    else
                    {
                        musicPlay.Ctlcontrols.pause();
                        btnPlay.Text = "播放";
                    }
                }
            }

            private void ShowLrc(string p)//显示歌词
            {
                //检测歌词文件lrc是否存在
                string f = Path.GetFileNameWithoutExtension(p) + ".lrc";
                f = Path.Combine(Path.GetDirectoryName(p), f);
                if (!File.Exists(f))
                {
                    label1.Text = "";//label1-labe5清空
                    label2.Text = "";
                    label3.Text = "无歌词";
                    label4.Text = "";
                    label5.Text = "";
                    return;
                }

                timeList.Clear();//清空原歌词
                lrcList.Clear();
                string[] s = File.ReadAllLines(f);
                for (int i = 0; i < s.Length; i++)
                {
                    //分割,只要前面三个元素
                    string[] s1 = s[i].Split(new char[] { '[', ']' }, 2, StringSplitOptions.RemoveEmptyEntries);
                    if (s1.Length > 1)//只加时间和歌词部分
                    {
                        double d = TimeSpan.Parse("00:" + s1[0]).TotalSeconds;//取得时长
                        timeList.Add(d);
                        lrcList.Add(s1[1]);
                    }
                }
                index = 0; //重置歌词开始
                timer2.Enabled = true;//激活歌词显示
            }

            private void btnStop_Click(object sender, EventArgs e)//停止播放
            {
                musicPlay.Ctlcontrols.stop();
                btnPlay.Text = "播放";
            }

            private void 删除ToolStripMenuItem_Click(object sender, EventArgs e)//右击删除
            {
                if (listBox1.SelectedItems.Count == 0)
                {
                    //没有选中,不必删除
                }
                else if (listBox1.SelectedItems.Count < listBox1.Items.Count)
                {
                    int min = listBox1.SelectedIndices[0];

                    foreach (int i in listBox1.SelectedIndices)
                    {
                        mlist.RemoveAt(i);
                        listBox1.Items.RemoveAt(i);
                    }
                    listBox1.SelectedIndices.Clear();
                    if (min == 0)
                    {
                        listBox1.SelectedIndex = 0;
                    }
                    else
                    {
                        listBox1.SelectedIndex = min - 1;
                    }
                }
                else//全部删除,停止播放
                {
                    listBox1.Items.Clear();
                    mlist.Clear();

                    timer2.Enabled = false;
                    btnPlay.Text = "播放";

                    bAuto = false;
                    btnAutoNext.Text = "自动播放";

                    lblDuration.Text = "显示播放时长";
                    musicPlay.Ctlcontrols.stop();
                    btnPlay.Enabled = false;
                }
            }

            private void BtnBrowser_Click(object sender, EventArgs e)//添加歌曲
            {
                OpenFileDialog ofd = new OpenFileDialog();
                ofd.InitialDirectory = @"E:\音乐";
                ofd.Filter = "MP3文件|*.mp3|WAV文件|*.wav";
                ofd.Multiselect = true;

                if (ofd.ShowDialog() == DialogResult.OK)
                {
                    string[] f = ofd.FileNames;
                    //listBox1.Items.AddRange(f);//添加到列表
                    //mlist.AddRange(f);//添加到泛型
                    for (int i = 0; i < f.Length; i++)
                    {
                        mlist.Add(f[i]);
                        listBox1.Items.Add(Path.GetFileName(f[i]));
                    }
                    btnPlay.Enabled = true;
                }
            }

            private void listBox1_MouseClick(object sender, MouseEventArgs e)//右击菜单是否生效
            {
                if (e.Button == MouseButtons.Right)//右击
                {
                    if (listBox1.Items.Count == 0)//无歌曲
                    {
                        contextMenuStrip1.Items[0].Enabled = false;//第一项"右击"失效
                    }
                    else
                    {
                        contextMenuStrip1.Items[0].Enabled = true;
                    }
                }
                else if (e.Button == MouseButtons.Left)//单击
                {
                    if (listBox1.SelectedIndex != -1)
                    {//已经由双击做起了播放。实际中双击更人性化,单击播放用户操作不便.
                    }
                }
            }

            private void btnNext_Click(object sender, EventArgs e)//下一曲
            {
                if (listBox1.SelectedIndex == -1)
                {
                    MessageBox.Show("尚未选定歌曲");
                }
                else
                {
                    int index = listBox1.SelectedIndex;
                    index++;
                    if (index == listBox1.Items.Count)
                    {
                        index = 0;
                    }
                    listBox1.SelectedIndices.Clear();
                    listBox1.SelectedIndex = index;

                    btnPlay.Enabled = true;//激活,上面已经先选定item,下面直接调用播放
                    btnPlay.Text = "播放";//关键,不然间隔不播放
                    btnPlay.PerformClick();
                }
            }

            private void btnPre_Click(object sender, EventArgs e)//上一曲
            {
                if (listBox1.SelectedIndex == -1)
                {
                    MessageBox.Show("尚未选定歌曲");
                }
                else
                {
                    int index = listBox1.SelectedIndex;
                    index--;
                    if (index == -1)
                    {
                        index = listBox1.Items.Count - 1;
                    }
                    listBox1.SelectedIndices.Clear();
                    listBox1.SelectedIndex = index;

                    btnPlay.Enabled = true;
                    btnPlay.Text = "播放";
                    btnPlay.PerformClick();
                }
            }

            private void lblMute_Click(object sender, EventArgs e)//静音切换
            {
                if (lblMute.Tag.ToString() == "静音")
                {
                    musicPlay.settings.mute = false;
                    lblMute.Image = Image.FromFile(@"E:\放音.jpg");
                    lblMute.Size = lblMute.Image.Size;
                    lblMute.Tag = "放音";
                }
                else
                {
                    musicPlay.settings.mute = true;
                    lblMute.Image = Image.FromFile(@"E:\静音.jpg");
                    lblMute.Size = lblMute.Image.Size;
                    lblMute.Tag = "静音";
                }
            }

            private void button1_Click(object sender, EventArgs e)//音量加
            {
                musicPlay.settings.volume += 2;
            }

            private void button2_Click(object sender, EventArgs e)//音量减
            {
                musicPlay.settings.volume -= 2;
            }

            private void listBox1_MouseDoubleClick(object sender, MouseEventArgs e)//双击播放
            {
                if (listBox1.SelectedIndex != -1)
                {
                    if (musicPlay.URL != mlist[listBox1.SelectedIndex])
                    {
                        btnPlay.Enabled = true;
                        btnPlay.Text = "播放";
                        btnPlay.PerformClick();
                    }
                    else
                    {
                        b = false;
                        btnPlay.PerformClick();//相同歌曲,双击可在播放也暂停之间切换
                    }
                }
            }

            private void timer1_Tick(object sender, EventArgs e)//自动下一曲
            {
                //if (musicPlay.playState == WMPLib.WMPPlayState.wmppsPlaying)
                //{
                //    // lblDuration.Text = musicPlay.currentMedia.duration.ToString();//总时长184 秒数
                //    //lblDuration.Text = musicPlay.currentMedia.durationString;//总时长03:05  时分秒

                //    //lblDuration.Text = musicPlay.Ctlcontrols.currentPosition.ToString();//播放的时间,秒数
                //    //lblDuration.Text = musicPlay.Ctlcontrols.currentPositionString;//播放的时间,秒数
                //    double d1 = musicPlay.currentMedia.duration;//本身是double不用转
                //    double d2 = musicPlay.Ctlcontrols.currentPosition;//本身是double不用转换
                //    if (d1 - d2 < 1) //因为在转换中四舍五入,两者可能有点差异,也就不到1秒的差异
                //    {
                //        btnNext.PerformClick();
                //    }
                //}
                if (musicPlay.playState == WMPLib.WMPPlayState.wmppsStopped)
                {
                    btnPlay.Text = "播放";//停止时,恢复初始
                    timer2.Enabled = false;//无歌放,歌词也停止

                    if (bAuto) btnNext.PerformClick();
                }
                else if (musicPlay.playState == WMPLib.WMPPlayState.wmppsPlaying)
                {
                    lblDuration.Text = musicPlay.Ctlcontrols.currentPositionString;
                }
                else
                {
                    lblDuration.Text = "显示播放时长";
                }
            }

            private void btnAutoNext_Click(object sender, EventArgs e)//自动开关
            {
                if (listBox1.SelectedIndex == -1)
                {
                    MessageBox.Show("请先选择起始歌曲!");
                    return;
                }
                if (btnAutoNext.Text == "自动播放")
                {
                    bAuto = true;
                    if (musicPlay.playState == WMPLib.WMPPlayState.wmppsStopped || musicPlay.playState == WMPLib.WMPPlayState.wmppsUndefined)
                    {
                        btnPlay.PerformClick();
                    }
                    btnAutoNext.Text = "停止自动";
                }
                else
                {
                    bAuto = false;
                    btnAutoNext.Text = "自动播放";
                }
            }

            private void timer2_Tick(object sender, EventArgs e)//歌曲显示
            {
                if (musicPlay.Ctlcontrols.currentPosition > timeList[index])
                {
                    label3.Text = lrcList[index];
                    if (index > 1) label1.Text = lrcList[index - 2];
                    else label1.Text = "";
                    if (index > 0) label2.Text = lrcList[index - 1];
                    else label1.Text = "";

                    if (index < lrcList.Count - 2) label4.Text = lrcList[index + 1];
                    else label4.Text = "";
                    if (index < lrcList.Count - 3) label5.Text = lrcList[index + 2];
                    else label5.Text = "";

                    if (index < lrcList.Count - 1) index++;//最后一个不再累增
                }
            }

            private void lblMute_Click_1(object sender, EventArgs e)//静音与放音
            {
                if (lblMute.Tag.ToString() == "放音")
                {
                    lblMute.Image = Image.FromFile(@"E:\静音.jpg");
                    lblMute.Size = lblMute.Image.Size;
                    lblMute.Tag = "静音";

                    musicPlay.settings.mute = true;
                }
                else
                {
                    lblMute.Image = Image.FromFile(@"E:\放音.jpg");
                    lblMute.Size = lblMute.Image.Size;
                    lblMute.Tag = "放音";

                    musicPlay.settings.mute = false;
                }
            }

            private void listBox1_SelectedIndexChanged(object sender, EventArgs e)//歌曲切换时重置
            {
                b = true;
            }
        }
    }    
    
    


    

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值