同学,你好!
Microsoft Visual Studio 2015
目录
点击Visual C#-WPF应用程序,在下方自定义相关信息。点确定。
建立图标文件夹。右键点击文件夹-点击在文件资源管理器中打开文件夹。将相关图片放进去如果看不到,可能是隐藏了
点击MainWindow.xaml-双击进入MainWindow.xaml.cs进行功能设计
点击文件-新建-项目
点击Visual C#-WPF应用程序,在下方自定义相关信息。点确定。
界面介绍
建立图标文件夹
。右键点击文件夹-点击在文件资源管理器中打开文件夹
。将相关图片放进去
如果看不到,可能是隐藏了
请能力高者自主设计样式。附样式参考代码:
<Window x:Class="你的程序名.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:你的程序名"
mc:Ignorable="d"
Title="你的程序名" Height="570" MinHeight="565" Width="800" MinWidth="500" Closed="Window_Closed" Closing="Window_Closing" Loaded="Window_Loaded" ResizeMode="CanResizeWithGrip" BorderThickness="0">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.Close"
Executed="Window_Closed" />
</Window.CommandBindings>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="76*"/>
<RowDefinition/>
<RowDefinition Height="0*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="225*"/>
<ColumnDefinition Width="49*"/>
<ColumnDefinition Width="418*"/>
</Grid.ColumnDefinitions>
<Grid Grid.ColumnSpan="3" Grid.RowSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="83*"></ColumnDefinition>
<ColumnDefinition Width="263*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0"></RowDefinition>
<RowDefinition MinHeight="320" Height="361*"></RowDefinition>
<RowDefinition MaxHeight="158" Height="158*"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" VerticalAlignment="Top" Height="0" Grid.RowSpan="2" >
<Menu DockPanel.Dock="Top">
<Menu.Background>
<ImageBrush/>
</Menu.Background>
</Menu>
</DockPanel>
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" ></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Row="0">
<Grid.RowDefinitions>
<RowDefinition Height="230"></RowDefinition>
<RowDefinition Height="90"></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Grid.Column="0" Margin="0,4,0,87" Grid.RowSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="60" ></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition Height="50"></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Name="Port" Text="串口号:" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="0" Grid.Column="0" Margin="10,25,10,22" Grid.RowSpan="2"></TextBlock>
<TextBlock Name="BaundRate" Text="波特率:" HorizontalAlignment="Center" VerticalAlignment="Center" Grid.Row="1" Grid.Column="0" Margin="10,25,10,22" Grid.RowSpan="2"></TextBlock>
<ComboBox Name="AvailableComCbobox" ToolTip="点击刷新" Grid.Row="0" Grid.Column="1" VerticalAlignment="Top" Margin="0,22,0,0" PreviewMouseDown="AvailableComCbobox_PreviewMouseDown" Grid.RowSpan="2" />
<ComboBox Name="RateListCbobox" Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" Margin="0,22,0,0" SelectionChanged="RateListCbobox_SelectionChanged" Grid.RowSpan="2"/>
<Button Content="重置" Name="defaultSet" Grid.Row="4" Grid.ColumnSpan="2" Margin="40,18,48,0" Click="defaultSet_Click" Background="#FFF3F1C1" Grid.RowSpan="2" />
<Button Content="打开通讯端口" Click="Button_Open" Name="openBtn" Grid.Row="6" VerticalAlignment="Center" Height="45" Margin="27,50,33,-45" RenderTransformOrigin="0.444,0" Background="{DynamicResource {x:Static SystemColors.MenuBrushKey}}" Grid.ColumnSpan="2" />
<Image Name="OpenImage" Source="image\Off.png" HorizontalAlignment="Center" VerticalAlignment="Center" Height="40" Grid.Row="6" Width="40" Margin="52,109,58,-99" RenderTransformOrigin="1.4,-0.325" Grid.ColumnSpan="2"/>
</Grid>
<Grid Grid.Row="1" Grid.Column="0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="27*"/>
<ColumnDefinition Width="23*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<CheckBox Content="16进制接收" Name="recModeCheck" FontSize="15" HorizontalAlignment="Left" VerticalAlignment="Center" Margin="23,-124,0,150" Grid.Row="0" Grid.ColumnSpan="2" Height="20"/>
</Grid>
</Grid>
<Grid Grid.Row="0" Grid.Column="1" Margin="5,0,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0*"/>
<ColumnDefinition Width="105*"/>
<ColumnDefinition Width="437*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<TextBlock Margin="10,9,0,1" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"><Run Text=" "/><Run Text="接收区"/><Run Text=":"/></TextBlock>
<Label Content="已暂停接收,点击开启→" Name="recPrompt" Grid.Row="0" Grid.Column="2" Margin="0,0,170,0" HorizontalAlignment="Right" Visibility="Hidden" Foreground="#FFFFFF2F" Background="{x:Null}" />
<Button Content="暂停接收" Name="stopRecBtn" HorizontalAlignment="Right" Grid.Row="0" Grid.Column="2" Width="75" Margin="0,3,102,0" Click="stopRecBtn_Click"/>
<Button Content="清空接收区" Name="recClearBtn" HorizontalAlignment="Right" Grid.Row="0" Grid.Column="2" VerticalAlignment="Bottom" Width="74" Click="recClearBtn_Click" Height="22" Margin="0,0,7,0"/>
<Border Name="recBorder" BorderThickness="1" Margin="11,1,6,1" Grid.Row="1" Grid.Column="0" BorderBrush="#FF911C1C" Background="White" Opacity="0.4" Grid.ColumnSpan="3" />
<ScrollViewer Name="recScrol" VerticalScrollBarVisibility="Auto" Grid.Row="1" Grid.Column="0" Padding="0,0,0,0" Margin="12,2,7,2" Grid.ColumnSpan="3" >
<TextBox x:Name="recTBox" TextWrapping="Wrap" ScrollViewer.VerticalScrollBarVisibility="Auto" AcceptsReturn="True" Background="{x:Null}" ClipToBounds="True" Foreground="Black" TextChanged="recTBox_TextChanged" BorderBrush="#FF8BA08B" IsEnabled="False" />
</ScrollViewer>
<Line Width="500" Height="500" Fill="#FFD13535" Stroke="#FFD81010" OpacityMask="Black" Grid.ColumnSpan="3" Margin="21,0,21,-139" Grid.RowSpan="2"></Line>
</Grid>
</Grid>
<Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="0,1,0,19" Grid.RowSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="101*"/>
<ColumnDefinition Width="97*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="25"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto" ></ColumnDefinition>
</Grid.ColumnDefinitions>
<Border BorderThickness="1" Margin="10,0,0,0" Grid.Row="0" Grid.Column="0" Background="White" Opacity="0.4" />
<ScrollViewer Name="sendScrol" VerticalScrollBarVisibility="Auto" Grid.Row="0" Grid.Column="0" Padding="0,0,0,0" MinHeight="100" MinWidth="460" Margin="10,0,0,0">
<TextBox x:Name="sendTBox" TextWrapping="Wrap" ClipToBounds="True" Background="{x:Null}" AcceptsReturn="True" />
</ScrollViewer>
<Grid Grid.Row="0" Grid.Column="2" Margin="0,3,0,0" >
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="32*"></RowDefinition>
<RowDefinition Height="33*"></RowDefinition>
<RowDefinition Height="32*"></RowDefinition>
<RowDefinition Height="21*"></RowDefinition>
<RowDefinition Height="12*"/>
</Grid.RowDefinitions>
<CheckBox Content="16进制发送" Name="sendModeCheck" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="15" Grid.Row="0" Grid.Column="0" Margin="0,7"/>
<CheckBox Content="自动发送" Name="autoSendCheck" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="15" Grid.Row="1" Grid.Column="0" Click="autoSendCheck_Click" Margin="0,8,0,7" />
<TextBlock Text="周期:" Name="Cycle" HorizontalAlignment="Left" VerticalAlignment="Center" FontSize="15" Grid.Row="2" Grid.Column="0" Margin="0,19,0,14" Grid.RowSpan="2"></TextBlock>
<TextBox Text="1000" Name="Time" HorizontalAlignment="Right" VerticalAlignment="Top" Grid.Row="2" Grid.Column="0" Margin="0,21,23,0" Width="38" KeyDown="Time_KeyDown" LostFocus="Time_LostFocus" TextChanged="Time_TextChanged" Grid.RowSpan="2"/>
<TextBlock Text="ms" Name="Unit" HorizontalAlignment="Right" VerticalAlignment="Center" FontSize="15" Grid.Row="2" Grid.Column="0" Margin="0,19,2,14" Grid.RowSpan="2"></TextBlock>
<Button Content="清空发送区" Name="sendClearBtn" HorizontalAlignment="Center" VerticalAlignment="Center" Click="sendClearBtn_Click" Grid.Row="0" Grid.Column="1" Height="24" Width="80" Margin="5,4"/>
<Button Content="计数清零" x:Name="countClearBtn" HorizontalAlignment="Center" VerticalAlignment="Bottom" Grid.Row="1" Grid.Column="1" Height="25" Width="80" Click="countClearBtn_Click" Margin="5,0"/>
<Button Content="发送数据" Click="Button_Send" x:Name="sendBtn" HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="5,0,5,5" Grid.Row="2" Grid.RowSpan="3" Grid.Column="1" Height="50" Width="80" Background="#FFB9B55A"/>
</Grid>
</Grid>
<TextBlock Text="发送区:" Grid.Row="0" Grid.Column="0" Margin="10,8,0,2" Grid.ColumnSpan="2"/>
</Grid>
<TextBlock Grid.Row="3" Grid.Column="0" Text="发送数:" Margin="10,2,0,3" Grid.ColumnSpan="2"/>
<TextBlock Grid.Row="3" Grid.Column="0" Text="0" Name="sendCount" Margin="80,0,0,2" VerticalAlignment="Bottom" Grid.ColumnSpan="2" Height="15"></TextBlock>
<TextBlock Grid.Row="3" Grid.Column="0" Text="接收数:" Margin="160,0,0,3" VerticalAlignment="Bottom" Grid.ColumnSpan="2" Height="15"></TextBlock>
<TextBlock Grid.Row="3" Grid.Column="1" Text="0" Name="recCount" Margin="41,0,371,3" VerticalAlignment="Bottom" Height="15"/>
</Grid>
</Grid>
</Window>
点击MainWindow.xaml-双击进入MainWindow.xaml.cs进行功能设计
#define MULTITHREAD
using Microsoft.Win32;
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 你的程序名
{
/// <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 ,解决关闭串口时,程序假死, 仅在单线程收发使用,但是在公共代码区有相关设置,所以未用#define隔离
IList<customer> comList = new List<customer>();//可用串口集合
DispatcherTimer autoSendTick = new DispatcherTimer();//定时发送
#if MULTITHREAD
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;//发送模式
}
#endif
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";
//↑↑↑↑↑↑↑↑↑波特率下拉控件↑↑↑↑↑↑↑↑↑
//↓↓↓↓↓↓↓↓↓默认设置↓↓↓↓↓↓↓↓↓
RateListCbobox.SelectedValue = "115200";//波特率默认设置115200
ComPort.ReadTimeout = 8000;//串口读超时8秒
ComPort.WriteTimeout = 8000;//串口写超时8秒,在1ms自动发送数据时拔掉串口,写超时5秒后,会自动停止发送,如果无超时设定,这时程序假死
ComPort.ReadBufferSize = 1024;//数据读缓存
ComPort.WriteBufferSize = 1024;//数据写缓存
sendBtn.IsEnabled = false;//发送按钮初始化为不可用状态
sendModeCheck.IsChecked = true;//发送模式默认为未选中状态
recModeCheck.IsChecked = true;//接收模式默认为未选中状态
//↑↑↑↑↑↑↑↑↑默认设置↑↑↑↑↑↑↑↑↑
ComPort.DataReceived += new SerialDataReceivedEventHandler(ComReceive);//串口接收中断
autoSendTick.Tick += new EventHandler(autoSend);//定时发送中断
#if MULTITHREAD
Thread _ComRec = new Thread(new ThreadStart(ComRec)); //查询串口接收数据线程声明
_ComRec.Start();//启动线程
#endif
}
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; }//波特率
}
private void Button_Open(object sender, RoutedEventArgs e)//打开/关闭串口事件
{
if (AvailableComCbobox.SelectedValue == null)//先判断是否有可用串口
{
MessageBox.Show("无可用串口,无法打开!");
return;//没有串口,提示后直接返回
}
#region 打开串口
if (ComPortIsOpen == false)//ComPortIsOpen == false当前串口为关闭状态,按钮事件为打开串口
{
try//尝试打开串口
{
ComPort.PortName = AvailableComCbobox.SelectedValue.ToString();//设置要打开的串口
ComPort.BaudRate = Convert.ToInt32(RateListCbobox.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;//失能可用波特率控件
//↑↑↑↑↑↑↑↑↑成功打开串口后的设置↑↑↑↑↑↑↑↑↑
if (autoSendCheck.IsChecked == true)//如果打开前,自动发送控件就被选中,则打开串口后自动开始发送数据
{
autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));//设置自动发送间隔
autoSendTick.Start();//开启自动发送
}
}
#endregion
#region 关闭串口
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;//无法关闭串口,提示后直接返回
}
}
}
#endregion
}
private void Button_Send(object sender, RoutedEventArgs e)//发送数据按钮点击事件
{
Send();//调用发送方法
}
void autoSend(object sender, EventArgs e)//自动发送
{
Send();//调用发送方法
}
void Send()//发送数据,分为多线程方式和单线程方式
{
#if MULTITHREAD
if (Sending == true) return;//如果当前正在发送,则取消本次发送,本句注释后,可能阻塞在ComSend的lock处
_ComSend = new Thread(new ParameterizedThreadStart(ComSend)); //new发送线程
SendSet.SendSetData = sendTBox.Text;//发送数据线程传递参数的结构体--发送的数据
SendSet.SendSetMode = sendModeCheck.IsChecked;//发送数据线程传递参数的结构体--发送方式
_ComSend.Start(SendSet);//发送线程启动
#endif
}
#if MULTITHREAD
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 % 2 != 0)//数据长度为奇数位时,去除最后一位数据
{
sendData = sendData + "0";
}
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();//终止本线程
}
}
#endif
#if MULTITHREAD
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过高
}
}
#endif
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;//使能可用波特率下拉控件
}
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 = 1;接收区透明度改为1
}
}
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));//设置自动发送周期
}
public static class DispatcherHelper
{
[SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)]
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 = "115200";//波特率默认设置9600
}
private void recTBox_TextChanged(object sender, TextChangedEventArgs e)
{
}
private void ParityComCbobox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
private void RateListCbobox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
}
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
}
private void sendTBox_TextChanged(object sender, TextChangedEventArgs e)
{
}
}
}
- 想加背景的可在GUI代码区加入以下代码:
<Window.Background >
<ImageBrush ImageSource="图片地址"/>
</Window.Background>
点击生成-点击生成(你的程序名)
成功
期间可点击调试进行测试
调试效果如图
所生成的程序位置:你的程序名\你的程序名\bin\Debug
本文含有隐藏内容,请 开通VIP 后查看