1.Invoke 跨线程访问
有些回调函数不能直接访问控件的属性,这时就需要用到这个。
用法:
Invoke(new Action(() =>
{
//正常代码
receiveCount++;
tb_recvCount.Text = Convert.ToString(receiveCount);
}));
2.控制打印机打印
2.1.绘制表格
准备一个 panel ,在里面放入 label ,将 label 的宽度设置为 1 或者 2 ,背景色改为黑色,就能得到一根直线,再放入几个 label 作为文本容器。
2.2.填充数据
使用 label ,给 label.Text 赋值即可。
2.3.开始打印
在页面中添加如下两个控件。
将 printPreviewDialog 的 Document 属性设置为 刚刚添加的 printDocument 。
在 printDocument 的 PrintPage 事件中绘制打印的内容。
private void printDocument1_PrintPage(object sender, PrintPageEventArgs e)
{
//打印内容 为 局部的 this.panel1
Bitmap _NewBitmap = new Bitmap(panel_label.Width, panel_label.Height);
panel_label.DrawToBitmap(_NewBitmap, new Rectangle(0, 0, _NewBitmap.Width, _NewBitmap.Height));
e.Graphics.DrawImage(_NewBitmap, 0, 0, _NewBitmap.Width, _NewBitmap.Height);
Graphics g = e.Graphics;
g.SmoothingMode = SmoothingMode.HighQuality;
Pen pen01 = new Pen(Color.Black, 0.5f);
//g.DrawLine(pen01, 5, 93, 233, 93);
}
3.威纶通触摸屏使用MQTT发送打印指令
- 触摸屏的MQTT发送的数据为 JSON 格式,带有 “d” 层和时间戳,值为数组格式,访问时应访问:
json["d"]["SN"][0]
- 触摸屏发送打印指令,使用位状态切换开关,操作模式为“设为ON”。这样点击打印按钮之后,直到 MQTT 发布完成, LB-20 都为 true ,如果设置为 “切换开关”,MQTT发送的时候 LB-20 很可能为 false ,这会有什么影响呢,请继续看。
- 以下是发送的 MQTT 主题,发送模式为触发式,触发地址为第 2 点提到的 LB-20 。
- 发送的信息有 LB-20 ,这就是第 2 点提到的会受到影响的地方,打印机控制程序通过判断 Print 的值是否为 true 来判断是否打印这条讯息,如果点击打印之后,发送过去的 Print 为 false 。打印机就不会打印 。
4.打印机控制程序接收打印信息
4.1.使用 MQTT 客户端订阅讯息
获得触摸屏发送到 MQTT 服务器的讯息,解析讯息为 JSON 格式。
4.2.判断 Print 是否为 true
为 true 则将数据填充到表格,切换默认打印机,开始打印。
切换默认打印机的代码如下:
class SetPrinter
{
[DllImport("winspool.drv")]
public static extern bool SetDefaultPrinter(String Name); //调用win api将指定名称的打印机设置为默认打印机
}
5.打印机配置
5.1. 配置特定机台对应特定的打印机
如下图,以主题来区分机台。
5.2. 获取打印机列表
private void mbtn_getPrinterList_Click(object sender, EventArgs e)//获取打印机列表
{
cbb_printerList.Items.Clear();
int i;
using (PrintDocument pd = new PrintDocument())
{
for (i = 0; i < PrinterSettings.InstalledPrinters.Count; i++) //开始遍历
{
printers = PrinterSettings.InstalledPrinters[i]; //取得名称
cbb_printerList.Items.Add(printers); //加入ComboBox
//if (pd.PrinterSettings.IsDefaultPrinter) //判断是否为默认打印机
//{
// textBox_actPrinter.Text = pd.PrinterSettings.PrinterName;
// cmbPrinterList.Text = pd.PrinterSettings.PrinterName; //显示默认打印机名称
//}
}
if (cbb_printerList.Items != null)
{
cbb_printerList.SelectedItem = cbb_printerList.Items[0];
}
}
}
5.3.将配置写入配置文件。使用 ConfigHelper 类。
public class ConfigHelper
{
[DllImport("kernel32")]
private static extern long GetPrivateProfileString(string section, string key, string def, StringBuilder retval, int size, string filepath);
[DllImport("kernel32")]
private static extern long WritePrivateProfileString(string section, string key, string val, string filepath);
/// <summary>
/// 读配置文件
/// </summary>
/// <param name="section">选择读取的部分</param>
/// <param name="key">键</param>
/// <param name="def">默认值</param>
/// <param name="size"></param>
/// <param name="filepath"></param>
/// <returns></returns>
public static String ReadIni(string section, string key, string def, int size, string filepath)
{
StringBuilder sb = new StringBuilder();
GetPrivateProfileString(section, key, def, sb, size, filepath);
return sb.ToString();
}
/// <summary>
/// 写入数据到配置文件
/// </summary>
/// <param name="section"></param>
/// <param name="key"></param>
/// <param name="val"></param>
/// <param name="filepath"></param>
/// <returns></returns>
public static long WriteIni(string section, string key, string val, string filepath)
{
return WritePrivateProfileString(section, key, val, filepath);
}
// 删除ini文件下所有段落
public void ClearAllSection(string filepath)
{
WriteIni(null, null, null, filepath);
}
//删除ini文件下personal段落下的所有键
public void ClearSection(string Section, string filepath)
{
WriteIni(Section, null, null, filepath);
}
}
用法:
private void writeConfig()//配置写入
{
string path = Application.StartupPath + "/config.ini";
ConfigHelper.WriteIni("part1", "Machine1_printer", tb_printer1.Text, path);
}
效果:
5.4.读取配置文件到JSON,同时将数据写入控件
private void readConfig(JObject configJson)//配置读取
{
string path = Application.StartupPath + "/config.ini";
for(int i = 1; i <= 50; i++)
{
string key1=string.Empty, value1=string.Empty;
//读取主题名
string tb_topic = $"tb_topic{i}";
Control[] tb_topics = Controls.Find(tb_topic, true);
foreach (var tbt in tb_topics)
{
if(tbt.Name == tb_topic)
{
tbt.Text = ConfigHelper.ReadIni("part2", $"Machine{i}_topic", "空数据", 1024, path);
key1 = tbt.Text;
}
}
//读取打印机名
string tb_printer = $"tb_printer{i}";
Control[] tb_printers = Controls.Find(tb_printer, true);
foreach (var tbp in tb_printers)
{
if (tbp.Name == tb_printer)
{
tbp.Text = ConfigHelper.ReadIni("part1", $"Machine{i}_printer", "空数据", 1024, path);
value1 = tbp.Text;
}
}
if(configJson.ContainsKey(key1) == false)
{
configJson.Add(key1, value1);
}
}
}
6.打印记录
6.1.写入 .csv 文件
public static void writeRecord(string line)
{
string path = Application.StartupPath + $"/PrintRecord/PrintRecord-{DateTime.Now.ToString("yyyy-MM")}.csv";
//导出到文件的具体位置
FileInfo fi = new FileInfo(path);
if (!fi.Directory.Exists) //如果文件不存在则创建文件
{
fi.Directory.Create();
}
FileStream fs = new FileStream(path, FileMode.Append, FileAccess.Write);
StreamWriter sw = new StreamWriter(fs);
sw.WriteLine(line); //按行写入文件中
sw.Close();
fs.Close();
}
6.2.从 .csv 文件读取
- 准备一个空 DataGridView ,不要添加列。
- 使用 LoadCSV 方法读取 .csv 文件,得到一个 DataTable 。
public static DataTable LoadCSV(string data,string tableHead)
{
DataTable resultTable = new DataTable();//创建空DataTable
//在下面写解析代码
data = data.Replace("\r", string.Empty);//清除一些必须清理的东西
string[] lines = data.Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries);//分离每一行,StringSplitOptions.RemoveEmptyEntries:返回值不包含空字符串
string[] dataHeads = tableHead.Split(',');//获取表头
foreach (string dataHead in dataHeads)
{
resultTable.Columns.Add(new DataColumn { ColumnName = dataHead });//把文件头中的元素加入dataTable的列中
}
foreach (string line in lines)//对每一行数据进行操作
{
if (string.IsNullOrEmpty(line)) continue;
string[] items = line.Split(',');//以分号分离每一行数据
这一块用于读取文件头
//if (line == lines[0])//如果在读取文件头
//{
// dataHead = new string[items.Length];
// dataHead = items;//将文件头数据赋值给dataHead
// foreach (string item in items)
// {
// resultTable.Columns.Add(new DataColumn { ColumnName = item });//把文件头中的元素加入dataTable的列中
// }
//}
//else//这一块用于读取数据并将数据写入表格
{
DataRow row = resultTable.NewRow();//添加行进入dataTable
for (int i = 0; i < items.Length; i++)//向行中添加数据(除类名)
{
row[dataHeads[i]] = items[i];//dataHead[i]为文件头元素,item[i]为文件头元素对应值
}
resultTable.Rows.Add(row);//添加行进入dataTable
}
}
return resultTable;
}
- 将 DataGridView.DataSource 设置为这个 DataTable
string tableHead = "日期,时间,班次,机台编号,开机员";//设置表头,以‘,’分隔
string path = Application.StartupPath + "/PrintRecord/"+ cbb_printRecordList.Text;
Encoding encoding = Encoding.GetEncoding("UTF-8");//编码
string csv = File.ReadAllText(path, encoding);
dGV_printRecord.DataSource = UserClass1.LoadCSV(csv, tableHead);
效果: