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項目了,這個只是前臺,那么大家也可以寫一個后臺進行管理和維護前臺,通過數據庫就可以
把前臺和后臺連在一起。
新聞熱點
疑難解答