这个影院售票的项目比较的复杂,共需要九个类.
No.1首先第一个类,座位类。该项目的座位默认为黄色,被售出的状态下默认为红色。需要导入命名空间 Using.System.Drawing.Color
public class Seat { public Color Color { get; set; }//座位颜色 public string Num { get; set; }//座位号 public Seat() { } public Seat(Color color,string num) { this.Color = color; this.Num = num; } }
No.2电影类,没啥说的。按照面向对象的思想封装一堆属性。
[Serializable]//可序列化的标识! public class Movie { public string Name { get; set; } public string DaoYan { get; set; } public string Star { get; set; } public string MovieType { get; set; } public string Url { get; set; } public int Price { get; set; } public Movie() { } public Movie(string name,string daoyan,string star,string type,string url,int price) { this.Name = name; this.DaoYan = daoyan; this.Star = star; this.MovieType = type; this.Url = url; this.Price = price; } }
No.3放映计划类,保存每天放映计划的集合,有一个解析XML文件的方法!
[Serializable]//可序列化标识 public class Schedule { public Dictionary<string, ScheduleItems> Items { get; set; } public Schedule() { this.Items = new Dictionary<string, ScheduleItems>(); } public Schedule(Dictionary<string, ScheduleItems> item) { this.Items = item; } public void Show() { XmlDocument myxml = new XmlDocument(); myxml.Load("ShowList.xml"); XmlNode root = myxml.DocumentElement; foreach (XmlNode item in root.ChildNodes) { Movie movie = new Movie(); foreach (XmlNode child in item.ChildNodes) { switch (child.Name) { case "Name": movie.Name = child.InnerText; break; case "Poster": movie.Url = child.InnerText; break; case "Director": movie.Star = child.InnerText; break; case "Actor": movie.DaoYan = child.InnerText; break; case "Price": movie.Price = Convert.ToInt32(child.InnerText); break; case "Type": movie.MovieType = child.InnerText; break; case "Schedule": foreach (XmlNode childs in child.ChildNodes)//解析并赋值 { ScheduleItems sch = new ScheduleItems(movie, childs.InnerText); this.Items.Add(childs.InnerText, sch); } break; } } } }
No.4影院每天放映计划的场次,保存每场电影的信息,所以有一个Movie类型的movie属性
[Serializable] public class ScheduleItems { public Movie Movie { get; set; } public string Time { get; set; } public ScheduleItems(Movie movie,string time) { this.Movie = movie; this.Time = time; } public ScheduleItems() { } }
No.5电影票父类,保存电影票的信息和放映场次,里面有2个虚方法,用于计算价格,打印电影票.
[Serializable] public class Tickey { public ScheduleItems Item { get; set; } public Seat Seat{ get; set; } public int Price { get; set; } public virtual int CalcPrice() { int money = this.Item.Movie.Price; return money; } public Tickey() { } public Tickey(ScheduleItems item,Seat seat) { this.Item = item; this.Seat = seat; this.Price = CalcPrice(); } public virtual void Print() { string path = (this.Item.Time + " " + this.Seat.Num).Replace(":","-") +".txt";//文件名 MessageBox.Show(path); FileStream fs = new FileStream(path, FileMode.Create); StreamWriter sw = new StreamWriter(fs, Encoding.Default); sw.WriteLine("*******************************"); sw.WriteLine(" 青鸟影院 "); sw.WriteLine("-------------------------------"); sw.WriteLine("电影名:" + Item.Movie.Name); sw.WriteLine("时间:" + this.Item.Time); sw.WriteLine("座位号:" + this.Seat.Num); sw.WriteLine("价格:" + this.Price); sw.WriteLine("*******************************"); sw.Close(); fs.Close(); } }
No.6学生票子类,继承电影票类,保存特殊的信息(折扣),并且重写父类的虚方法
public int DicCount { get; set; } public override int CalcPrice() { int money = this.Item.Movie.Price*DicCount/10; return money; } public StudentTickey() { } public StudentTickey(ScheduleItems Item, Seat seat, int discount) : base(Item, seat) { this.DicCount = discount; this.Price = CalcPrice(); } public override void Print() { string path = (this.Item.Time + " " + this.Seat.Num).Replace(":", "-") + ".txt";//文件名 FileStream fs = new FileStream(path, FileMode.Create); StreamWriter sw = new StreamWriter(fs, Encoding.Default); sw.WriteLine("*******************************"); sw.WriteLine(" 青鸟影院 "); sw.WriteLine("-------------------------------"); sw.WriteLine("电影名:" + Item.Movie.Name); sw.WriteLine("时间:" + this.Item.Time); sw.WriteLine("座位号:" + this.Seat.Num); sw.WriteLine("价格:" + this.Price); sw.WriteLine("*******************************"); sw.Close(); fs.Close(); } }
No.7免费票子类,获取获赠者的名字,重写父类的虚方法,将计算价格方法的返回值改为0
public class FreeTickey:Tickey { public string FreeName { get; set; } public override int CalcPrice() { int money = 0; return money; } public FreeTickey() { } public FreeTickey(ScheduleItems Item, Seat seat,string name) : base(Item, seat) { this.FreeName = name; this.Price = CalcPrice(); } public override void Print() { string path = (this.Item.Time + " " + this.Seat.Num).Replace(":", "-") + ".txt";//文件名 FileStream fs = new FileStream(path, FileMode.Create); StreamWriter sw = new StreamWriter(fs, Encoding.Default); sw.WriteLine("*******************************"); sw.WriteLine(" 青鸟影院 "); sw.WriteLine("-------------------------------"); sw.WriteLine("电影名:" + Item.Movie.Name); sw.WriteLine("时间:" + this.Item.Time); sw.WriteLine("座位号:" + this.Seat.Num); sw.WriteLine("价格:" + this.Price); sw.WriteLine("*******************************"); sw.Close(); fs.Close(); } }
No.8电影院类,可以看作是以上几个类的集合。定义座位集合Dictionary<string,Seat>;放映计划和已售出票的集合list<Tickey>
[Serializable] public class Cinema { public Dictionary<string,Seat> Seats { get; set; } public Schedule Dule { get; set; } public List<Tickey> SoldTickeys { get; set; } public Cinema() { this.Seats = new Dictionary<string, Seat>(); this.SoldTickeys = new List<Tickey>(); this.Dule = new Schedule(); }
接下来我们要研究一下Form窗体中的代码了!!!!
1:首先:初始化TreeView树状图中的电影信息及播放场次
public void Show() { cinema.Dule.Show(); TreeNode root = null; foreach (ScheduleItems item in cinema.Dule.Items.Values) { if (root == null || root.Text != item.Movie.Name) { root = new TreeNode(item.Movie.Name); this.TvList.Nodes.Add(root); } root.Nodes.Add(item.Time); } }
通过调用在放映计划类中的(Schedule)中写好的解析XML文档的方法解析。为什么要使用影院类的对象来调用这个方法呢?因为在cinema中封装好了3个自定义类型的字段,可以直接通过cinema对象调用,避免了多次创建其他类的对象,统一调配。 树状图以电影名为父节点,播放时间为子节点,首先创建出父节点对象,遍历播放时间集合,做出一道判断,将电影名放到父节点上,则将播放时间赋给子节点。
2.展示电影和电影票的信息,在树状结构被选中的事件上。
private void treeView1_AfterSelect(object sender, TreeViewEventArgs e) { TreeNode node = TvList.SelectedNode; if (node.Nodes==null || node.Nodes.Count==0) { this.label1.Text = cinema.Dule.Items[node.Text].Movie.Name; this.label2.Text = cinema.Dule.Items[node.Text].Movie.DaoYan; this.label3.Text = cinema.Dule.Items[node.Text].Movie.DaoYan; this.label4.Text = cinema.Dule.Items[node.Text].Movie.MovieType; this.label5.Text = cinema.Dule.Items[node.Text].Time; this.label6.Text = cinema.Dule.Items[node.Text].Movie.Price.ToString(); this.label7.Text = ""; this.picMovie.Image = Image.FromFile(cinema.Dule.Items[node.Text].Movie.Url); string key = node.Parent.Text + node.Text; ColorYellow();//将控件变成黄色 } public void ColorYellow() { foreach (Control itemC in this.tabCon.Controls) { itemC.BackColor = Color.Yellow; } }
首先记录下被选中节点(基本上在树状类型被选中的事件中都要有这么的一句话)判断如果被选中的节点下没有子节点了,开始将界面上的label标签赋值。首先被选中的节点是父节点电影名称,之后被选中的节点是放映时段,根据此时段加载出该电影的信息和票价,并且将控件变为黄色。因为每一种电影的每一个播放场次均有座位(各有自己的一套座位)。
3:实现买票功能:首先动态加载出一堆的label控件,即座位。确定几种买票的方式,通过单选按钮的事件实现。
private void rb02_CheckedChanged(object sender, EventArgs e) { this.textBox1.Enabled = true; this.comboBox1.Enabled = false; this.comboBox1.Text = ""; this.label7.Text = "0"; } private void rb03_CheckedChanged(object sender, EventArgs e) { this.textBox1.Enabled = false; this.textBox1.Text = ""; this.comboBox1.Enabled = true; this.comboBox1.Text = "7"; if (this.label6.Text != "") { int price = Convert.ToInt32(label6.Text); int discount = Convert.ToInt32(comboBox1.Text); this.label7.Text = (price * discount / 10).ToString(); } } private void rb01_CheckedChanged(object sender, EventArgs e) { this.textBox1.Enabled = false; this.textBox1.Text = ""; this.comboBox1.Enabled = false; this.comboBox1.Text = ""; int price = Convert.ToInt32(label6.Text); this.label7.Text = price.ToString(); } private void FrmMain_Load(object sender, EventArgs e) { Init(5,7); } public void Init(int row, int line) { for (int i = 0; i < row; i++) { for (int j = 0; j < line; j++) { Label lb = new Label(); lb.BackColor = Color.Yellow; lb.Location = new Point(20 + j * 100, 50 + i * 70); lb.Font = new Font("宋体", 11); lb.Name = (i + 1) + "-" + (j + 1); lb.Size = new Size(80, 30); lb.TabIndex = 0; lb.Text = (i + 1) + "-" + (j + 1); lb.TextAlign = ContentAlignment.MiddleCenter; lb.Click += new System.EventHandler(lb_Click); this.tabCon.Controls.Add(lb); Seat seat = new Seat( Color.Yellow,lb.Text); cinema.Seats.Add(seat.Num, seat); } } } private void lb_Click(object sender, EventArgs e) { if (this.TvList.Nodes.Count == 0 || this.TvList.SelectedNode.Level == 0) { return; } lbl = sender as Label;//object基类型转化为Label类型 if (lbl.BackColor == Color.Red) { MessageBox.Show("已售出"); } else { if (DialogResult.OK == MessageBox.Show("是否购买", "提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Question)) { //用于判断如何得到Cinema bool flag = true; Cinema cinema2 = null; string text = TvList.SelectedNode.Text; string txt = TvList.SelectedNode.Parent.Text; string key = txt + text; foreach (string itemKey in dic.Keys) { if (key.Equals(itemKey)) { cinema2 = dic[itemKey]; flag = false; } } if (flag) { cinema2 = new Cinema(); cinema2.Seats = cinema.Seats; cinema2.Dule = cinema.Dule; } string time = this.TvList.SelectedNode.Text; ScheduleItems item = cinema2.Dule.Items[time]; string type =this.rb01.Checked ? "normal" : rb02.Checked ? "free" : "student"; Tickey tic = null; switch (type) { case "normal": tic = new Tickey(item, cinema2.Seats[lbl.Text]); tic.Print(); break; case"free": tic = new FreeTickey(item, cinema2.Seats[lbl.Text], textBox1.Text); tic.Print(); break; case"student": tic = new StudentTickey(item, cinema2.Seats[lbl.Text], Convert.ToInt32(comboBox1.Text)); tic.Print(); break; } dic.Add(key, cinema2); lbl.BackColor = Color.Red; cinema.Seats[lbl.Text].Color = Color.Red; } } }
创建一个双列集合Dictionary(string,Ciname)如果被选中的节点的文本与其父节点的文本与该集合的key值相等,cinema对象等于dic[key];因为Cinema保存的是播放时段,座位和被卖出去的票。
通过电影名称和选中的时间段来确定这三个属性。然后判断被买的票的类型,将其加入到dic集合中。最后将控件的颜色变成红色。
4:通过序列化和反序列化来实现保存和继续销售的功能。
private void 保存ToolStripMenuItem_Click(object sender, EventArgs e) { //直接序列化 Dictionary dic 这样 在反序列化时就啥都不用干了。 BinaryFormatter bf = new BinaryFormatter(); FileStream fs = new FileStream("YingPianInfo.bin", FileMode.Open); bf.Serialize(fs, dic); fs.Close(); MessageBox.Show("保存成功"); } private void 继续销售ToolStripMenuItem_Click(object sender, EventArgs e) { Show(); BinaryFormatter bf = new BinaryFormatter(); FileStream fs = new FileStream("YingPianInfo.bin", FileMode.Open); dic = (Dictionary<string, Cinema>)bf.Deserialize(fs); fs.Close(); }
直接序列化dic集合就可以了,因为dic集合中保存的就是被卖出票的所有信息。
5:最后在继续销售的时候,将已经卖出的座位标识为红色(即已售出)
public void SeatColorChanage(List<Tickey> tickeys)//将已售出的票变成红色 根据字典集合的键值 { if (tickeys!=null && tickeys.Count!=0) { foreach (Tickey item in tickeys)//所有被卖出的票 { foreach (string cinemaSet in cinema.Seats.Keys)//已经售出座位的编号 { if (cinemaSet.Equals(item.Seat.Num))//如果匹配已经售出的座位 { foreach (Control itemLast in this.tabCon.Controls) { if (itemLast.Text.Equals(cinemaSet)) { itemLast.BackColor = Color.Red; } } } } } }
}
因为以上的dic集合保存的是已经售出票的信息,如果其中的座位编号与当前的座位编号相匹配的话,将其变成红色,在treeView的事件中调用该方法。如果是该电影该时段的情况下,以dic[item].Soldtickeys.因为Soldtickey是list<tickey>类型,该字段又存在于cinema类中,所以直接以其为参数传递,将控件变为红色!