第一部分:这个项目对于新手来说有一点难度,但是当你理清类之间的关系和怎样去实现功能后就会感觉轻松很多。
话不多说,先上类图:
接着是数据库表间关系:
本项目要实现以下功能:
- 明星点歌
- 拼音点歌
- 类型选择
- 金榜排行
- 字数点歌
一共五大块,那么明星点歌下还有一个播放的功能。
在主页面有一个正在播放和下一首的提示功能。
这是ktv主页面:
在下边还有重唱,切歌,已点,服务和退出功能
相信大家都去过KTV,所以这些功能就不说了,比我都清楚!
这里我把播放控件放在了主页面,位置随意,放在哪都行,也可以单独开一个窗体进行播放。
点击明星点歌进入到明星点歌页面:
组合,女歌手和男歌手都放在listView中,这里要注意的是在这一个窗体中一共有三个listView,先在窗体中隐藏后两个,
那么点击第一个进入到第二个时要把第一个listView隐藏。
隐藏listView只需把它的Visible属性设置成false就ok了:
1 lvCountry.Visible = false; 2 lvSinger.Visible = false;
第二个listView就是供用户一个更精确的选择歌曲或歌手了:
第三个listView就是显示歌手对应的图片:
这个图片要从数据库中取,不能写死,还有很多功能,像金榜排行,都不能写死。
再次点击就进入播放列表:
刷新歌曲列表代码:
1 /// <summary> 2 /// 刷新歌曲列表 3 /// </summary> 4 private void RefreshSongList() 5 { 6 lvSongList.Items.Clear(); // 清空原列表 7 int i = 0; 8 while (PlayList.SongList[i] != null) 9 { 10 ListViewItem item = new ListViewItem(); 11 item.Text = PlayList.SongList[i].SongName; 12 item.Tag = i; 13 string playState = PlayList.SongList[i].PlayState== SongPlayState.unplayed?"未播放":"已播放"; 14 item.SubItems.Add(playState); 15 lvSongList.Items.Add(item); 16 i++; 17 } 18 }
明星点歌代码:
1 string singertype = "组合"; 2 int singertypid = 0; 3 4 /// <summary> 5 /// 第一层listView 6 /// </summary> 7 public void LoadSingerArea() 8 { 9 if (lvType.SelectedItems[0]!=null) 10 { 11 lvType.Visible = false; 12 lvCountry.Visible = true; 13 lvCountry.Location = lvType.Location; 14 lvCountry.Dock = DockStyle.Fill; 15 this.singertype = Convert.ToString(lvType.SelectedItems[0].Text); 16 } 17 string sql = "select singertype_id,singertype_name from singer_type"; 18 SqlCommand cmd = new SqlCommand(sql,db.Connection ); 19 try 20 { 21 db.OpenConnection(); 22 SqlDataReader dr = cmd.ExecuteReader(); 23 lvCountry.Items.Clear(); 24 if (dr.HasRows) 25 { 26 int index = 0; 27 while (dr.Read()) 28 { 29 ListViewItem lvitem = new ListViewItem(); 30 int typeid = Convert.ToInt32(dr["singertype_id"]); 31 string typename = Convert.ToString(dr["singertype_name"]); 32 lvitem.Text = typename; 33 lvitem.Tag = typeid; 34 lvitem.ImageIndex = index; 35 lvCountry.Items.Add(lvitem); 36 index++; 37 } 38 } 39 dr.Close(); 40 } 41 catch (Exception ex) 42 { 43 44 MessageBox.Show(ex.Message); 45 } 46 finally 47 { 48 db.CloseConnection(); 49 } 50 } 51 /// <summary> 52 /// 第二层listView 53 /// </summary> 54 public void LoadSingerName() 55 { 56 if (lvCountry.SelectedItems[0]!=null) 57 { 58 //隐藏歌手地区,显示歌手的姓名 59 lvCountry.Visible = false; 60 lvSinger.Visible = true; 61 lvSinger.Location = lvCountry.Location; 62 singertypid = Convert.ToInt32(lvCountry.SelectedItems[0].Tag); 63 StringBuilder sql = new StringBuilder(); 64 string result = singertype; 65 if (result!="组合") 66 { 67 result = singertype == "女歌手" ? "女" : "男"; 68 } 69 sql.AppendFormat("select singe_id,singer_name,singer_photo_url from Singer_info where singertype_id={0}and singer_gemder=‘{1}‘",singertypid,result); 70 SqlCommand cmd = new SqlCommand(sql.ToString(), db.Connection); 71 try 72 { 73 db.OpenConnection(); 74 SqlDataReader dr = cmd.ExecuteReader(); 75 int imageIndex = 0; //代表歌手头像的索引 76 imageList1.Images.Clear(); 77 lvSinger.Items.Clear(); 78 if (dr.HasRows) 79 { 80 while (dr.Read()) 81 { 82 string photoURL = KTVUtil.singerPhotoPath + "\\" + Convert.ToString(dr["singer_photo_url"]); 83 imageList1.Images.Add(Image.FromFile(photoURL)); 84 ListViewItem item = new ListViewItem(); 85 item.Text = Convert.ToString(dr["singer_name"]); 86 item.Tag = Convert.ToString(dr["singer_id"]); 87 item.ImageIndex = imageIndex; 88 lvSinger.Items.Add(item); 89 imageIndex++; 90 } 91 } 92 dr.Close(); 93 } 94 catch (Exception ex) 95 { 96 MessageBox.Show(ex.Message); 97 } 98 finally 99 { 100 db.CloseConnection(); 101 } 102 } 103 } 104 105 private void tsplMenu_Click(object sender, EventArgs e) 106 { 107 MainForm mf = new MainForm(); 108 mf.Show(); 109 this.Close(); 110 111 } 112 /// <summary> 113 /// 第三层listView 114 /// </summary> 115 public void SongList() 116 { 117 StringBuilder sb = new StringBuilder(); 118 sb.AppendFormat("select song_id,song_name, singer_name=‘{0}‘,song_url from SongInfo,Singer_Info where singer_id={1}", 119 lvSinger.SelectedItems[0].Text, Convert.ToInt32(lvSinger.SelectedItems[0].Tag)); 120 121 SongListForm songList = new SongListForm(); 122 songList.Sql = sb.ToString(); 123 songList.Show(); 124 this.Close(); 125 }
之后一定要在listView的Click事件中调用方法:
1 private void lvType_Click(object sender, EventArgs e) 2 { 3 LoadSingerArea(); 4 } 5 6 private void lvSinger_Click(object sender, EventArgs e) 7 { 8 SongList(); 9 } 10 11 private void lvCountry_Click(object sender, EventArgs e) 12 { 13 LoadSingerName(); 14 }
播放过程:
当选中某首歌曲后,点击一下,那么就会将各个列的值拼接成一个Song对象,
1 Song song=new Song(); 2 song.songName="值"; 3 song.songUrl="地址";
歌曲列表中数据来源于数据库!所以我们要将喜欢的歌曲添加到数据库中!
当我们点击已点的时候就会循环遍历数组,然后每遍历一项,就会创建一个 ListViewItem对象。
刚才忘了说了,每个页面下面的菜单我用的是ToolStrip控件。
接下来是拼音点歌。
拼音点歌相对来说就简单多了,就是一个模糊查询,页面如下:
拼音点歌部分代码:
1 // 查询歌曲显示在窗体中 2 private void btnSearch_Click(object sender, EventArgs e) 3 { 4 DBHelper dbHelper = new DBHelper(); 5 DataSet dataSet = new DataSet(); 6 StringBuilder sb = new StringBuilder(); 7 sb.Append("select song_id,song_name,singer_name,song_url from song_info inner join singer_info on singer_info.singer_id=song_info.singer_id "); 8 sb.AppendFormat("where song_name like ‘%{0}%‘ or song_ab like ‘{0}‘",this.txtSongName.Text); 9 10 Console.WriteLine(sb.ToString()); 11 12 SqlDataAdapter adapter = new SqlDataAdapter(sb.ToString(), dbHelper.Connection); 13 14 // 清空当前列表 15 if (dataSet.Tables["songList"] != null) 16 { 17 dataSet.Tables["songList"].Clear(); 18 } 19 20 adapter.Fill(dataSet, "songList"); 21 this.dgvSong.DataSource = dataSet.Tables["songList"]; 22 }
类型点歌:
这个和酷狗里的如下页面功能类似:
点击某一个项进入到相应的歌曲页面,部分代码如下:
1 // 窗体加载时,显示歌曲类别 2 private void OrderBySongTypeForm_Load(object sender, EventArgs e) 3 { 4 // 读取歌曲类别 5 DBHelper dbHelper = new DBHelper(); 6 string sql = "select * from song_type"; 7 try 8 { 9 // 查询数据库 10 SqlCommand command = new SqlCommand(sql, dbHelper.Connection); 11 dbHelper.OpenConnection(); 12 SqlDataReader reader = command.ExecuteReader(); 13 14 // 循环将类别读取出来添加到ListView中 15 this.lvSongType.Items.Clear(); 16 int i = 0; 17 while (reader.Read()) 18 { 19 ListViewItem item = new ListViewItem(); 20 item.Text = Convert.ToString(reader["songtype_name"]); 21 item.Tag = Convert.ToInt32(reader["songtype_id"]); 22 item.ImageIndex = i; 23 this.lvSongType.Items.Add(item); 24 i++; 25 } 26 reader.Close(); 27 } 28 catch (Exception ex) 29 { 30 Console.WriteLine(ex.Message); 31 MessageBox.Show("系统错误,请联系服务人员!"); 32 33 } 34 finally 35 { 36 dbHelper.CloseConnection(); 37 } 38 }
金榜排行和字数点歌大家可以尝试着写一下,都不难!字数点歌这里要注意一下:
上边的那12个Label不是拖12个Label控件,而是利用二重数组进行控制Label的:
1 for (int i = 1; i <= 5; i++)//行数 2 { 3 for (int j = 1; j <= 5; j++) 4 { 5 Label label = new Label(); 6 label.Text = i+"-"+j; 7 //自身大小(重点) 8 label.Size = new Size(80, 50); 9 //背景颜色 10 label.BackColor = Color.Yellow; 11 //相对于窗体0,0点的位置 12 label.Location = new Point(20+100*j, 20+80*i); 13 //文本居中 14 label.TextAlign = ContentAlignment.MiddleCenter; 15 //字体大小 16 label.Font=new Font("Bradley Hand ITC",20);17 //触发Click事件 18 label.Click += label_Click; 19 20 //让Label对象归属于当前窗体 21 this.Controls.Add(label); 22 } 23 } 24 25 } 26 27 void label_MouseMove(object sender, MouseEventArgs e) 28 { 29 this.Text = e.X + "," + e.Y; 30 } 31 32 void label_Click(object sender, EventArgs e) 33 { 34 35 Label label = (Label)sender;36 MessageBox.Show(label.Text);37 38 39 } 40 41 private void Form1_MouseMove(object sender, MouseEventArgs e) 42 { 43 this.Text = e.X + "," + e.Y; 44 }
要记住:每一个控件都是一个类。
第二部分:
部分关键代码如下:
1.重唱:
1 // 重新播放当前歌曲 2 private void tsbtnAgain_Click(object sender, EventArgs e) 3 { 4 PlayList.PlayAgain(); 5 }
就是调用PlayList中的PlayAgain()方法。PlayList类我会在下面给出。
2.切歌:
1 // 切歌 2 private void tsbtnCut_Click(object sender, EventArgs e) 3 { 4 if (MessageBox.Show("确定要切歌吗?", "操作提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK) 5 { 6 int songId = -1; // 切歌的编号 7 if (this.lvSongList.SelectedItems.Count > 0) 8 { 9 songId = Convert.ToInt32(this.lvSongList.SelectedItems[0].Tag); 10 } 11 PlayList.CutSong(songId); 12 this.RefreshSongList(); 13 } 14 }
3.播放:
1 private Song song;//当前播放的歌曲 2 //播放歌曲 3 private void PlaySong() 4 { 5 this.song = PlayList.GetPlaySong();//获取当前播放的歌曲 6 if (song != null) 7 { 8 this.song.SetSongPlayed();//已播放 9 //D:\song\恋爱新手.mp3 10 Player1.URL = KTVUtil.songPath + "\\" + this.song.SongURL;//得到当前播放歌曲的路径 11 txtNext.Text = this.song.SongName; 12 } 13 }
4.PlayList类:
1 /// <summary> 2 /// 播放列表管理 3 /// </summary> 4 class PlayList 5 { 6 private static Song[] songList = new Song[50]; // 歌曲播放列表数组 7 private static int songIndex = 0; // 当前播放的歌曲在数组中的索引 8 9 /// <summary> 10 /// 播放列表数组 11 /// </summary> 12 public static Song[] SongList 13 { 14 get { return PlayList.songList; } 15 } 16 17 /// <summary> 18 /// 当前播放歌曲的索引 19 /// </summary> 20 public static int SongIndex 21 { 22 get { return PlayList.songIndex; } 23 } 24 25 /// <summary> 26 /// 当前播放的歌曲名称 27 /// </summary> 28 /// <returns>歌曲名称</returns> 29 public static string PlayingSongName() 30 { 31 string songName = ""; // 歌曲名称 32 if (SongList[SongIndex] != null) 33 { 34 songName = SongList[SongIndex].SongName; 35 } 36 37 return songName; 38 } 39 40 /// <summary> 41 /// 获取当前播放的歌曲 42 /// </summary> 43 /// <returns>当前要播放的歌曲</returns> 44 public static Song GetPlayingSong() 45 { 46 if (SongList[songIndex] != null) 47 { 48 return SongList[songIndex]; 49 } 50 else 51 { 52 return null; 53 } 54 } 55 56 /// <summary> 57 /// 下一首要播放的歌曲名称 58 /// </summary> 59 /// <returns>歌曲名称</returns> 60 public static string NextSongName() 61 { 62 string songName = ""; // 歌曲名称 63 if (SongList[SongIndex+1] != null) 64 { 65 songName = SongList[SongIndex+1].SongName; 66 } 67 68 return songName; 69 } 70 71 /// <summary> 72 /// 点播一首歌曲 73 /// </summary> 74 /// <param name="song">新点播的歌曲</param> 75 public static bool AddSong(Song song) 76 { 77 bool success = false; 78 for (int i = 0; i < SongList.Length; i++) 79 { 80 if (SongList[i] == null) 81 { 82 SongList[i] = song; 83 Console.WriteLine(song.SongName); 84 success = true; 85 break; 86 } 87 } 88 89 return success; 90 } 91 92 /// <summary> 93 /// 切歌 94 /// </summary> 95 /// <param name="index">要切歌曲的编号,如果是切当前播放的歌曲传入-1</param> 96 public static void CutSong(int index) 97 { 98 int i; // 循环变量,代表切歌的位置 99 if (index == -1) 100 { 101 i = SongIndex; 102 } 103 else 104 { 105 i = index; // 从切歌的位置开始,将歌曲逐个向前移一个位置 106 } 107 108 SongList[i].SetSongCut(); 109 while (SongList[i] != null) 110 { 111 SongList[i] = SongList[i + 1]; 112 i++; 113 114 // 如果到达数组最后一个元素,就将最后一个元素指向空 115 if (i == SongList.Length) 116 { 117 SongList[i] = null; 118 } 119 } 120 } 121 122 /// <summary> 123 /// 重放当前歌曲 124 /// </summary> 125 public static void PlayAgain() 126 { 127 if (SongList[songIndex] != null) 128 { 129 SongList[songIndex].SetPlayAgain(); 130 } 131 } 132 133 /// <summary> 134 /// 播放下一首 135 /// </summary> 136 public static void MoveOn() 137 { 138 if (SongList[songIndex] != null && SongList[songIndex].PlayState == SongPlayState.again) 139 { 140 SongList[songIndex].SetSongPlayed(); 141 } 142 else 143 { 144 songIndex++; 145 } 146 } 147 }
5.Song类:
1 enum SongPlayState 2 { 3 unplayed,played,again,cut 4 } 5 6 7 /// <summary> 8 /// 歌曲类 9 /// </summary> 10 class Song 11 { 12 /// <summary> 13 /// 歌曲名称 14 /// </summary> 15 public string SongName 16 { 17 get { return songName; } 18 set { songName = value; } 19 } 20 21 /// <summary> 22 /// 歌曲存放路径 23 /// </summary> 24 public string SongURL 25 { 26 get { return songURL; } 27 set { songURL = value; } 28 } 29 30 /// <summary> 31 /// 歌曲播放状态 32 /// </summary> 33 internal SongPlayState PlayState 34 { 35 get { return playState; } 36 set { playState = value; } 37 } 38 39 private string songName; 40 private string songURL; 41 private SongPlayState playState = SongPlayState.unplayed; // 歌曲播放状态 42 43 44 /// <summary> 45 /// 将歌曲状态改为已播放 46 /// </summary> 47 public void SetSongPlayed() 48 { 49 this.playState = SongPlayState.played; 50 } 51 52 /// <summary> 53 /// 将歌曲状态改为再拨放一次 54 /// </summary> 55 public void SetPlayAgain() 56 { 57 this.playState = SongPlayState.again; 58 } 59 60 /// <summary> 61 /// 将歌曲状态改为切歌 62 /// </summary> 63 public void SetSongCut() 64 { 65 this.playState = SongPlayState.cut; 66 } 67 }
6.KTVUtil类:
这里主要存的就是路径
1 public static string singerPhotoPath = ""; // 歌手照片路径 2 public static string songPath = ""; // 歌曲路径
7.SongList类:
1 public enum PalySongState 2 { 3 //未播放 , 播放, 重播,切歌 4 unplayed,played,again,cut 5 } 6 /// <summary> 7 /// 歌曲播放类 8 /// </summary> 9 public class SongList 10 { 11 //歌曲名称 12 private string SongName; 13 //歌曲路径 14 private string SongUl; 15 //歌曲状态 16 private string SongState; 17 18 public string SongState1 19 { 20 get { return SongState; } 21 set { SongState = value; } 22 } 23 24 public string SongUl1 25 { 26 get { return SongUl; } 27 set { SongUl = value; } 28 } 29 30 public string SongName1 31 { 32 get { return SongName; } 33 set { SongName = value; } 34 } 35 36 //把当前的播放状态设置为未播放状态 37 private PalySongState playSong = PalySongState.unplayed; 38 39 public PalySongState PlaySong 40 { 41 get { return playSong; } 42 set { playSong = value; } 43 } 44 /// <summary> 45 /// 将未播放状态改为播放状态 46 /// </summary> 47 public void PalyState() 48 { 49 this.PlaySong = PalySongState.played; 50 } 51 /// <summary> 52 /// 将歌曲重新播放 53 /// </summary> 54 public void AgainState() 55 { 56 this.PlaySong = PalySongState.again; 57 } 58 /// <summary> 59 /// 切歌状态 60 /// </summary> 61 public void CutState() 62 { 63 this.PlaySong = PalySongState.cut; 64 } 65 }
那么以上就是本次的KTV项目了,这个只是前台,那么大家也可以写一个后台进行管理和维护前台,通过数据库就可以
把前台和后台连在一起。