打造最漂亮的串口调试助手(基于WPF + .NET C# VS2013)附源码!
WPF界面全部用XAML语言手打,基本都是Grid布局,VS很强大,编程很舒服便捷,源码有很详细的注释。
* 学C#和WPF编的第一个软件,整个编程过程,通过百度不断学习,其中很多借鉴了一叶知秋串口助手的代码
* 作者是做硬件的,只为学习做简单的上位机程序,C#简单,开发效率高,所以选择C#
* 以前没有PC端软件编程经验,所以该编程思想继承于单片机编程思想,未用到面向对象和WPF的精髓,不建议模仿,仅供参考
* 实际上到现在我还不知道面向对象是什么意思 ̄□ ̄||
* 欢迎反馈BUG QQ45213212 E-MAIL lincolne@126.com
下载点这里http://download.csdn.net/detail/q45213212/7560727
直接上图
以下为主体c#源码
-
-
//相对单线收发模式,占用系统资源稍微大些,但是执行效果更好,尤其是在大数据收发时的UI反应尤其明显
-
using
Microsoft.Win32; -
using
SerialComWindow; -
using
System; -
using
System.Collections; -
using
System.Collections.Generic; -
using
System.IO; -
using
System.IO.Ports; -
using
System.Linq; -
using
System.Security.Permissions; -
using
System.Text; -
using
System.Threading; -
using
System.Threading.Tasks; -
using
System.Windows; -
using
System.Windows.Controls; -
using
System.Windows.Data; -
using
System.Windows.Documents; -
using
System.Windows.Input; -
using
System.Windows.Media; -
using
System.Windows.Media.Imaging; -
using
System.Windows.Navigation; -
using
System.Windows.Shapes; -
using
System.Windows.Threading; -
-
-
-
-
namespace
SerialCom
-
{
-
///
<summary>
-
///
MainWindow.xaml 的交互逻辑
-
///
</summary>
-
public
partial
class
MainWindow
:
Window
-
{
-
SerialPort ComPort =
new
SerialPort();
//声明一个串口
-
private
string
[] ports;
//可用串口数组
-
private
bool
recStaus =
true
;
//接收状态字
-
private
bool
ComPortIsOpen =
false
;
//COM口开启状态字,在打开/关闭串口中使用,这里没有使用自带的ComPort.IsOpen,因为在串口突然丢失的时候,ComPort.IsOpen会自动false,逻辑混乱
-
private
bool
Listening =
false
;
//用于检测是否没有执行完invoke相关操作,仅在单线程收发使用,但是在公共代码区有相关设置,所以未用#define隔离
-
private
bool
WaitClose =
false
;
//invoke里判断是否正在关闭串口是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke ,解决关闭串口时,程序假死,具体参见http://news.ccidnet.com/art/32859/20100524/2067861_4.html 仅在单线程收发使用,但是在公共代码区有相关设置,所以未用#define隔离
-
IList<customer> comList =
new
List<customer>();
//可用串口集合
-
DispatcherTimer autoSendTick =
new
DispatcherTimer();
//定时发送
-
-
private
static
bool
Sending =
false
;
//正在发送数据状态字
-
private
static
Thread _ComSend;
//发送数据线程
-
Queue recQueue =
new
Queue();
//接收数据过程中,接收数据线程与数据处理线程直接传递的队列,先进先出
-
private
SendSetStr SendSet =
new
SendSetStr();
//发送数据线程传递参数的结构体
-
private
struct
SendSetStr
//发送数据线程传递参数的结构体格式
-
{
-
public
string
SendSetData;
//发送的数据
-
public
bool
? SendSetMode;
//发送模式
-
}
-
-
-
public
MainWindow
(
)
//主窗口
-
{
-
InitializeComponent();
//控件初始化
-
-
}
-
-
private
void
Window_Loaded
(
object
sender, RoutedEventArgs e
)
//主窗口初始化
-
{
-
//↓↓↓↓↓↓↓↓↓可用串口下拉控件↓↓↓↓↓↓↓↓↓
-
ports= SerialPort.GetPortNames();
//获取可用串口
-
if
(ports.Length >
0
)
//ports.Length > 0说明有串口可用
-
{
-
for
(
int
i =
0
; i < ports.Length; i++) -
{
-
comList.Add(
new
customer() { com = ports[i] });
//下拉控件里添加可用串口
-
}
-
AvailableComCbobox.ItemsSource = comList;
//资源路劲
-
AvailableComCbobox.DisplayMemberPath =
“com”
;
//显示路径
-
AvailableComCbobox.SelectedValuePath =
“com”
;
//值路径
-
AvailableComCbobox.SelectedValue = ports[
0
];
//默认选第1个串口
-
}
-
else
//未检测到串口
-
{
-
MessageBox.Show(
“无可用串口”
); -
}
-
//↑↑↑↑↑↑↑↑↑可用串口下拉控件↑↑↑↑↑↑↑↑↑
-
-
//↓↓↓↓↓↓↓↓↓波特率下拉控件↓↓↓↓↓↓↓↓↓
-
IList<customer> rateList =
new
List<customer>();
//可用波特率集合
-
rateList.Add(
new
customer() { BaudRate =
“1200”
}); -
rateList.Add(
new
customer() { BaudRate =
“2400”
}); -
rateList.Add(
new
customer() { BaudRate =
“4800”
}); -
rateList.Add(
new
customer() { BaudRate =
“9600”
}); -
rateList.Add(
new
customer() { BaudRate =
“14400”
}); -
rateList.Add(
new
customer() { BaudRate =
“19200”
}); -
rateList.Add(
new
customer() { BaudRate =
“28800”
}); -
rateList.Add(
new
customer() { BaudRate =
“38400”
}); -
rateList.Add(
new
customer() { BaudRate =
“57600”
}); -
rateList.Add(
new
customer() { BaudRate =
“115200”
}); -
RateListCbobox.ItemsSource = rateList;
-
RateListCbobox.DisplayMemberPath =
“BaudRate”
; -
RateListCbobox.SelectedValuePath =
“BaudRate”
; -
//↑↑↑↑↑↑↑↑↑波特率下拉控件↑↑↑↑↑↑↑↑↑
-
-
//↓↓↓↓↓↓↓↓↓校验位下拉控件↓↓↓↓↓↓↓↓↓
-
IList<customer> comParity =
new
List<customer>();
//可用校验位集合
-
comParity.Add(
new
customer() { Parity =
“None”
, ParityValue =
“0”
}); -
comParity.Add(
new
customer() { Parity =
“Odd”
, ParityValue =
“1”
}); -
comParity.Add(
new
customer() { Parity =
“Even”
, ParityValue =
“2”
}); -
comParity.Add(
new
customer() { Parity =
“Mark”
, ParityValue =
“3”
}); -
comParity.Add(
new
customer() { Parity =
“Space”
, ParityValue =
“4”
}); -
ParityComCbobox.ItemsSource = comParity;
-
ParityComCbobox.DisplayMemberPath =
“Parity”
; -
ParityComCbobox.SelectedValuePath =
“ParityValue”
; -
//↑↑↑↑↑↑↑↑↑校验位下拉控件↑↑↑↑↑↑↑↑↑
-
-
//↓↓↓↓↓↓↓↓↓数据位下拉控件↓↓↓↓↓↓↓↓↓
-
IList<customer> dataBits =
new
List<customer>();
//数据位集合
-
dataBits.Add(
new
customer() { Dbits =
“8”
}); -
dataBits.Add(
new
customer() { Dbits =
“7”
}); -
dataBits.Add(
new
customer() { Dbits =
“6”
}); -
DataBitsCbobox.ItemsSource = dataBits;
-
DataBitsCbobox.SelectedValuePath =
“Dbits”
; -
DataBitsCbobox.DisplayMemberPath =
“Dbits”
; -
//↑↑↑↑↑↑↑↑↑数据位下拉控件↑↑↑↑↑↑↑↑↑
-
-
//↓↓↓↓↓↓↓↓↓停止位下拉控件↓↓↓↓↓↓↓↓↓
-
IList<customer> stopBits =
new
List<customer>();
//停止位集合
-
stopBits.Add(
new
customer() { Sbits =
“1”
}); -
stopBits.Add(
new
customer() { Sbits =
“1.5”
}); -
stopBits.Add(
new
customer() { Sbits =
“2”
}); -
StopBitsCbobox.ItemsSource = stopBits;
-
StopBitsCbobox.SelectedValuePath =
“Sbits”
; -
StopBitsCbobox.DisplayMemberPath =
“Sbits”
; -
//↑↑↑↑↑↑↑↑↑停止位下拉控件↑↑↑↑↑↑↑↑↑
-
-
//↓↓↓↓↓↓↓↓↓默认设置↓↓↓↓↓↓↓↓↓
-
RateListCbobox.SelectedValue =
“9600”
;
//波特率默认设置9600
-
ParityComCbobox.SelectedValue =
“0”
;
//校验位默认设置值为0,对应NONE
-
DataBitsCbobox.SelectedValue =
“8”
;
//数据位默认设置8位
-
StopBitsCbobox.SelectedValue =
“1”
;
//停止位默认设置1
-
ComPort.ReadTimeout =
8000
;
//串口读超时8秒
-
ComPort.WriteTimeout =
8000
;
//串口写超时8秒,在1ms自动发送数据时拔掉串口,写超时5秒后,会自动停止发送,如果无超时设定,这时程序假死
-
ComPort.ReadBufferSize =
1024
;
//数据读缓存
-
ComPort.WriteBufferSize =
1024
;
//数据写缓存
-
sendBtn.IsEnabled =
false
;
//发送按钮初始化为不可用状态
-
sendModeCheck.IsChecked =
false
;
//发送模式默认为未选中状态
-
recModeCheck.IsChecked =
false
;
//接收模式默认为未选中状态
-
//↑↑↑↑↑↑↑↑↑默认设置↑↑↑↑↑↑↑↑↑
-
ComPort.DataReceived +=
new
SerialDataReceivedEventHandler(ComReceive);
//串口接收中断
-
autoSendTick.Tick +=
new
EventHandler(autoSend);
//定时发送中断
-
-
Thread _ComRec =
new
Thread(
new
ThreadStart(ComRec));
//查询串口接收数据线程声明
-
_ComRec.Start();
//启动线程
-
-
-
}
-
-
-
private
void
Window_Closing
(
object
sender, System.ComponentModel.CancelEventArgs e
)
//关闭窗口closing
-
{
-
MessageBoxResult result = MessageBox.Show(
“确认是否要退出?”
,
“退出”
, MessageBoxButton.YesNo);
//显示确认窗口
-
if
(result == MessageBoxResult.No) -
{
-
e.Cancel =
true
;
//取消操作
-
}
-
}
-
-
private
void
Window_Closed
(
object
sender, EventArgs e
)
//关闭窗口确认后closed ALT+F4
-
{
-
Application.Current.Shutdown();
//先停止线程,然后终止进程.
-
Environment.Exit(
0
);
//直接终止进程.
-
}
-
-
public
class
customer
//各下拉控件访问接口
-
{
-
-
public
string
com {
get
;
set
; }
//可用串口
-
public
string
com1 {
get
;
set
; }
//可用串口
-
public
string
BaudRate {
get
;
set
; }
//波特率
-
public
string
Parity {
get
;
set
; }
//校验位
-
public
string
ParityValue {
get
;
set
; }
//校验位对应值
-
public
string
Dbits {
get
;
set
; }
//数据位
-
public
string
Sbits {
get
;
set
; }
//停止位
-
-
-
}
-
-
-
private
void
Button_Open
(
object
sender, RoutedEventArgs e
)
//打开/关闭串口事件
-
{
-
if
(AvailableComCbobox.SelectedValue ==
null
)
//先判断是否有可用串口
-
{
-
MessageBox.Show(
“无可用串口,无法打开!”
); -
return
;
//没有串口,提示后直接返回
-
}
-
-
if
(ComPortIsOpen ==
false
)
//ComPortIsOpen == false当前串口为关闭状态,按钮事件为打开串口
-
{
-
-
try
//尝试打开串口
-
{
-
ComPort.PortName = AvailableComCbobox.SelectedValue.ToString();
//设置要打开的串口
-
ComPort.BaudRate = Convert.ToInt32(RateListCbobox.SelectedValue);
//设置当前波特率
-
ComPort.Parity = (Parity)Convert.ToInt32(ParityComCbobox.SelectedValue);
//设置当前校验位
-
ComPort.DataBits = Convert.ToInt32(DataBitsCbobox.SelectedValue);
//设置当前数据位
-
ComPort.StopBits = (StopBits)Convert.ToDouble(StopBitsCbobox.SelectedValue);
//设置当前停止位
-
ComPort.Open();
//打开串口
-
-
}
-
catch
//如果串口被其他占用,则无法打开
-
{
-
MessageBox.Show(
“无法打开串口,请检测此串口是否有效或被其他占用!”
); -
GetPort();
//刷新当前可用串口
-
return
;
//无法打开串口,提示后直接返回
-
}
-
-
//↓↓↓↓↓↓↓↓↓成功打开串口后的设置↓↓↓↓↓↓↓↓↓
-
openBtn.Content =
“关闭串口”
;
//按钮显示改为“关闭按钮”
-
OpenImage.Source =
new
BitmapImage(
new
Uri(
“image\\On.png”
, UriKind.Relative));
//开关状态图片切换为ON
-
ComPortIsOpen =
true
;
//串口打开状态字改为true
-
WaitClose =
false
;
//等待关闭串口状态改为false
-
sendBtn.IsEnabled =
true
;
//使能“发送数据”按钮
-
defaultSet.IsEnabled =
false
;
//打开串口后失能重置功能
-
AvailableComCbobox.IsEnabled =
false
;
//失能可用串口控件
-
RateListCbobox.IsEnabled =
false
;
//失能可用波特率控件
-
ParityComCbobox.IsEnabled =
false
;
//失能可用校验位控件
-
DataBitsCbobox.IsEnabled =
false
;
//失能可用数据位控件
-
StopBitsCbobox.IsEnabled =
false
;
//失能可用停止位控件
-
//↑↑↑↑↑↑↑↑↑成功打开串口后的设置↑↑↑↑↑↑↑↑↑
-
-
if
(autoSendCheck.IsChecked ==
true
)
//如果打开前,自动发送控件就被选中,则打开串口后自动开始发送数据
-
{
-
autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));
//设置自动发送间隔
-
autoSendTick.Start();
//开启自动发送
-
}
-
-
}
-
-
-
else
//ComPortIsOpen == true,当前串口为打开状态,按钮事件为关闭串口
-
{
-
try
//尝试关闭串口
-
{
-
autoSendTick.Stop();
//停止自动发送
-
autoSendCheck.IsChecked =
false
;
//停止自动发送控件改为未选中状态
-
ComPort.DiscardOutBuffer();
//清发送缓存
-
ComPort.DiscardInBuffer();
//清接收缓存
-
WaitClose =
true
;
//激活正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口
-
while
(Listening)
//判断invoke是否结束
-
{
-
DispatcherHelper.DoEvents();
//循环时,仍进行等待事件中的进程,该方法为winform中的方法,WPF里面没有,这里在后面自己实现
-
}
-
ComPort.Close();
//关闭串口
-
WaitClose =
false
;
//关闭正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口
-
SetAfterClose();
//成功关闭串口或串口丢失后的设置
-
}
-
-
catch
//如果在未关闭串口前,串口就已丢失,这时关闭串口会出现异常
-
{
-
if
(ComPort.IsOpen ==
false
)
//判断当前串口状态,如果ComPort.IsOpen==false,说明串口已丢失
-
{
-
SetComLose();
-
}
-
else
//未知原因,无法关闭串口
-
{
-
MessageBox.Show(
“无法关闭串口,原因未知!”
); -
return
;
//无法关闭串口,提示后直接返回
-
}
-
}
-
}
-
-
-
}
-
-
private
void
Button_Send
(
object
sender, RoutedEventArgs e
)
//发送数据按钮点击事件
-
{
-
Send();
//调用发送方法
-
}
-
-
void
autoSend
(
object
sender, EventArgs e
)
//自动发送
-
{
-
Send();
//调用发送方法
-
}
-
void
Send
(
)
//发送数据,分为多线程方式和单线程方式
-
{
-
-
if
(Sending ==
true
)
return
;
//如果当前正在发送,则取消本次发送,本句注释后,可能阻塞在ComSend的lock处
-
_ComSend =
new
Thread(
new
ParameterizedThreadStart(ComSend));
//new发送线程
-
SendSet.SendSetData = sendTBox.Text;
//发送数据线程传递参数的结构体–发送的数据
-
SendSet.SendSetMode = sendModeCheck.IsChecked;
//发送数据线程传递参数的结构体–发送方式
-
_ComSend.Start(SendSet);
//发送线程启动
-
-
ComSend();
//单线程发送方法
-
-
}
-
-
private
void
ComSend
(
Object obj
)
//发送数据 独立线程方法 发送数据时UI可以响应
-
{
-
-
lock
(
this
)
//由于send()中的if (Sending == true) return,所以这里不会产生阻塞,如果没有那句,多次启动该线程,会在此处排队
-
{
-
Sending =
true
;
//正在发生状态字
-
byte
[] sendBuffer =
null
;
//发送数据缓冲区
-
string
sendData = SendSet.SendSetData;
//复制发送数据,以免发送过程中数据被手动改变
-
if
(SendSet.SendSetMode ==
true
)
//16进制发送
-
{
-
try
//尝试将发送的数据转为16进制Hex
-
{
-
sendData = sendData.Replace(
” ”
,
“”
);
//去除16进制数据中所有空格
-
sendData = sendData.Replace(
“\r”
,
“”
);
//去除16进制数据中所有换行
-
sendData = sendData.Replace(
“\n”
,
“”
);
//去除16进制数据中所有换行
-
if
(sendData.Length ==
1
)
//数据长度为1的时候,在数据前补0
-
{
-
sendData =
“0”
+ sendData; -
}
-
else
if
(sendData.Length %
2
!=
0
)
//数据长度为奇数位时,去除最后一位数据
-
{
-
sendData = sendData.Remove(sendData.Length –
1
,
1
); -
}
-
-
List<
string
> sendData16 =
new
List<
string
>();
//将发送的数据,2个合为1个,然后放在该缓存里 如:123456→12,34,56
-
for
(
int
i =
0
; i < sendData.Length; i +=
2
) -
{
-
sendData16.Add(sendData.Substring(i,
2
)); -
}
-
sendBuffer =
new
byte
[sendData16.Count];
//sendBuffer的长度设置为:发送的数据2合1后的字节数
-
for
(
int
i =
0
; i < sendData16.Count; i++) -
{
-
sendBuffer[i] = (
byte
)(Convert.ToInt32(sendData16[i],
16
));
//发送数据改为16进制
-
}
-
}
-
catch
//无法转为16进制时,出现异常
-
{
-
UIAction(() =>
-
{
-
autoSendCheck.IsChecked =
false
;
//自动发送改为未选中
-
autoSendTick.Stop();
//关闭自动发送
-
MessageBox.Show(
“请输入正确的16进制数据”
); -
});
-
-
Sending =
false
;
//关闭正在发送状态
-
_ComSend.Abort();
//终止本线程
-
return
;
//输入的16进制数据错误,无法发送,提示后返回
-
}
-
-
}
-
else
//ASCII码文本发送
-
{
-
sendBuffer = System.Text.Encoding.Default.GetBytes(sendData);
//转码
-
}
-
try
//尝试发送数据
-
{
//如果发送字节数大于1000,则每1000字节发送一次
-
int
sendTimes = (sendBuffer.Length /
1000
);
//发送次数
-
for
(
int
i =
0
; i < sendTimes; i++)
//每次发生1000Bytes
-
{
-
ComPort.Write(sendBuffer, i *
1000
,
1000
);
//发送sendBuffer中从第i * 1000字节开始的1000Bytes
-
UIAction(() =>
//激活UI
-
{
-
sendCount.Text = (Convert.ToInt32(sendCount.Text) +
1000
).ToString();
//刷新发送字节数
-
});
-
}
-
if
(sendBuffer.Length %
1000
!=
0
)
//发送字节小于1000Bytes或上面发送剩余的数据
-
{
-
ComPort.Write(sendBuffer, sendTimes *
1000
, sendBuffer.Length %
1000
); -
UIAction(() =>
-
{
-
sendCount.Text = (Convert.ToInt32(sendCount.Text) + sendBuffer.Length %
1000
).ToString();
//刷新发送字节数
-
});
-
}
-
-
-
}
-
catch
//如果无法发送,产生异常
-
{
-
UIAction(() =>
//激活UI
-
{
-
if
(ComPort.IsOpen ==
false
)
//如果ComPort.IsOpen == false,说明串口已丢失
-
{
-
SetComLose();
//串口丢失后的设置
-
}
-
else
-
{
-
MessageBox.Show(
“无法发送数据,原因未知!”
); -
}
-
});
-
}
-
//sendScrol.ScrollToBottom();//发送数据区滚动到底部
-
Sending =
false
;
//关闭正在发送状态
-
_ComSend.Abort();
//终止本线程
-
}
-
-
}
-
-
private
void
ComSend
(
)
//发送数据 普通方法,发送数据过程中UI会失去响应
-
{
-
-
byte
[] sendBuffer =
null
;
//发送数据缓冲区
-
string
sendData = sendTBox.Text;
//复制发送数据,以免发送过程中数据被手动改变
-
if
(sendModeCheck.IsChecked ==
true
)
//16进制发送
-
{
-
try
//尝试将发送的数据转为16进制Hex
-
{
-
sendData = sendData.Replace(
” ”
,
“”
);
//去除16进制数据中所有空格
-
sendData = sendData.Replace(
“\r”
,
“”
);
//去除16进制数据中所有换行
-
sendData = sendData.Replace(
“\n”
,
“”
);
//去除16进制数据中所有换行
-
if
(sendData.Length ==
1
)
//数据长度为1的时候,在数据前补0
-
{
-
sendData =
“0”
+ sendData; -
}
-
else
if
(sendData.Length %
2
!=
0
)
//数据长度为奇数位时,去除最后一位数据
-
{
-
sendData = sendData.Remove(sendData.Length –
1
,
1
); -
}
-
-
List<
string
> sendData16 =
new
List<
string
>();
//将发送的数据,2个合为1个,然后放在该缓存里 如:123456→12,34,56
-
for
(
int
i =
0
; i < sendData.Length; i +=
2
) -
{
-
sendData16.Add(sendData.Substring(i,
2
)); -
}
-
sendBuffer =
new
byte
[sendData16.Count];
//sendBuffer的长度设置为:发送的数据2合1后的字节数
-
for
(
int
i =
0
; i < sendData16.Count; i++) -
{
-
sendBuffer[i] = (
byte
)(Convert.ToInt32(sendData16[i],
16
));
//发送数据改为16进制
-
}
-
}
-
catch
//无法转为16进制时,出现异常
-
{
-
autoSendCheck.IsChecked =
false
;
//自动发送改为未选中
-
autoSendTick.Stop();
//关闭自动发送
-
MessageBox.Show(
“请输入正确的16进制数据”
); -
return
;
//输入的16进制数据错误,无法发送,提示后返回
-
}
-
-
}
-
else
//ASCII码文本发送
-
{
-
sendBuffer = System.Text.Encoding.Default.GetBytes(sendData);
//转码
-
}
-
-
try
//尝试发送数据
-
{
//如果发送字节数大于1000,则每1000字节发送一次
-
int
sendTimes = (sendBuffer.Length /
1000
);
//发送次数
-
for
(
int
i =
0
; i < sendTimes; i++)
//每次发生1000Bytes
-
{
-
ComPort.Write(sendBuffer, i*
1000
,
1000
);
//发送sendBuffer中从第i * 1000字节开始的1000Bytes
-
sendCount.Text = (Convert.ToInt32(sendCount.Text) +
1000
).ToString();
//刷新发送字节数
-
}
-
if
(sendBuffer.Length %
1000
!=
0
) -
{
-
ComPort.Write(sendBuffer, sendTimes *
1000
, sendBuffer.Length %
1000
);
//发送字节小于1000Bytes或上面发送剩余的数据
-
sendCount.Text = (Convert.ToInt32(sendCount.Text) + sendBuffer.Length %
1000
).ToString();
//刷新发送字节数
-
}
-
}
-
catch
//如果无法发送,产生异常
-
{
-
if
(ComPort.IsOpen ==
false
)
//如果ComPort.IsOpen == false,说明串口已丢失
-
{
-
SetComLose();
//串口丢失后相关设置
-
}
-
else
-
{
-
MessageBox.Show(
“无法发送数据,原因未知!”
); -
}
-
}
-
//sendScrol.ScrollToBottom();//发送数据区滚动到底部
-
-
}
-
-
-
-
-
private
void
ComReceive
(
object
sender, SerialDataReceivedEventArgs e
)
//接收数据 中断只标志有数据需要读取,读取操作在中断外进行
-
{
-
if
(WaitClose)
return
;
//如果正在关闭串口,则直接返回
-
Thread.Sleep(
10
);
//发送和接收均为文本时,接收中为加入判断是否为文字的算法,发送你(C4E3),接收可能识别为C4,E3,可用在这里加延时解决
-
if
(recStaus)
//如果已经开启接收
-
{
-
byte
[] recBuffer;
//接收缓冲区
-
try
-
{
-
recBuffer =
new
byte
[ComPort.BytesToRead];
//接收数据缓存大小
-
ComPort.Read(recBuffer,
0
, recBuffer.Length);
//读取数据
-
recQueue.Enqueue(recBuffer);
//读取数据入列Enqueue(全局)
-
}
-
catch
-
{
-
UIAction(() =>
-
{
-
if
(ComPort.IsOpen ==
false
)
//如果ComPort.IsOpen == false,说明串口已丢失
-
{
-
SetComLose();
//串口丢失后相关设置
-
}
-
else
-
{
-
MessageBox.Show(
“无法接收数据,原因未知!”
); -
}
-
});
-
}
-
-
}
-
else
//暂停接收
-
{
-
ComPort.DiscardInBuffer();
//清接收缓存
-
}
-
}
-
void
ComRec
(
)
//接收线程,窗口初始化中就开始启动运行
-
{
-
while
(
true
)
//一直查询串口接收线程中是否有新数据
-
{
-
if
(recQueue.Count >
0
)
//当串口接收线程中有新的数据时候,队列中有新进的成员recQueue.Count > 0
-
{
-
string
recData;
//接收数据转码后缓存
-
byte
[] recBuffer = (
byte
[])recQueue.Dequeue();
//出列Dequeue(全局)
-
recData = System.Text.Encoding.Default.GetString(recBuffer);
//转码
-
UIAction(() =>
-
{
-
if
(recModeCheck.IsChecked ==
false
)
//接收模式为ASCII文本模式
-
{
-
recTBox.Text += recData;
//加显到接收区
-
}
-
else
-
{
-
StringBuilder recBuffer16 =
new
StringBuilder();
//定义16进制接收缓存
-
for
(
int
i =
0
; i < recBuffer.Length; i++) -
{
-
recBuffer16.AppendFormat(
“{0:X2}”
+
” ”
, recBuffer[i]);
//X2表示十六进制格式(大写),域宽2位,不足的左边填0。
-
}
-
recTBox.Text += recBuffer16.ToString();
//加显到接收区
-
}
-
recCount.Text = (Convert.ToInt32(recCount.Text) + recBuffer.Length).ToString();
//接收数据字节数
-
recScrol.ScrollToBottom();
//接收文本框滚动至底部
-
});
-
}
-
else
-
Thread.Sleep(
100
);
//如果不延时,一直查询,将占用CPU过高
-
}
-
-
}
-
-
private
void
ComReceive
(
object
sender, SerialDataReceivedEventArgs e
)
//接收数据 数据在接收中断里面处理
-
{
-
if
(WaitClose)
return
;
//如果正在关闭串口,则直接返回
-
if
(recStaus)
//如果已经开启接收
-
{
-
try
-
{
-
Listening =
true
;
///
/设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。
-
Thread.Sleep(
10
);
//发送和接收均为文本时,接收中为加入判断是否为文字的算法,发送你(C4E3),接收可能识别为C4,E3,可用在这里加延时解决
-
string
recData;
//接收数据转码后缓存
-
byte
[] recBuffer =
new
byte
[ComPort.BytesToRead];
//接收数据缓存
-
ComPort.Read(recBuffer,
0
, recBuffer.Length);
//读取数据
-
recData = System.Text.Encoding.Default.GetString(recBuffer);
//转码
-
this
.recTBox.Dispatcher.Invoke(
//WPF为单线程,此接收中断线程不能对WPF进行操作,用如下方法才可操作
-
new
Action( -
delegate
-
{
-
recCount.Text = (Convert.ToInt32(recCount.Text) + recBuffer.Length).ToString();
//接收数据字节数
-
if
(recModeCheck.IsChecked ==
false
)
//接收模式为ASCII文本模式
-
{
-
recTBox.Text += recData;
//加显到接收区
-
-
}
-
else
-
{
-
StringBuilder recBuffer16 =
new
StringBuilder();
//定义16进制接收缓存
-
for
(
int
i =
0
; i < recBuffer.Length; i++) -
{
-
recBuffer16.AppendFormat(
“{0:X2}”
+
” ”
, recBuffer[i]);
//X2表示十六进制格式(大写),域宽2位,不足的左边填0。
-
}
-
recTBox.Text += recBuffer16.ToString();
//加显到接收区
-
}
-
recScrol.ScrollToBottom();
//接收文本框滚动至底部
-
}
-
)
-
);
-
-
}
-
finally
-
{
-
Listening =
false
;
//UI使用结束,用于关闭串口时判断,避免自动发送时拔掉串口,陷入死循环
-
}
-
-
}
-
else
//暂停接收
-
{
-
ComPort.DiscardInBuffer();
//清接收缓存
-
}
-
-
-
}
-
-
void
UIAction
(
Action action
)
//在主线程外激活线程方法
-
{
-
System.Threading.SynchronizationContext.SetSynchronizationContext(
new
System.Windows.Threading.DispatcherSynchronizationContext(App.Current.Dispatcher)); -
System.Threading.SynchronizationContext.Current.Post(_ => action(),
null
); -
}
-
private
void
SetAfterClose
(
)
//成功关闭串口或串口丢失后的设置
-
{
-
-
openBtn.Content =
“打开串口”
;
//按钮显示为“打开串口”
-
OpenImage.Source =
new
BitmapImage(
new
Uri(
“image\\Off.png”
, UriKind.Relative)); -
ComPortIsOpen =
false
;
//串口状态设置为关闭状态
-
sendBtn.IsEnabled =
false
;
//失能发送数据按钮
-
defaultSet.IsEnabled =
true
;
//打开串口后使能重置功能
-
AvailableComCbobox.IsEnabled =
true
;
//使能可用串口控件
-
RateListCbobox.IsEnabled =
true
;
//使能可用波特率下拉控件
-
ParityComCbobox.IsEnabled =
true
;
//使能可用校验位下拉控件
-
DataBitsCbobox.IsEnabled =
true
;
//使能数据位下拉控件
-
StopBitsCbobox.IsEnabled =
true
;
//使能停止位下拉控件
-
}
-
private
void
SetComLose
(
)
//成功关闭串口或串口丢失后的设置
-
{
-
autoSendTick.Stop();
//串口丢失后要关闭自动发送
-
autoSendCheck.IsChecked =
false
;
//自动发送改为未选中
-
WaitClose =
true
;
//;//激活正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口
-
while
(Listening)
//判断invoke是否结束
-
{
-
DispatcherHelper.DoEvents();
//循环时,仍进行等待事件中的进程,该方法为winform中的方法,WPF里面没有,这里在后面自己实现
-
}
-
MessageBox.Show(
“串口已丢失”
); -
WaitClose =
false
;
//关闭正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口
-
GetPort();
//刷新可用串口
-
SetAfterClose();
//成功关闭串口或串口丢失后的设置
-
}
-
-
-
private
void
AvailableComCbobox_PreviewMouseDown
(
object
sender, MouseButtonEventArgs e
)
//刷新可用串口
-
{
-
GetPort();
//刷新可用串口
-
}
-
-
-
private
void
GetPort
(
)
//刷新可用串口
-
{
-
-
comList.Clear();
//情况控件链接资源
-
AvailableComCbobox.DisplayMemberPath =
“com1”
; -
AvailableComCbobox.SelectedValuePath =
null
;
//路径都指为空,清空下拉控件显示,下面重新添加
-
-
ports =
new
string
[SerialPort.GetPortNames().Length];
//重新定义可用串口数组长度
-
ports = SerialPort.GetPortNames();
//获取可用串口
-
if
(ports.Length >
0
)
//有可用串口
-
{
-
for
(
int
i =
0
; i < ports.Length; i++) -
{
-
comList.Add(
new
customer() { com = ports[i] });
//下拉控件里添加可用串口
-
}
-
AvailableComCbobox.ItemsSource = comList;
//可用串口下拉控件资源路径
-
AvailableComCbobox.DisplayMemberPath =
“com”
;
//可用串口下拉控件显示路径
-
AvailableComCbobox.SelectedValuePath =
“com”
;
//可用串口下拉控件值路径
-
}
-
-
-
}
-
-
-
private
void
sendClearBtn_Click
(
object
sender, RoutedEventArgs e
)
//清空发送区
-
{
-
sendTBox.Text =
“”
; -
}
-
-
private
void
recClearBtn_Click
(
object
sender, RoutedEventArgs e
)
//清空接收区
-
{
-
recTBox.Text =
“”
; -
}
-
-
-
private
void
countClearBtn_Click
(
object
sender, RoutedEventArgs e
)
//计数清零
-
{
-
sendCount.Text =
“0”
; -
recCount.Text =
“0”
; -
}
-
-
private
void
stopRecBtn_Click
(
object
sender, RoutedEventArgs e
)
//暂停/开启接收按钮事件
-
{
-
if
(recStaus ==
true
)
//当前为开启接收状态
-
{
-
recStaus =
false
;
//暂停接收
-
stopRecBtn.Content =
“开启接收”
;
//按钮显示为开启接收
-
recPrompt.Visibility = Visibility.Visible;
//显示已暂停接收提示
-
recBorder.Opacity =
0
;
//接收区透明度改为0
-
}
-
else
//当前状态为关闭接收状态
-
{
-
recStaus =
true
;
//开启接收
-
stopRecBtn.Content =
“暂停接收”
;
//按钮显示状态改为暂停接收
-
recPrompt.Visibility = Visibility.Hidden;
//隐藏已暂停接收提示
-
recBorder.Opacity =
0.4
;
///
/接收区透明度改为0.4
-
}
-
}
-
-
-
-
-
private
void
autoSendCheck_Click
(
object
sender, RoutedEventArgs e
)
//自动发送控件点击事件
-
{
-
-
if
(autoSendCheck.IsChecked ==
true
&& ComPort.IsOpen ==
true
)
//如果当前状态为开启自动发送且串口已打开,则开始自动发送
-
{
-
autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));
//设置自动发送间隔
-
autoSendTick.Start();
//开始自动发送定时器
-
}
-
else
//点击之前为开启自动发送状态,点击后关闭自动发送
-
{
-
autoSendTick.Stop();
//关闭自动发送定时器
-
}
-
}
-
-
private
void
Time_KeyDown
(
object
sender, KeyEventArgs e
)
//发送周期文本控件-键盘按键事件
-
{
-
if
(e.Key >= Key.D0 && e.Key <= Key.D9 || e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9)
//只能输入数字
-
{
-
e.Handled =
false
; -
}
-
else
e.Handled =
true
; -
if
(e.Key == Key.Enter)
//输入回车
-
{
-
if
(Time.Text.Length ==
0
|| Convert.ToInt32(Time.Text) ==
0
)
//时间为空或时间等于0,设置为1000
-
{
-
Time.Text =
“1000”
; -
}
-
autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));
//设置自动发送周期
-
}
-
}
-
-
private
void
Time_TextChanged
(
object
sender, TextChangedEventArgs e
)
//发送周期文本控件-文本内容改变事件,与上面Time_KeyDown事件相比,可以防止粘贴进来非数字数据
-
{
-
//只允许输入数字
-
TextBox textBox = sender
as
TextBox; -
TextChange[] change =
new
TextChange[e.Changes.Count]; -
byte
[] checkText =
new
byte
[textBox.Text.Length]; -
bool
result =
true
; -
e.Changes.CopyTo(change,
0
); -
int
offset = change[
0
].Offset; -
checkText = System.Text.Encoding.Default.GetBytes(textBox.Text);
-
for
(
int
i =
0
; i < textBox.Text.Length; i++) -
{
-
result &=
0x2F
< (Convert.ToInt32(checkText[i])) & (Convert.ToInt32(checkText[i])) <
0x3A
;
//0x2f-0x3a之间是数字0-10的ASCII码
-
}
-
if
(change[
0
].AddedLength >
0
) -
{
-
-
if
(!result || Convert.ToInt32(textBox.Text) >
100000
)
//不是数字或数据大于100000,取消本次change
-
{
-
textBox.Text = textBox.Text.Remove(offset, change[
0
].AddedLength); -
textBox.Select(offset,
0
); -
}
-
}
-
-
}
-
-
private
void
Time_LostFocus
(
object
sender, RoutedEventArgs e
)
//发送周期文本控件-失去事件
-
{
-
if
(Time.Text.Length ==
0
|| Convert.ToInt32(Time.Text) ==
0
)
//时间为空或时间等于0,设置为1000
-
{
-
Time.Text =
“1000”
; -
}
-
autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));
//设置自动发送周期
-
}
-
-
//模拟 Winfrom 中 Application.DoEvents() 详见 http://www.silverlightchina.net/html/study/WPF/2010/1216/4186.html?1292685167
-
public
static
class
DispatcherHelper
-
{
-
[
] -
public
static
void
DoEvents
(
)
-
{
-
DispatcherFrame frame =
new
DispatcherFrame(); -
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
new
DispatcherOperationCallback(ExitFrames), frame); -
try
{ Dispatcher.PushFrame(frame); } -
catch
(InvalidOperationException) { } -
}
-
private
static
object
ExitFrames
(
object
frame
)
-
{
-
((DispatcherFrame)frame).Continue =
false
; -
return
null
; -
}
-
}
-
-
private
void
defaultSet_Click
(
object
sender, RoutedEventArgs e
)
//重置按钮click事件
-
{
-
RateListCbobox.SelectedValue =
“9600”
;
//波特率默认设置9600
-
ParityComCbobox.SelectedValue =
“0”
;
//校验位默认设置值为0,对应NONE
-
DataBitsCbobox.SelectedValue =
“8”
;
//数据位默认设置8位
-
StopBitsCbobox.SelectedValue =
“1”
;
//停止位默认设置1
-
}
-
private
void
FileOpen
(
object
sender, ExecutedRoutedEventArgs e
)
//打开文件快捷键事件crtl+O
-
{
-
OpenFileDialog open_fd =
new
OpenFileDialog();
//调用系统打开文件窗口
-
open_fd.Filter =
“TXT文本|*.txt”
;
//文件过滤器
-
if
(open_fd.ShowDialog() ==
true
)
//选择了文件
-
{
-
sendTBox.Text = File.ReadAllText(open_fd.FileName);
//读TXT方法1 简单,快捷,为StreamReader的封装
-
//StreamReader sr = new StreamReader(open_fd.FileName);//读TXT方法2 复杂,功能强大
-
//sendTBox.Text = sr.ReadToEnd();//调用ReadToEnd方法读取选中文件的全部内容
-
//sr.Close();//关闭当前文件读取流
-
}
-
}
-
private
void
FileSave
(
object
sender, RoutedEventArgs e
)
//保存数据按钮crtl+S
-
{
-
SaveModWindow SaveMod =
new
SaveModWindow();
//new保存数据方式窗口
-
SaveMod.Owner =
this
;
//赋予主窗口,子窗口打开后,再次点击主窗口,子窗口闪烁
-
SaveMod.ShowDialog();
//ShowDialog方式打开保存数据方式窗口
-
if
(SaveMod.mode ==
“new”
)
//保存为新文件
-
{
-
SaveNew();
//保存为新文件
-
}
-
else
if
(SaveMod.mode ==
“old”
)
//保存到已有文件
-
{
-
SaveOld();
//保存到已有文件
-
}
-
else
//取消
-
{
-
return
; -
}
-
-
}
-
private
void
SaveNew_Click
(
object
sender, RoutedEventArgs e
)
//文件-保存-保存为新文件click事件
-
{
-
SaveNew();
//保存为新文件
-
}
-
private
void
SaveOld_Click
(
object
sender, RoutedEventArgs e
)
//文件-保存-保存到已有文件click事件
-
{
-
SaveOld();
//保存到已有文件
-
}
-
private
void
SaveNew
(
)
//保存为新文件
-
{
-
if
(recTBox.Text ==
string
.Empty)
//接收区数据为空
-
{
-
MessageBox.Show(
“接收区为空,无法保存!”
); -
}
-
else
-
{
-
SaveFileDialog Save_fd =
new
SaveFileDialog();
//调用系统保存文件窗口
-
Save_fd.Filter =
“TXT文本|*.txt”
;
//文件过滤器
-
if
(Save_fd.ShowDialog() ==
true
)
//选择了文件
-
{
-
File.WriteAllText(Save_fd.FileName, recTBox.Text);
//写入新的数据
-
File.AppendAllText(Save_fd.FileName,
“\r\n——”
+ DateTime.Now.ToString() +
“\r\n”
);
//数据后面写入时间戳
-
MessageBox.Show(
“保存成功!”
); -
}
-
-
}
-
}
-
private
void
SaveOld
(
)
//保存到已有文件
-
{
-
if
(recTBox.Text ==
string
.Empty)
//接收区数据为空
-
{
-
MessageBox.Show(
“接收区为空,无法保存!”
); -
}
-
else
-
{
-
OpenFileDialog Open_fd =
new
OpenFileDialog();
//调用系统保存文件窗口
-
Open_fd.Filter =
“TXT文本|*.txt”
;
//文件过滤器
-
if
(Open_fd.ShowDialog() ==
true
)
//选择了文件
-
{
-
File.AppendAllText(Open_fd.FileName, recTBox.Text);
//在打开文件末尾写入数据
-
File.AppendAllText(Open_fd.FileName,
“\r\n——”
+ DateTime.Now.ToString() +
“\r\n”
);
//数据后面写入时间戳
-
MessageBox.Show(
“添加成功!”
); -
}
-
}
-
}
-
-
private
void
info_click
(
object
sender, RoutedEventArgs e
)
//帮助-关于click事件
-
{
-
InfoWindow info =
new
InfoWindow();
//new关于窗口
-
info.Owner =
this
;
//赋予主窗口,子窗口打开后,再次点击主窗口,子窗口闪烁
-
info.Show();
//ShowDialog方式打开关于窗口
-
}
-
-
private
void
feedBack_Click
(
object
sender, RoutedEventArgs e
)
//帮助-反馈click事件
-
{
-
FeedBackWindow feedBack =
new
FeedBackWindow();
//new反馈窗口
-
feedBack.Owner =
this
;
//赋予主窗口,子窗口打开后,再次点击主窗口,子窗口闪烁
-
feedBack.ShowDialog();
//ShowDialog方式打开反馈窗口
-
}
-
-
-
}
-
}