MyKTV项目,走起!

时间:2023-03-09 21:59:43
MyKTV项目,走起!

MyKTV项目,走起!

第一部分:这个项目对于新手来说有一点难度,但是当你理清类之间的关系和怎样去实现功能后就会感觉轻松很多。

话不多说,先上类图:

MyKTV项目,走起!

接着是数据库表间关系:

MyKTV项目,走起!

本项目要实现以下功能:

  1. 明星点歌
  2. 拼音点歌
  3. 类型选择
  4. 金榜排行
  5. 字数点歌

一共五大块,那么明星点歌下还有一个播放的功能。

在主页面有一个正在播放和下一首的提示功能。

这是ktv主页面:

MyKTV项目,走起!

在下边还有重唱,切歌,已点,服务和退出功能

相信大家都去过KTV,所以这些功能就不说了,比我都清楚!

这里我把播放控件放在了主页面,位置随意,放在哪都行,也可以单独开一个窗体进行播放。

点击明星点歌进入到明星点歌页面:

MyKTV项目,走起!

组合,女歌手和男歌手都放在listView中,这里要注意的是在这一个窗体中一共有三个listView,先在窗体中隐藏后两个,

那么点击第一个进入到第二个时要把第一个listView隐藏。

隐藏listView只需把它的Visible属性设置成false就ok了:

             lvCountry.Visible = false;
lvSinger.Visible = false;

第二个listView就是供用户一个更精确的选择歌曲或歌手了:

MyKTV项目,走起!

第三个listView就是显示歌手对应的图片:

MyKTV项目,走起!

这个图片要从数据库中取,不能写死,还有很多功能,像金榜排行,都不能写死。

再次点击就进入播放列表:

MyKTV项目,走起!

刷新歌曲列表代码:

         /// <summary>
/// 刷新歌曲列表
/// </summary>
private void RefreshSongList()
{
lvSongList.Items.Clear(); // 清空原列表
int i = ;
while (PlayList.SongList[i] != null)
{
ListViewItem item = new ListViewItem();
item.Text = PlayList.SongList[i].SongName;
item.Tag = i;
string playState = PlayList.SongList[i].PlayState== SongPlayState.unplayed?"未播放":"已播放";
item.SubItems.Add(playState);
lvSongList.Items.Add(item);
i++;
}
}

明星点歌代码:

      string singertype = "组合";
int singertypid = ;      /// <summary>
/// 第一层listView
/// </summary>
public void LoadSingerArea()
{
if (lvType.SelectedItems[]!=null)
{
lvType.Visible = false;
lvCountry.Visible = true;
lvCountry.Location = lvType.Location;
lvCountry.Dock = DockStyle.Fill;
this.singertype = Convert.ToString(lvType.SelectedItems[].Text);
}
string sql = "select singertype_id,singertype_name from singer_type";
SqlCommand cmd = new SqlCommand(sql,db.Connection );
try
{
db.OpenConnection();
SqlDataReader dr = cmd.ExecuteReader();
lvCountry.Items.Clear();
if (dr.HasRows)
{
int index = ;
while (dr.Read())
{
ListViewItem lvitem = new ListViewItem();
int typeid = Convert.ToInt32(dr["singertype_id"]);
string typename = Convert.ToString(dr["singertype_name"]);
lvitem.Text = typename;
lvitem.Tag = typeid;
lvitem.ImageIndex = index;
lvCountry.Items.Add(lvitem);
index++;
}
}
dr.Close();
}
catch (Exception ex)
{ MessageBox.Show(ex.Message);
}
finally
{
db.CloseConnection();
}
}
/// <summary>
/// 第二层listView
/// </summary>
public void LoadSingerName()
{
if (lvCountry.SelectedItems[]!=null)
{
//隐藏歌手地区,显示歌手的姓名
lvCountry.Visible = false;
lvSinger.Visible = true;
lvSinger.Location = lvCountry.Location;
singertypid = Convert.ToInt32(lvCountry.SelectedItems[].Tag);
StringBuilder sql = new StringBuilder();
string result = singertype;
if (result!="组合")
{
result = singertype == "女歌手" ? "女" : "男";
}
sql.AppendFormat("select singe_id,singer_name,singer_photo_url from Singer_info where singertype_id={0}and singer_gemder='{1}'",singertypid,result);
SqlCommand cmd = new SqlCommand(sql.ToString(), db.Connection);
try
{
db.OpenConnection();
SqlDataReader dr = cmd.ExecuteReader();
int imageIndex = ; //代表歌手头像的索引
imageList1.Images.Clear();
lvSinger.Items.Clear();
if (dr.HasRows)
{
while (dr.Read())
{
string photoURL = KTVUtil.singerPhotoPath + "\\" + Convert.ToString(dr["singer_photo_url"]);
imageList1.Images.Add(Image.FromFile(photoURL));
ListViewItem item = new ListViewItem();
item.Text = Convert.ToString(dr["singer_name"]);
item.Tag = Convert.ToString(dr["singer_id"]);
item.ImageIndex = imageIndex;
lvSinger.Items.Add(item);
imageIndex++;
}
}
dr.Close();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
db.CloseConnection();
}
}
} private void tsplMenu_Click(object sender, EventArgs e)
{
MainForm mf = new MainForm();
mf.Show();
this.Close(); }
/// <summary>
/// 第三层listView
/// </summary>
public void SongList()
{
StringBuilder sb = new StringBuilder();
sb.AppendFormat("select song_id,song_name, singer_name='{0}',song_url from SongInfo,Singer_Info where singer_id={1}",
lvSinger.SelectedItems[].Text, Convert.ToInt32(lvSinger.SelectedItems[].Tag)); SongListForm songList = new SongListForm();
songList.Sql = sb.ToString();
songList.Show();
this.Close();
}

之后一定要在listView的Click事件中调用方法:

      private void lvType_Click(object sender, EventArgs e)
{
LoadSingerArea();
} private void lvSinger_Click(object sender, EventArgs e)
{
SongList();
} private void lvCountry_Click(object sender, EventArgs e)
{
LoadSingerName();
}

播放过程:

当选中某首歌曲后,点击一下,那么就会将各个列的值拼接成一个Song对象,

 Song song=new Song();
song.songName="值";
song.songUrl="地址";

歌曲列表中数据来源于数据库!所以我们要将喜欢的歌曲添加到数据库中!

当我们点击已点的时候就会循环遍历数组,然后每遍历一项,就会创建一个 ListViewItem对象。

刚才忘了说了,每个页面下面的菜单我用的是ToolStrip控件。

接下来是拼音点歌

拼音点歌相对来说就简单多了,就是一个模糊查询,页面如下:

MyKTV项目,走起!

拼音点歌部分代码:

         // 查询歌曲显示在窗体中
private void btnSearch_Click(object sender, EventArgs e)
{
DBHelper dbHelper = new DBHelper();
DataSet dataSet = new DataSet();
StringBuilder sb = new StringBuilder();
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 ");
sb.AppendFormat("where song_name like '%{0}%' or song_ab like '{0}'",this.txtSongName.Text); Console.WriteLine(sb.ToString()); SqlDataAdapter adapter = new SqlDataAdapter(sb.ToString(), dbHelper.Connection); // 清空当前列表
if (dataSet.Tables["songList"] != null)
{
dataSet.Tables["songList"].Clear();
} adapter.Fill(dataSet, "songList");
this.dgvSong.DataSource = dataSet.Tables["songList"];
}

类型点歌:

MyKTV项目,走起!

这个和酷狗里的如下页面功能类似:

MyKTV项目,走起!

点击某一个项进入到相应的歌曲页面,部分代码如下:

  // 窗体加载时,显示歌曲类别
private void OrderBySongTypeForm_Load(object sender, EventArgs e)
{
// 读取歌曲类别
DBHelper dbHelper = new DBHelper();
string sql = "select * from song_type";
try
{
// 查询数据库
SqlCommand command = new SqlCommand(sql, dbHelper.Connection);
dbHelper.OpenConnection();
SqlDataReader reader = command.ExecuteReader(); // 循环将类别读取出来添加到ListView中
this.lvSongType.Items.Clear();
int i = ;
while (reader.Read())
{
ListViewItem item = new ListViewItem();
item.Text = Convert.ToString(reader["songtype_name"]);
item.Tag = Convert.ToInt32(reader["songtype_id"]);
item.ImageIndex = i;
this.lvSongType.Items.Add(item);
i++;
}
reader.Close();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
MessageBox.Show("系统错误,请联系服务人员!"); }
finally
{
dbHelper.CloseConnection();
}
}

金榜排行和字数点歌大家可以尝试着写一下,都不难!字数点歌这里要注意一下:

MyKTV项目,走起!

上边的那12个Label不是拖12个Label控件,而是利用二重数组进行控制Label的:

         for (int i = ; i <= ; i++)//行数
{
for (int j = ; j <= ; j++)
{
Label label = new Label();
label.Text = i+"-"+j;
//自身大小(重点)
label.Size = new Size(, );
//背景颜色
label.BackColor = Color.Yellow;
//相对于窗体0,0点的位置
label.Location = new Point(+*j, +*i);
//文本居中
label.TextAlign = ContentAlignment.MiddleCenter;
//字体大小
label.Font=new Font("Bradley Hand ITC",20); //触发Click事件
label.Click += label_Click;
//让Label对象归属于当前窗体
this.Controls.Add(label);
}
} } void label_MouseMove(object sender, MouseEventArgs e)
{
this.Text = e.X + "," + e.Y;
} void label_Click(object sender, EventArgs e)
{
Label label = (Label)sender; MessageBox.Show(label.Text); } private void Form1_MouseMove(object sender, MouseEventArgs e)
{
this.Text = e.X + "," + e.Y;
}

要记住:每一个控件都是一个类。

第二部分:

部分关键代码如下:

1.重唱:

 // 重新播放当前歌曲
private void tsbtnAgain_Click(object sender, EventArgs e)
{
PlayList.PlayAgain();
}

就是调用PlayList中的PlayAgain()方法。PlayList类我会在下面给出。

2.切歌:

         // 切歌
private void tsbtnCut_Click(object sender, EventArgs e)
{
if (MessageBox.Show("确定要切歌吗?", "操作提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Warning) == DialogResult.OK)
{
int songId = -; // 切歌的编号
if (this.lvSongList.SelectedItems.Count > )
{
songId = Convert.ToInt32(this.lvSongList.SelectedItems[].Tag);
}
PlayList.CutSong(songId);
this.RefreshSongList();
}
}

3.播放:

         private Song song;//当前播放的歌曲
//播放歌曲
private void PlaySong()
{
this.song = PlayList.GetPlaySong();//获取当前播放的歌曲
if (song != null)
{
this.song.SetSongPlayed();//已播放
//D:\song\恋爱新手.mp3
Player1.URL = KTVUtil.songPath + "\\" + this.song.SongURL;//得到当前播放歌曲的路径
txtNext.Text = this.song.SongName;
}
}

4.PlayList类:

 /// <summary>
/// 播放列表管理
/// </summary>
class PlayList
{
private static Song[] songList = new Song[]; // 歌曲播放列表数组
private static int songIndex = ; // 当前播放的歌曲在数组中的索引 /// <summary>
/// 播放列表数组
/// </summary>
public static Song[] SongList
{
get { return PlayList.songList; }
} /// <summary>
/// 当前播放歌曲的索引
/// </summary>
public static int SongIndex
{
get { return PlayList.songIndex; }
} /// <summary>
/// 当前播放的歌曲名称
/// </summary>
/// <returns>歌曲名称</returns>
public static string PlayingSongName()
{
string songName = ""; // 歌曲名称
if (SongList[SongIndex] != null)
{
songName = SongList[SongIndex].SongName;
} return songName;
} /// <summary>
/// 获取当前播放的歌曲
/// </summary>
/// <returns>当前要播放的歌曲</returns>
public static Song GetPlayingSong()
{
if (SongList[songIndex] != null)
{
return SongList[songIndex];
}
else
{
return null;
}
} /// <summary>
/// 下一首要播放的歌曲名称
/// </summary>
/// <returns>歌曲名称</returns>
public static string NextSongName()
{
string songName = ""; // 歌曲名称
if (SongList[SongIndex+] != null)
{
songName = SongList[SongIndex+].SongName;
} return songName;
} /// <summary>
/// 点播一首歌曲
/// </summary>
/// <param name="song">新点播的歌曲</param>
public static bool AddSong(Song song)
{
bool success = false;
for (int i = ; i < SongList.Length; i++)
{
if (SongList[i] == null)
{
SongList[i] = song;
Console.WriteLine(song.SongName);
success = true;
break;
}
} return success;
} /// <summary>
/// 切歌
/// </summary>
/// <param name="index">要切歌曲的编号,如果是切当前播放的歌曲传入-1</param>
public static void CutSong(int index)
{
int i; // 循环变量,代表切歌的位置
if (index == -)
{
i = SongIndex;
}
else
{
i = index; // 从切歌的位置开始,将歌曲逐个向前移一个位置
} SongList[i].SetSongCut();
while (SongList[i] != null)
{
SongList[i] = SongList[i + ];
i++; // 如果到达数组最后一个元素,就将最后一个元素指向空
if (i == SongList.Length)
{
SongList[i] = null;
}
}
} /// <summary>
/// 重放当前歌曲
/// </summary>
public static void PlayAgain()
{
if (SongList[songIndex] != null)
{
SongList[songIndex].SetPlayAgain();
}
} /// <summary>
/// 播放下一首
/// </summary>
public static void MoveOn()
{
if (SongList[songIndex] != null && SongList[songIndex].PlayState == SongPlayState.again)
{
SongList[songIndex].SetSongPlayed();
}
else
{
songIndex++;
}
}
}

5.Song类:

     enum SongPlayState
{
unplayed,played,again,cut
} /// <summary>
/// 歌曲类
/// </summary>
class Song
{
/// <summary>
/// 歌曲名称
/// </summary>
public string SongName
{
get { return songName; }
set { songName = value; }
} /// <summary>
/// 歌曲存放路径
/// </summary>
public string SongURL
{
get { return songURL; }
set { songURL = value; }
} /// <summary>
/// 歌曲播放状态
/// </summary>
internal SongPlayState PlayState
{
get { return playState; }
set { playState = value; }
} private string songName;
private string songURL;
private SongPlayState playState = SongPlayState.unplayed; // 歌曲播放状态 /// <summary>
/// 将歌曲状态改为已播放
/// </summary>
public void SetSongPlayed()
{
this.playState = SongPlayState.played;
} /// <summary>
/// 将歌曲状态改为再拨放一次
/// </summary>
public void SetPlayAgain()
{
this.playState = SongPlayState.again;
} /// <summary>
/// 将歌曲状态改为切歌
/// </summary>
public void SetSongCut()
{
this.playState = SongPlayState.cut;
}
}

6.KTVUtil类:

这里主要存的就是路径

         public static string singerPhotoPath = "";  // 歌手照片路径
public static string songPath = ""; // 歌曲路径

7.SongList类:

 public enum PalySongState
{
//未播放 , 播放, 重播,切歌
unplayed,played,again,cut
}
/// <summary>
/// 歌曲播放类
/// </summary>
public class SongList
{
//歌曲名称
private string SongName;
//歌曲路径
private string SongUl;
//歌曲状态
private string SongState; public string SongState1
{
get { return SongState; }
set { SongState = value; }
} public string SongUl1
{
get { return SongUl; }
set { SongUl = value; }
} public string SongName1
{
get { return SongName; }
set { SongName = value; }
} //把当前的播放状态设置为未播放状态
private PalySongState playSong = PalySongState.unplayed; public PalySongState PlaySong
{
get { return playSong; }
set { playSong = value; }
}
/// <summary>
/// 将未播放状态改为播放状态
/// </summary>
public void PalyState()
{
this.PlaySong = PalySongState.played;
}
/// <summary>
/// 将歌曲重新播放
/// </summary>
public void AgainState()
{
this.PlaySong = PalySongState.again;
}
/// <summary>
/// 切歌状态
/// </summary>
public void CutState()
{
this.PlaySong = PalySongState.cut;
}
}

那么以上就是本次的KTV项目了,这个只是前台,那么大家也可以写一个后台进行管理和维护前台,通过数据库就可以

把前台和后台连在一起。