2018最新串列埠讀寫程式簡化版傻瓜式教程
阿新 • • 發佈:2018-12-21
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