MyKTV项目,走起!
第一部分:这个项目对于新手来说有一点难度,但是当你理清类之间的关系和怎样去实现功能后就会感觉轻松很多。
话不多说,先上类图:
接着是数据库表间关系:
本项目要实现以下功能:
- 明星点歌
- 拼音点歌
- 类型选择
- 金榜排行
- 字数点歌
一共五大块,那么明星点歌下还有一个播放的功能。
在主页面有一个正在播放和下一首的提示功能。
这是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 /// 第三层listView114 /// </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 finally35 {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\恋爱新手.mp310 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 else104 { 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 else143 {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 Song11 { 12 /// <summary>13 /// 歌曲名称14 /// </summary>15 public string SongName16 {17 get { return songName; }18 set { songName = value; }19 }20 21 /// <summary>22 /// 歌曲存放路径23 /// </summary>24 public string SongURL25 {26 get { return songURL; }27 set { songURL = value; }28 }29 30 /// <summary>31 /// 歌曲播放状态32 /// </summary>33 internal SongPlayState PlayState34 {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 SongList10 {11 //歌曲名称12 private string SongName;13 //歌曲路径14 private string SongUl;15 //歌曲状态16 private string SongState;17 18 public string SongState119 {20 get { return SongState; }21 set { SongState = value; }22 }23 24 public string SongUl125 {26 get { return SongUl; }27 set { SongUl = value; }28 }29 30 public string SongName131 {32 get { return SongName; }33 set { SongName = value; }34 }35 36 //把当前的播放状态设置为未播放状态37 private PalySongState playSong = PalySongState.unplayed;38 39 public PalySongState PlaySong40 {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项目了,这个只是前台,那么大家也可以写一个后台进行管理和维护前台,通过数据库就可以
把前台和后台连在一起。