(一)项目概述和效果展示
(二)项目面向对象分析
1)基于OOP(面向对象编程)设计项目的基本方法
项目的类型
- 基于OOP设计小型项目(可以使用数据库,也可以不使用数据库而采用其他数据源)
- 没有数据库的项目:要求使用OOP思想和方法,完成项目各个类的设计,确定各个类之间的关系;【纯面向对象】
- 有数据库的项目:项目的框架和思路是相对固定的,在框架的约束下开发相对会更容易;
- 常见框架:三层架构、 三层架构+MVC;
核心问题:
- 一个项目确定多少个类:
- 名词分析法:先找到关键的名词(主题中),然后对名词进行筛选。筛选的依据:抽取的类应该具备相关的属性和方法。如果一个名称无法找到相关的属性和方法,则该名称不能抽象成类;以此为核心,能都辅助完成功能的辅助类;
- 头脑风暴法:大家一起商量,各抒己见,按照少数服从多数的方法进行筛选;
- 每个类有什么要求:
- 属性选择的依据:主要是考虑这个对象应该提供哪些数据;【临时保存的数据】【属性用于交换数据】
- 方法:依据相关的核心功能找到,然后依据和类的相关性,放到对应的类里面;
注意:不管是属性还是方法,务必遵循面向对象各种原则的使用,尤其是“对象职责明确原则”。
- 类之间的关系怎样确定:
- 一对一的关系:一个类的对象作为另一个类的属性。数据库表也会有一对一的关系;
- 一对多的关系:一个类的多个对象作为另一个类的属性。通常这个属性是集合类型(List、Dictionary<k,v>)。数据表中这种关系体现为两个表之间的“主外键关系”;
(三)无边框窗体设计和移动方法
- 去掉边框:将属性设置为None;【默认情况下不能移动】
- 设计外观的时候可以直接使用RGB值
- Windows窗体采取重绘的方式,使用图片的话绘制的过程会出现闪的过程,因此,体验不好;
- 使用Panel对控件集合分组;
- 使用Dock控制位置:
- 使用PictureBox放置一张图片,这样使用SizeMode属性来设置这个图片的放置方式;
由于没有边框,需要单独使用鼠标事件实现窗体的移动:
将这三个鼠标事件分别添加进窗体,以及两个Panel的事件中即可;
#region 拖动窗体的实现
private Point mouseOff;//鼠标移动位置变量
private bool leftFlag;//标签是否为左键
private void FrmMain_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mouseOff = new Point(-e.X, -e.Y); //得到变量的值
leftFlag = true; //点击左键按下时标注为true;
}
}
private void FrmMain_MouseMove(object sender, MouseEventArgs e)
{
if (leftFlag)
{
Point mouseSet = Control.MousePosition;
mouseSet.Offset(mouseOff.X, mouseOff.Y); //设置移动后的位置
Location = mouseSet;
}
}
private void FrmMain_MouseUp(object sender, MouseEventArgs e)
{
if (leftFlag)
{
leftFlag = false;//释放鼠标后标注为false;
}
}
#endregion
(四)编写选号器类,实现随机展示号码
选号器类
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WFAppDemo
{
/// <summary>
/// 选号器类
/// </summary>
public class Selector
{
private Random objRandom = new Random();//生成随机数【成员变量】
//可以将生成的随机号码暂存在一个属性中,这样可以直接从暂存的属性中添加到选中
//属性【用于临时存储数据】
/// <summary>
/// 选择的号码(临时存储,后面打印完毕以后会保存起来,用来兑奖)
/// </summary>
public List<string[]> SelectedNums { get; set; }
//方法(构造方法、实例方法)
/// <summary>
/// 构造方法(初始化成员)
/// </summary>
public Selector()
{
this.SelectedNums = new List<string[]>();
}
/// <summary>
/// 生成7个随机号码
/// </summary>
/// <returns></returns>
public string[] CreateNum()
{
string[] numList = new string[7];
for (int i = 0; i < 7; i++)
{
numList[i] = objRandom.Next(10).ToString();
}
return numList;
}
public void CreatGroupNums(int Count)
{
SelectedNums.Clear();//清空已经选号的集合
for (int i = 0; i < Count; i++)
{
SelectedNums.Add(CreateNum());//增加一组
}
}
/// <summary>
/// 获取打印格式的号码列表
/// </summary>
/// <returns></returns>
public List<string> GetPrintedNums()
{
List<string> numList = new List<string>();
for (int i = 0; i < this.SelectedNums.Count; i++)
{
string printedNum = string.Empty;
if(i<9)
{
printedNum = "第0" + (i + 1) + "组:";
}
else
{
printedNum = "第" + (i + 1) + "组:";
}
for (int j = 0; j < this.SelectedNums[i].Length; j++)
{
printedNum += this.SelectedNums[i][j] + " ";
if (j == 5) printedNum += " "; //最后一个加大空格
}
numList.Add(printedNum);
}
return numList;
}
}
}
FrmMain主类
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WFAppDemo
{
public partial class FrmMain : Form
{
//创建选号器对象
private Selector objSelector = new Selector();
public FrmMain()
{
InitializeComponent();
//禁用相关按钮
this.btnSelect.Enabled = false;
this.btnPrint.Enabled = false;
this.btnClear.Enabled = false;
this.btnDel.Enabled = false;
}
#region 拖动窗体的实现
private Point mouseOff;//鼠标移动位置变量
private bool leftFlag;//标签是否为左键
private void FrmMain_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mouseOff = new Point(-e.X, -e.Y); //得到变量的值
leftFlag = true; //点击左键按下时标注为true;
}
}
private void FrmMain_MouseMove(object sender, MouseEventArgs e)
{
if (leftFlag)
{
Point mouseSet = Control.MousePosition;
mouseSet.Offset(mouseOff.X, mouseOff.Y); //设置移动后的位置
Location = mouseSet;
}
}
private void FrmMain_MouseUp(object sender, MouseEventArgs e)
{
if (leftFlag)
{
leftFlag = false;//释放鼠标后标注为false;
}
}
#endregion
#region 最小化、关闭
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();
}
private void btnMin_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
}
#endregion
/// <summary>
/// 启动选号
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
this.RandomTimer.Start();
//设置相关按钮
this.btnStart.Enabled = false;
this.btnSelect.Enabled = true;
}
/// <summary>
/// 定时生成随机号
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RandomTimer_Tick(object sender, EventArgs e)
{
string[] numList = objSelector.CreateNum();//生成随机号
//显示随机号
this.lbNum1.Text = numList[0];
this.lbNum2.Text = numList[1];
this.lbNum3.Text = numList[2];
this.lbNum4.Text = numList[3];
this.lbNum5.Text = numList[4];
this.lbNum6.Text = numList[5];
this.lbNum7.Text = numList[6];
}
/// <summary>
/// 确认选号
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSelect_Click(object sender, EventArgs e)
{
this.RandomTimer.Stop(); //定时器停止
string[] selectedNum = //获取定时器停止时的号码数字,放在数组中
{
this.lbNum1.Text,
this.lbNum2.Text,
this.lbNum3.Text,
this.lbNum4.Text,
this.lbNum5.Text,
this.lbNum6.Text,
this.lbNum7.Text,
};//使用暂存的属性,则可以直接读取
objSelector.SelectedNums.Add(selectedNum); //保存当前选中号码
ShowSelectedNums(); //显示选中的号码
}
/// <summary>
/// 显示已经选中的号码
/// </summary>
private void ShowSelectedNums()
{
this.lbNumberList.Items.Clear();
this.lbNumberList.Items.AddRange(this.objSelector.GetPrintedNums().ToArray());
//按钮启用
this.btnStart.Enabled = true;
this.btnPrint.Enabled = true;
this.btnClear.Enabled = true;
this.btnDel.Enabled = true;
}
/// <summary>
/// 随机组选
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnGroupSelect_Click(object sender, EventArgs e)
{
this.RandomTimer.Stop(); //定时器停止,因为可能处于随机选号状态
//验证是数字
this.objSelector.CreatGroupNums(Convert.ToInt32(this.txtGroup.Text.Trim()));
ShowSelectedNums();
}
#region 获取并显示手写数字
/// <summary>
/// 手写号码
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnWriteSelf_Click(object sender, EventArgs e)
{
//首先必须验证每个文本框都是数字
string[] selectedNum = //获取手写数字
{
this.txtNum1.Text,
this.txtNum2.Text,
this.txtNum3.Text,
this.txtNum4.Text,
this.txtNum5.Text,
this.txtNum6.Text,
this.txtNum7.Text,
};//使用暂存的属性,则可以直接读取
objSelector.SelectedNums.Add(selectedNum); //保存当前选中号码
ShowSelectedNums(); //显示选中的号码
}
#endregion
/// <summary>
/// 删除当前所选的号码
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnDel_Click(object sender, EventArgs e)
{
if (this.lbNumberList.Items.Count == 0 || this.lbNumberList.SelectedItem == null) return;
int index = this.lbNumberList.SelectedIndex; //获取所选中行的索引
objSelector.SelectedNums.RemoveAt(index); //在选中号码集合中删除指定号码
if(objSelector.SelectedNums.Count==0) //判断是不是全部删除
{
this.btnClear.Enabled = false;
this.btnDel.Enabled = false;
this.btnPrint.Enabled = false;
}
ShowSelectedNums();
}
/// <summary>
/// 清空所有的号码
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnClear_Click(object sender, EventArgs e)
{
this.lbNumberList.Items.Clear(); //从界面中清楚显示
this.objSelector.SelectedNums.Clear(); //从选号器集合中清空
//号码复位
this.lbNum1.Text = "0";
this.lbNum2.Text = "0";
this.lbNum3.Text = "0";
this.lbNum4.Text = "0";
this.lbNum5.Text = "0";
this.lbNum6.Text = "0";
this.lbNum7.Text = "0";
this.txtNum1.Text = "0";
this.txtNum2.Text = "0";
this.txtNum3.Text = "0";
this.txtNum4.Text = "0";
this.txtNum5.Text = "0";
this.txtNum6.Text = "0";
this.txtNum7.Text = "0";
//禁用相关按钮
this.btnSelect.Enabled = false;
this.btnPrint.Enabled = false;
this.btnSelect.Enabled = false;
this.btnClear.Enabled = false;
this.btnDel.Enabled = false;
}
}
}
(五)实现打印
实现打印需要使用条形码和二维码;我们使用现成的 dll 即可;首先要添加引用:
代码实现:
选号器类:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WFAppDemo.Models;
using System.Runtime.Serialization.Formatters.Binary;
namespace WFAppDemo
{
/// <summary>
/// 选号器类
/// </summary>
public class Selector
{
Fath.BarcodeX barCode = new Fath.BarcodeX(); //创建条形码生成对象
#region
private Random objRandom = new Random();//生成随机数【成员变量】
//可以将生成的随机号码暂存在一个属性中,这样可以直接从暂存的属性中添加到选中
//属性【用于临时存储数据】
/// <summary>
/// 选择的号码(临时存储,后面打印完毕以后会保存起来,用来兑奖)
/// </summary>
public List<string[]> SelectedNums { get; set; }
//方法(构造方法、实例方法)
/// <summary>
/// 构造方法(初始化成员)
/// </summary>
public Selector()
{
this.SelectedNums = new List<string[]>();
}
/// <summary>
/// 生成7个随机号码
/// </summary>
/// <returns></returns>
public string[] CreateNum()
{
string[] numList = new string[7];
for (int i = 0; i < 7; i++)
{
numList[i] = objRandom.Next(10).ToString();
}
return numList;
}
public void CreatGroupNums(int Count)
{
SelectedNums.Clear();//清空已经选号的集合
for (int i = 0; i < Count; i++)
{
SelectedNums.Add(CreateNum());//增加一组
}
}
/// <summary>
/// 获取打印格式的号码列表
/// </summary>
/// <returns></returns>
public List<string> GetPrintedNums()
{
List<string> numList = new List<string>();
for (int i = 0; i < this.SelectedNums.Count; i++)
{
string printedNum = string.Empty;
if(i<9)
{
printedNum = "第0" + (i + 1) + "组:";
}
else
{
printedNum = "第" + (i + 1) + "组:";
}
for (int j = 0; j < this.SelectedNums[i].Length; j++)
{
printedNum += this.SelectedNums[i][j] + " ";
if (j == 5) printedNum += " "; //最后一个加大空格
}
numList.Add(printedNum);
}
return numList;
}
#endregion
#region 打印部分,主要是定位左边界和上边界的距离
/// <summary>
/// 打印彩票
/// </summary>
public void PrintLottery(System.Drawing.Printing.PrintPageEventArgs e, string serialNum, List<string> numList)
{
//生成条形码
barCode.Text = serialNum;//条码数据
barCode.Symbology = Fath.bcType.Code128;//设置条码格式
barCode.ShowText = true;//同时显示文本
e.Graphics.DrawImage(barCode.Image(240, 50), new Point(20, 5));//画条形码
//生成彩票信息
float left = 2; //打印区域的左边界
float top = 70;//打印区域的上边界
Font titlefont = new Font("仿宋", 10);//标题字体
Font font = new Font("仿宋", 8);//内容字体
e.Graphics.DrawString("天津百万奖彩票中心", titlefont, Brushes.Blue, left + 20, top, new StringFormat());//打印标题
//画一条分界线
Pen pen = new Pen(Color.Green, 1);
e.Graphics.DrawLine(pen, new Point((int)left - 2, (int)top + 20), new Point((int)left + (int)180, (int)top + 20));
//循环打印选号
for (int i = 0; i < numList.Count; i++)
{
e.Graphics.DrawString(numList[i], font, Brushes.Blue, left,
top + titlefont.GetHeight(e.Graphics) + font.GetHeight(e.Graphics) * i + 12, new StringFormat());
}
//画一条分界线
float topPoint = titlefont.GetHeight(e.Graphics) + font.GetHeight(e.Graphics) * (numList.Count) + 22;
e.Graphics.DrawLine(pen, new Point((int)left - 2, (int)top + (int)topPoint),
new Point((int)left + (int)180, (int)top + (int)topPoint));
//打印时间
string time = "购买时间:" + DateTime.Now.ToString("yyy-MM-dd HH:mm:ss");
e.Graphics.DrawString(time, font, Brushes.Blue, left, top + titlefont.GetHeight(e.Graphics)
+ font.GetHeight(e.Graphics) * (numList.Count + 1) + 12, new StringFormat());
//二维码图片left和top坐标
int qrcodetop = (int)(top + titlefont.GetHeight(e.Graphics) + font.GetHeight(e.Graphics) * (numList.Count + 3) + 12);
int qrcodeleft = (int)left + 32;
//生成二维码图片
// Image bmp = QRcodeCreator.GetQRCodeBmp("http://www.xiketang.com/duijiang/query?id=" + serialNum);//直接添加流水号,将来从网站直接查询
Image bmp = QRcodeCreator.GetQRCodeBmp("http://www.xiketang.com");
//打印
e.Graphics.DrawImage(bmp, new Point(qrcodeleft, qrcodetop));//不同的URL图片大小不同,可以根据需要调整left坐标
e.Graphics.DrawString("扫描二维码可直接查询兑奖结果", font, Brushes.Blue, left, qrcodetop + bmp.Height + 10, new StringFormat());
}
/// <summary>
/// 保存用户所选彩票信息(实际开发中可以保存在数据库中)
/// </summary>
/// <param name="serialNum"></param>
internal void Save(string serialNum)
{
DirectoryInfo dir = new DirectoryInfo("numList");
if (!dir.Exists)
{
dir.Create();
}
string path = @"numList\" + serialNum + ".num";
FileStream fs = new FileStream(path, FileMode.Create);
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, this.SelectedNums);
fs.Close();
}
#endregion
}
}
FrmMain类
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Printing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WFAppDemo
{
public partial class FrmMain : Form
{
//创建选号器对象
private Selector objSelector = new Selector();
private PrintDocument printDoc = new PrintDocument(); //创建打印对象
public FrmMain()
{
InitializeComponent();
//禁用相关按钮
this.btnSelect.Enabled = false;
this.btnPrint.Enabled = false;
this.btnClear.Enabled = false;
this.btnDel.Enabled = false;
//关联打印对象的事件
//this.printDoc.PrintPage += new PrintPageEventHandler(this.LotteryPrintPage);
}
#region 拖动窗体的实现
private Point mouseOff;//鼠标移动位置变量
private bool leftFlag;//标签是否为左键
private void FrmMain_MouseDown(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
mouseOff = new Point(-e.X, -e.Y); //得到变量的值
leftFlag = true; //点击左键按下时标注为true;
}
}
private void FrmMain_MouseMove(object sender, MouseEventArgs e)
{
if (leftFlag)
{
Point mouseSet = Control.MousePosition;
mouseSet.Offset(mouseOff.X, mouseOff.Y); //设置移动后的位置
Location = mouseSet;
}
}
private void FrmMain_MouseUp(object sender, MouseEventArgs e)
{
if (leftFlag)
{
leftFlag = false;//释放鼠标后标注为false;
}
}
#endregion
#region 最小化、关闭
private void btnClose_Click(object sender, EventArgs e)
{
this.Close();
}
private void btnMin_Click(object sender, EventArgs e)
{
this.WindowState = FormWindowState.Minimized;
}
#endregion
/// <summary>
/// 启动选号
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnStart_Click(object sender, EventArgs e)
{
this.RandomTimer.Start();
//设置相关按钮
this.btnStart.Enabled = false;
this.btnSelect.Enabled = true;
}
/// <summary>
/// 定时生成随机号
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void RandomTimer_Tick(object sender, EventArgs e)
{
string[] numList = objSelector.CreateNum();//生成随机号
//显示随机号
this.lbNum1.Text = numList[0];
this.lbNum2.Text = numList[1];
this.lbNum3.Text = numList[2];
this.lbNum4.Text = numList[3];
this.lbNum5.Text = numList[4];
this.lbNum6.Text = numList[5];
this.lbNum7.Text = numList[6];
}
/// <summary>
/// 确认选号
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSelect_Click(object sender, EventArgs e)
{
this.RandomTimer.Stop(); //定时器停止
string[] selectedNum = //获取定时器停止时的号码数字,放在数组中
{
this.lbNum1.Text,
this.lbNum2.Text,
this.lbNum3.Text,
this.lbNum4.Text,
this.lbNum5.Text,
this.lbNum6.Text,
this.lbNum7.Text,
};//使用暂存的属性,则可以直接读取
objSelector.SelectedNums.Add(selectedNum); //保存当前选中号码
ShowSelectedNums(); //显示选中的号码
}
/// <summary>
/// 显示已经选中的号码
/// </summary>
private void ShowSelectedNums()
{
this.lbNumberList.Items.Clear();
this.lbNumberList.Items.AddRange(this.objSelector.GetPrintedNums().ToArray());
//按钮启用
this.btnStart.Enabled = true;
this.btnPrint.Enabled = true;
this.btnClear.Enabled = true;
this.btnDel.Enabled = true;
}
/// <summary>
/// 随机组选
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnGroupSelect_Click(object sender, EventArgs e)
{
this.RandomTimer.Stop(); //定时器停止,因为可能处于随机选号状态
//验证是数字
this.objSelector.CreatGroupNums(Convert.ToInt32(this.txtGroup.Text.Trim()));
ShowSelectedNums();
}
#region 获取并显示手写数字
/// <summary>
/// 手写号码
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnWriteSelf_Click(object sender, EventArgs e)
{
//首先必须验证每个文本框都是数字
string[] selectedNum = //获取手写数字
{
this.txtNum1.Text,
this.txtNum2.Text,
this.txtNum3.Text,
this.txtNum4.Text,
this.txtNum5.Text,
this.txtNum6.Text,
this.txtNum7.Text,
};//使用暂存的属性,则可以直接读取
objSelector.SelectedNums.Add(selectedNum); //保存当前选中号码
ShowSelectedNums(); //显示选中的号码
}
#endregion
/// <summary>
/// 删除当前所选的号码
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnDel_Click(object sender, EventArgs e)
{
if (this.lbNumberList.Items.Count == 0 || this.lbNumberList.SelectedItem == null) return;
int index = this.lbNumberList.SelectedIndex; //获取所选中行的索引
objSelector.SelectedNums.RemoveAt(index); //在选中号码集合中删除指定号码
if(objSelector.SelectedNums.Count==0) //判断是不是全部删除
{
this.btnClear.Enabled = false;
this.btnDel.Enabled = false;
this.btnPrint.Enabled = false;
}
ShowSelectedNums();
}
/// <summary>
/// 清空所有的号码
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnClear_Click(object sender, EventArgs e)
{
this.lbNumberList.Items.Clear(); //从界面中清楚显示
this.objSelector.SelectedNums.Clear(); //从选号器集合中清空
//号码复位
this.lbNum1.Text = "0";
this.lbNum2.Text = "0";
this.lbNum3.Text = "0";
this.lbNum4.Text = "0";
this.lbNum5.Text = "0";
this.lbNum6.Text = "0";
this.lbNum7.Text = "0";
this.txtNum1.Text = "0";
this.txtNum2.Text = "0";
this.txtNum3.Text = "0";
this.txtNum4.Text = "0";
this.txtNum5.Text = "0";
this.txtNum6.Text = "0";
this.txtNum7.Text = "0";
//禁用相关按钮
this.btnSelect.Enabled = false;
this.btnPrint.Enabled = false;
this.btnSelect.Enabled = false;
this.btnClear.Enabled = false;
this.btnDel.Enabled = false;
}
/// <summary>
/// 使用小票打印机打印
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnPrint_Click(object sender, EventArgs e)
{
this.printDoc.Print();
}
/// <summary>
/// 实现打印的具体方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LotteryPrintPage_PrintPage(object sender, PrintPageEventArgs e)
{
string serialNum = DateTime.Now.ToString("yyyyMMddHHmmssms");//流水号(生成条码使用)
this.objSelector.PrintLottery(e, serialNum, objSelector.GetPrintedNums());//调用前面编写的打印方法
objSelector.Save(serialNum);//同时保存所选号码
btnClear_Click(null, null);//打印完毕后清空
}
}
}