打造最漂亮的串口调试助手(基于WPF + .NET C# VS2013)附源码!

  • Post author:
  • Post category:其他


打造最漂亮的串口调试助手(基于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#源码



  1. #

    define

    MULTITHREAD//多线程收发模式,注释本句则使用单线程模式


  2. //相对单线收发模式,占用系统资源稍微大些,但是执行效果更好,尤其是在大数据收发时的UI反应尤其明显


  3. using

    Microsoft.Win32;


  4. using

    SerialComWindow;


  5. using

    System;


  6. using

    System.Collections;


  7. using

    System.Collections.Generic;


  8. using

    System.IO;


  9. using

    System.IO.Ports;


  10. using

    System.Linq;


  11. using

    System.Security.Permissions;


  12. using

    System.Text;


  13. using

    System.Threading;


  14. using

    System.Threading.Tasks;


  15. using

    System.Windows;


  16. using

    System.Windows.Controls;


  17. using

    System.Windows.Data;


  18. using

    System.Windows.Documents;


  19. using

    System.Windows.Input;


  20. using

    System.Windows.Media;


  21. using

    System.Windows.Media.Imaging;


  22. using

    System.Windows.Navigation;


  23. using

    System.Windows.Shapes;


  24. using

    System.Windows.Threading;


  25. namespace


    SerialCom

  26. {



  27. ///


    <summary>



  28. ///

    MainWindow.xaml 的交互逻辑



  29. ///


    </summary>


  30. public


    partial


    class


    MainWindow

    :

    Window

  31. {

  32. SerialPort ComPort =

    new

    SerialPort();

    //声明一个串口


  33. private


    string

    [] ports;

    //可用串口数组


  34. private


    bool

    recStaus =

    true

    ;

    //接收状态字


  35. private


    bool

    ComPortIsOpen =

    false

    ;

    //COM口开启状态字,在打开/关闭串口中使用,这里没有使用自带的ComPort.IsOpen,因为在串口突然丢失的时候,ComPort.IsOpen会自动false,逻辑混乱


  36. private


    bool

    Listening =

    false

    ;

    //用于检测是否没有执行完invoke相关操作,仅在单线程收发使用,但是在公共代码区有相关设置,所以未用#define隔离


  37. private


    bool

    WaitClose =

    false

    ;

    //invoke里判断是否正在关闭串口是否正在关闭串口,执行Application.DoEvents,并阻止再次invoke ,解决关闭串口时,程序假死,具体参见http://news.ccidnet.com/art/32859/20100524/2067861_4.html 仅在单线程收发使用,但是在公共代码区有相关设置,所以未用#define隔离

  38. IList<customer> comList =

    new

    List<customer>();

    //可用串口集合

  39. DispatcherTimer autoSendTick =

    new

    DispatcherTimer();

    //定时发送


  40. #

    if

    MULTITHREAD


  41. private


    static


    bool

    Sending =

    false

    ;

    //正在发送数据状态字


  42. private


    static

    Thread _ComSend;

    //发送数据线程

  43. Queue recQueue =

    new

    Queue();

    //接收数据过程中,接收数据线程与数据处理线程直接传递的队列,先进先出


  44. private

    SendSetStr SendSet =

    new

    SendSetStr();

    //发送数据线程传递参数的结构体


  45. private


    struct

    SendSetStr

    //发送数据线程传递参数的结构体格式

  46. {


  47. public


    string

    SendSetData;

    //发送的数据


  48. public


    bool

    ? SendSetMode;

    //发送模式

  49. }


  50. #

    endif



  51. public


    MainWindow

    (


    )

    //主窗口

  52. {

  53. InitializeComponent();

    //控件初始化

  54. }



  55. private


    void


    Window_Loaded

    (


    object

    sender, RoutedEventArgs e

    )

    //主窗口初始化

  56. {


  57. //↓↓↓↓↓↓↓↓↓可用串口下拉控件↓↓↓↓↓↓↓↓↓

  58. ports= SerialPort.GetPortNames();

    //获取可用串口


  59. if

    (ports.Length >

    0

    )

    //ports.Length > 0说明有串口可用

  60. {


  61. for

    (

    int

    i =

    0

    ; i < ports.Length; i++)

  62. {

  63. comList.Add(

    new

    customer() { com = ports[i] });

    //下拉控件里添加可用串口

  64. }

  65. AvailableComCbobox.ItemsSource = comList;

    //资源路劲

  66. AvailableComCbobox.DisplayMemberPath =

    “com”

    ;

    //显示路径

  67. AvailableComCbobox.SelectedValuePath =

    “com”

    ;

    //值路径

  68. AvailableComCbobox.SelectedValue = ports[

    0

    ];

    //默认选第1个串口

  69. }


  70. else


    //未检测到串口

  71. {

  72. MessageBox.Show(

    “无可用串口”

    );

  73. }


  74. //↑↑↑↑↑↑↑↑↑可用串口下拉控件↑↑↑↑↑↑↑↑↑


  75. //↓↓↓↓↓↓↓↓↓波特率下拉控件↓↓↓↓↓↓↓↓↓

  76. IList<customer> rateList =

    new

    List<customer>();

    //可用波特率集合

  77. rateList.Add(

    new

    customer() { BaudRate =

    “1200”

    });

  78. rateList.Add(

    new

    customer() { BaudRate =

    “2400”

    });

  79. rateList.Add(

    new

    customer() { BaudRate =

    “4800”

    });

  80. rateList.Add(

    new

    customer() { BaudRate =

    “9600”

    });

  81. rateList.Add(

    new

    customer() { BaudRate =

    “14400”

    });

  82. rateList.Add(

    new

    customer() { BaudRate =

    “19200”

    });

  83. rateList.Add(

    new

    customer() { BaudRate =

    “28800”

    });

  84. rateList.Add(

    new

    customer() { BaudRate =

    “38400”

    });

  85. rateList.Add(

    new

    customer() { BaudRate =

    “57600”

    });

  86. rateList.Add(

    new

    customer() { BaudRate =

    “115200”

    });

  87. RateListCbobox.ItemsSource = rateList;

  88. RateListCbobox.DisplayMemberPath =

    “BaudRate”

    ;

  89. RateListCbobox.SelectedValuePath =

    “BaudRate”

    ;


  90. //↑↑↑↑↑↑↑↑↑波特率下拉控件↑↑↑↑↑↑↑↑↑


  91. //↓↓↓↓↓↓↓↓↓校验位下拉控件↓↓↓↓↓↓↓↓↓

  92. IList<customer> comParity =

    new

    List<customer>();

    //可用校验位集合

  93. comParity.Add(

    new

    customer() { Parity =

    “None”

    , ParityValue =

    “0”

    });

  94. comParity.Add(

    new

    customer() { Parity =

    “Odd”

    , ParityValue =

    “1”

    });

  95. comParity.Add(

    new

    customer() { Parity =

    “Even”

    , ParityValue =

    “2”

    });

  96. comParity.Add(

    new

    customer() { Parity =

    “Mark”

    , ParityValue =

    “3”

    });

  97. comParity.Add(

    new

    customer() { Parity =

    “Space”

    , ParityValue =

    “4”

    });

  98. ParityComCbobox.ItemsSource = comParity;

  99. ParityComCbobox.DisplayMemberPath =

    “Parity”

    ;

  100. ParityComCbobox.SelectedValuePath =

    “ParityValue”

    ;


  101. //↑↑↑↑↑↑↑↑↑校验位下拉控件↑↑↑↑↑↑↑↑↑


  102. //↓↓↓↓↓↓↓↓↓数据位下拉控件↓↓↓↓↓↓↓↓↓

  103. IList<customer> dataBits =

    new

    List<customer>();

    //数据位集合

  104. dataBits.Add(

    new

    customer() { Dbits =

    “8”

    });

  105. dataBits.Add(

    new

    customer() { Dbits =

    “7”

    });

  106. dataBits.Add(

    new

    customer() { Dbits =

    “6”

    });

  107. DataBitsCbobox.ItemsSource = dataBits;

  108. DataBitsCbobox.SelectedValuePath =

    “Dbits”

    ;

  109. DataBitsCbobox.DisplayMemberPath =

    “Dbits”

    ;


  110. //↑↑↑↑↑↑↑↑↑数据位下拉控件↑↑↑↑↑↑↑↑↑


  111. //↓↓↓↓↓↓↓↓↓停止位下拉控件↓↓↓↓↓↓↓↓↓

  112. IList<customer> stopBits =

    new

    List<customer>();

    //停止位集合

  113. stopBits.Add(

    new

    customer() { Sbits =

    “1”

    });

  114. stopBits.Add(

    new

    customer() { Sbits =

    “1.5”

    });

  115. stopBits.Add(

    new

    customer() { Sbits =

    “2”

    });

  116. StopBitsCbobox.ItemsSource = stopBits;

  117. StopBitsCbobox.SelectedValuePath =

    “Sbits”

    ;

  118. StopBitsCbobox.DisplayMemberPath =

    “Sbits”

    ;


  119. //↑↑↑↑↑↑↑↑↑停止位下拉控件↑↑↑↑↑↑↑↑↑


  120. //↓↓↓↓↓↓↓↓↓默认设置↓↓↓↓↓↓↓↓↓

  121. RateListCbobox.SelectedValue =

    “9600”

    ;

    //波特率默认设置9600

  122. ParityComCbobox.SelectedValue =

    “0”

    ;

    //校验位默认设置值为0,对应NONE

  123. DataBitsCbobox.SelectedValue =

    “8”

    ;

    //数据位默认设置8位

  124. StopBitsCbobox.SelectedValue =

    “1”

    ;

    //停止位默认设置1

  125. ComPort.ReadTimeout =

    8000

    ;

    //串口读超时8秒

  126. ComPort.WriteTimeout =

    8000

    ;

    //串口写超时8秒,在1ms自动发送数据时拔掉串口,写超时5秒后,会自动停止发送,如果无超时设定,这时程序假死

  127. ComPort.ReadBufferSize =

    1024

    ;

    //数据读缓存

  128. ComPort.WriteBufferSize =

    1024

    ;

    //数据写缓存

  129. sendBtn.IsEnabled =

    false

    ;

    //发送按钮初始化为不可用状态

  130. sendModeCheck.IsChecked =

    false

    ;

    //发送模式默认为未选中状态

  131. recModeCheck.IsChecked =

    false

    ;

    //接收模式默认为未选中状态


  132. //↑↑↑↑↑↑↑↑↑默认设置↑↑↑↑↑↑↑↑↑

  133. ComPort.DataReceived +=

    new

    SerialDataReceivedEventHandler(ComReceive);

    //串口接收中断

  134. autoSendTick.Tick +=

    new

    EventHandler(autoSend);

    //定时发送中断


  135. #

    if

    MULTITHREAD

  136. Thread _ComRec =

    new

    Thread(

    new

    ThreadStart(ComRec));

    //查询串口接收数据线程声明

  137. _ComRec.Start();

    //启动线程


  138. #

    endif

  139. }



  140. private


    void


    Window_Closing

    (


    object

    sender, System.ComponentModel.CancelEventArgs e

    )

    //关闭窗口closing

  141. {

  142. MessageBoxResult result = MessageBox.Show(

    “确认是否要退出?”

    ,

    “退出”

    , MessageBoxButton.YesNo);

    //显示确认窗口


  143. if

    (result == MessageBoxResult.No)

  144. {

  145. e.Cancel =

    true

    ;

    //取消操作

  146. }

  147. }



  148. private


    void


    Window_Closed

    (


    object

    sender, EventArgs e

    )

    //关闭窗口确认后closed ALT+F4

  149. {

  150. Application.Current.Shutdown();

    //先停止线程,然后终止进程.

  151. Environment.Exit(

    0

    );

    //直接终止进程.

  152. }


  153. public


    class


    customer


    //各下拉控件访问接口

  154. {


  155. public


    string

    com {

    get

    ;

    set

    ; }

    //可用串口


  156. public


    string

    com1 {

    get

    ;

    set

    ; }

    //可用串口


  157. public


    string

    BaudRate {

    get

    ;

    set

    ; }

    //波特率


  158. public


    string

    Parity {

    get

    ;

    set

    ; }

    //校验位


  159. public


    string

    ParityValue {

    get

    ;

    set

    ; }

    //校验位对应值


  160. public


    string

    Dbits {

    get

    ;

    set

    ; }

    //数据位


  161. public


    string

    Sbits {

    get

    ;

    set

    ; }

    //停止位

  162. }



  163. private


    void


    Button_Open

    (


    object

    sender, RoutedEventArgs e

    )

    //打开/关闭串口事件

  164. {


  165. if

    (AvailableComCbobox.SelectedValue ==

    null

    )

    //先判断是否有可用串口

  166. {

  167. MessageBox.Show(

    “无可用串口,无法打开!”

    );


  168. return

    ;

    //没有串口,提示后直接返回

  169. }


  170. #

    region

    打开串口


  171. if

    (ComPortIsOpen ==

    false

    )

    //ComPortIsOpen == false当前串口为关闭状态,按钮事件为打开串口

  172. {


  173. try


    //尝试打开串口

  174. {

  175. ComPort.PortName = AvailableComCbobox.SelectedValue.ToString();

    //设置要打开的串口

  176. ComPort.BaudRate = Convert.ToInt32(RateListCbobox.SelectedValue);

    //设置当前波特率

  177. ComPort.Parity = (Parity)Convert.ToInt32(ParityComCbobox.SelectedValue);

    //设置当前校验位

  178. ComPort.DataBits = Convert.ToInt32(DataBitsCbobox.SelectedValue);

    //设置当前数据位

  179. ComPort.StopBits = (StopBits)Convert.ToDouble(StopBitsCbobox.SelectedValue);

    //设置当前停止位

  180. ComPort.Open();

    //打开串口

  181. }


  182. catch


    //如果串口被其他占用,则无法打开

  183. {

  184. MessageBox.Show(

    “无法打开串口,请检测此串口是否有效或被其他占用!”

    );

  185. GetPort();

    //刷新当前可用串口


  186. return

    ;

    //无法打开串口,提示后直接返回

  187. }


  188. //↓↓↓↓↓↓↓↓↓成功打开串口后的设置↓↓↓↓↓↓↓↓↓

  189. openBtn.Content =

    “关闭串口”

    ;

    //按钮显示改为“关闭按钮”

  190. OpenImage.Source =

    new

    BitmapImage(

    new

    Uri(

    “image\\On.png”

    , UriKind.Relative));

    //开关状态图片切换为ON

  191. ComPortIsOpen =

    true

    ;

    //串口打开状态字改为true

  192. WaitClose =

    false

    ;

    //等待关闭串口状态改为false

  193. sendBtn.IsEnabled =

    true

    ;

    //使能“发送数据”按钮

  194. defaultSet.IsEnabled =

    false

    ;

    //打开串口后失能重置功能

  195. AvailableComCbobox.IsEnabled =

    false

    ;

    //失能可用串口控件

  196. RateListCbobox.IsEnabled =

    false

    ;

    //失能可用波特率控件

  197. ParityComCbobox.IsEnabled =

    false

    ;

    //失能可用校验位控件

  198. DataBitsCbobox.IsEnabled =

    false

    ;

    //失能可用数据位控件

  199. StopBitsCbobox.IsEnabled =

    false

    ;

    //失能可用停止位控件


  200. //↑↑↑↑↑↑↑↑↑成功打开串口后的设置↑↑↑↑↑↑↑↑↑


  201. if

    (autoSendCheck.IsChecked ==

    true

    )

    //如果打开前,自动发送控件就被选中,则打开串口后自动开始发送数据

  202. {

  203. autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));

    //设置自动发送间隔

  204. autoSendTick.Start();

    //开启自动发送

  205. }

  206. }


  207. #

    endregion


  208. #

    region

    关闭串口


  209. else


    //ComPortIsOpen == true,当前串口为打开状态,按钮事件为关闭串口

  210. {


  211. try


    //尝试关闭串口

  212. {

  213. autoSendTick.Stop();

    //停止自动发送

  214. autoSendCheck.IsChecked =

    false

    ;

    //停止自动发送控件改为未选中状态

  215. ComPort.DiscardOutBuffer();

    //清发送缓存

  216. ComPort.DiscardInBuffer();

    //清接收缓存

  217. WaitClose =

    true

    ;

    //激活正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口


  218. while

    (Listening)

    //判断invoke是否结束

  219. {

  220. DispatcherHelper.DoEvents();

    //循环时,仍进行等待事件中的进程,该方法为winform中的方法,WPF里面没有,这里在后面自己实现

  221. }

  222. ComPort.Close();

    //关闭串口

  223. WaitClose =

    false

    ;

    //关闭正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口

  224. SetAfterClose();

    //成功关闭串口或串口丢失后的设置

  225. }


  226. catch


    //如果在未关闭串口前,串口就已丢失,这时关闭串口会出现异常

  227. {


  228. if

    (ComPort.IsOpen ==

    false

    )

    //判断当前串口状态,如果ComPort.IsOpen==false,说明串口已丢失

  229. {

  230. SetComLose();

  231. }


  232. else


    //未知原因,无法关闭串口

  233. {

  234. MessageBox.Show(

    “无法关闭串口,原因未知!”

    );


  235. return

    ;

    //无法关闭串口,提示后直接返回

  236. }

  237. }

  238. }


  239. #

    endregion

  240. }



  241. private


    void


    Button_Send

    (


    object

    sender, RoutedEventArgs e

    )

    //发送数据按钮点击事件

  242. {

  243. Send();

    //调用发送方法

  244. }



  245. void


    autoSend

    (


    object

    sender, EventArgs e

    )

    //自动发送

  246. {

  247. Send();

    //调用发送方法

  248. }



  249. void


    Send

    (


    )

    //发送数据,分为多线程方式和单线程方式

  250. {


  251. #

    if

    MULTITHREAD


  252. if

    (Sending ==

    true

    )

    return

    ;

    //如果当前正在发送,则取消本次发送,本句注释后,可能阻塞在ComSend的lock处

  253. _ComSend =

    new

    Thread(

    new

    ParameterizedThreadStart(ComSend));

    //new发送线程

  254. SendSet.SendSetData = sendTBox.Text;

    //发送数据线程传递参数的结构体–发送的数据

  255. SendSet.SendSetMode = sendModeCheck.IsChecked;

    //发送数据线程传递参数的结构体–发送方式

  256. _ComSend.Start(SendSet);

    //发送线程启动


  257. #

    else

  258. ComSend();

    //单线程发送方法


  259. #

    endif

  260. }


  261. #

    if

    MULTITHREAD



  262. private


    void


    ComSend

    (

    Object obj

    )

    //发送数据 独立线程方法 发送数据时UI可以响应

  263. {


  264. lock

    (

    this

    )

    //由于send()中的if (Sending == true) return,所以这里不会产生阻塞,如果没有那句,多次启动该线程,会在此处排队

  265. {

  266. Sending =

    true

    ;

    //正在发生状态字


  267. byte

    [] sendBuffer =

    null

    ;

    //发送数据缓冲区


  268. string

    sendData = SendSet.SendSetData;

    //复制发送数据,以免发送过程中数据被手动改变


  269. if

    (SendSet.SendSetMode ==

    true

    )

    //16进制发送

  270. {


  271. try


    //尝试将发送的数据转为16进制Hex

  272. {

  273. sendData = sendData.Replace(

    ” ”

    ,

    “”

    );

    //去除16进制数据中所有空格

  274. sendData = sendData.Replace(

    “\r”

    ,

    “”

    );

    //去除16进制数据中所有换行

  275. sendData = sendData.Replace(

    “\n”

    ,

    “”

    );

    //去除16进制数据中所有换行


  276. if

    (sendData.Length ==

    1

    )

    //数据长度为1的时候,在数据前补0

  277. {

  278. sendData =

    “0”

    + sendData;

  279. }


  280. else


    if

    (sendData.Length %

    2

    !=

    0

    )

    //数据长度为奇数位时,去除最后一位数据

  281. {

  282. sendData = sendData.Remove(sendData.Length –

    1

    ,

    1

    );

  283. }

  284. List<

    string

    > sendData16 =

    new

    List<

    string

    >();

    //将发送的数据,2个合为1个,然后放在该缓存里 如:123456→12,34,56


  285. for

    (

    int

    i =

    0

    ; i < sendData.Length; i +=

    2

    )

  286. {

  287. sendData16.Add(sendData.Substring(i,

    2

    ));

  288. }

  289. sendBuffer =

    new


    byte

    [sendData16.Count];

    //sendBuffer的长度设置为:发送的数据2合1后的字节数


  290. for

    (

    int

    i =

    0

    ; i < sendData16.Count; i++)

  291. {

  292. sendBuffer[i] = (

    byte

    )(Convert.ToInt32(sendData16[i],

    16

    ));

    //发送数据改为16进制

  293. }

  294. }


  295. catch


    //无法转为16进制时,出现异常

  296. {

  297. UIAction(() =>

  298. {

  299. autoSendCheck.IsChecked =

    false

    ;

    //自动发送改为未选中

  300. autoSendTick.Stop();

    //关闭自动发送

  301. MessageBox.Show(

    “请输入正确的16进制数据”

    );

  302. });

  303. Sending =

    false

    ;

    //关闭正在发送状态

  304. _ComSend.Abort();

    //终止本线程


  305. return

    ;

    //输入的16进制数据错误,无法发送,提示后返回

  306. }

  307. }


  308. else


    //ASCII码文本发送

  309. {

  310. sendBuffer = System.Text.Encoding.Default.GetBytes(sendData);

    //转码

  311. }


  312. try


    //尝试发送数据

  313. {

    //如果发送字节数大于1000,则每1000字节发送一次


  314. int

    sendTimes = (sendBuffer.Length /

    1000

    );

    //发送次数


  315. for

    (

    int

    i =

    0

    ; i < sendTimes; i++)

    //每次发生1000Bytes

  316. {

  317. ComPort.Write(sendBuffer, i *

    1000

    ,

    1000

    );

    //发送sendBuffer中从第i * 1000字节开始的1000Bytes

  318. UIAction(() =>

    //激活UI

  319. {

  320. sendCount.Text = (Convert.ToInt32(sendCount.Text) +

    1000

    ).ToString();

    //刷新发送字节数

  321. });

  322. }


  323. if

    (sendBuffer.Length %

    1000

    !=

    0

    )

    //发送字节小于1000Bytes或上面发送剩余的数据

  324. {

  325. ComPort.Write(sendBuffer, sendTimes *

    1000

    , sendBuffer.Length %

    1000

    );

  326. UIAction(() =>

  327. {

  328. sendCount.Text = (Convert.ToInt32(sendCount.Text) + sendBuffer.Length %

    1000

    ).ToString();

    //刷新发送字节数

  329. });

  330. }

  331. }


  332. catch


    //如果无法发送,产生异常

  333. {

  334. UIAction(() =>

    //激活UI

  335. {


  336. if

    (ComPort.IsOpen ==

    false

    )

    //如果ComPort.IsOpen == false,说明串口已丢失

  337. {

  338. SetComLose();

    //串口丢失后的设置

  339. }


  340. else

  341. {

  342. MessageBox.Show(

    “无法发送数据,原因未知!”

    );

  343. }

  344. });

  345. }


  346. //sendScrol.ScrollToBottom();//发送数据区滚动到底部

  347. Sending =

    false

    ;

    //关闭正在发送状态

  348. _ComSend.Abort();

    //终止本线程

  349. }

  350. }


  351. #

    else



  352. private


    void


    ComSend

    (


    )

    //发送数据 普通方法,发送数据过程中UI会失去响应

  353. {


  354. byte

    [] sendBuffer =

    null

    ;

    //发送数据缓冲区


  355. string

    sendData = sendTBox.Text;

    //复制发送数据,以免发送过程中数据被手动改变


  356. if

    (sendModeCheck.IsChecked ==

    true

    )

    //16进制发送

  357. {


  358. try


    //尝试将发送的数据转为16进制Hex

  359. {

  360. sendData = sendData.Replace(

    ” ”

    ,

    “”

    );

    //去除16进制数据中所有空格

  361. sendData = sendData.Replace(

    “\r”

    ,

    “”

    );

    //去除16进制数据中所有换行

  362. sendData = sendData.Replace(

    “\n”

    ,

    “”

    );

    //去除16进制数据中所有换行


  363. if

    (sendData.Length ==

    1

    )

    //数据长度为1的时候,在数据前补0

  364. {

  365. sendData =

    “0”

    + sendData;

  366. }


  367. else


    if

    (sendData.Length %

    2

    !=

    0

    )

    //数据长度为奇数位时,去除最后一位数据

  368. {

  369. sendData = sendData.Remove(sendData.Length –

    1

    ,

    1

    );

  370. }

  371. List<

    string

    > sendData16 =

    new

    List<

    string

    >();

    //将发送的数据,2个合为1个,然后放在该缓存里 如:123456→12,34,56


  372. for

    (

    int

    i =

    0

    ; i < sendData.Length; i +=

    2

    )

  373. {

  374. sendData16.Add(sendData.Substring(i,

    2

    ));

  375. }

  376. sendBuffer =

    new


    byte

    [sendData16.Count];

    //sendBuffer的长度设置为:发送的数据2合1后的字节数


  377. for

    (

    int

    i =

    0

    ; i < sendData16.Count; i++)

  378. {

  379. sendBuffer[i] = (

    byte

    )(Convert.ToInt32(sendData16[i],

    16

    ));

    //发送数据改为16进制

  380. }

  381. }


  382. catch


    //无法转为16进制时,出现异常

  383. {

  384. autoSendCheck.IsChecked =

    false

    ;

    //自动发送改为未选中

  385. autoSendTick.Stop();

    //关闭自动发送

  386. MessageBox.Show(

    “请输入正确的16进制数据”

    );


  387. return

    ;

    //输入的16进制数据错误,无法发送,提示后返回

  388. }

  389. }


  390. else


    //ASCII码文本发送

  391. {

  392. sendBuffer = System.Text.Encoding.Default.GetBytes(sendData);

    //转码

  393. }


  394. try


    //尝试发送数据

  395. {

    //如果发送字节数大于1000,则每1000字节发送一次


  396. int

    sendTimes = (sendBuffer.Length /

    1000

    );

    //发送次数


  397. for

    (

    int

    i =

    0

    ; i < sendTimes; i++)

    //每次发生1000Bytes

  398. {

  399. ComPort.Write(sendBuffer, i*

    1000

    ,

    1000

    );

    //发送sendBuffer中从第i * 1000字节开始的1000Bytes

  400. sendCount.Text = (Convert.ToInt32(sendCount.Text) +

    1000

    ).ToString();

    //刷新发送字节数

  401. }


  402. if

    (sendBuffer.Length %

    1000

    !=

    0

    )

  403. {

  404. ComPort.Write(sendBuffer, sendTimes *

    1000

    , sendBuffer.Length %

    1000

    );

    //发送字节小于1000Bytes或上面发送剩余的数据

  405. sendCount.Text = (Convert.ToInt32(sendCount.Text) + sendBuffer.Length %

    1000

    ).ToString();

    //刷新发送字节数

  406. }

  407. }


  408. catch


    //如果无法发送,产生异常

  409. {


  410. if

    (ComPort.IsOpen ==

    false

    )

    //如果ComPort.IsOpen == false,说明串口已丢失

  411. {

  412. SetComLose();

    //串口丢失后相关设置

  413. }


  414. else

  415. {

  416. MessageBox.Show(

    “无法发送数据,原因未知!”

    );

  417. }

  418. }


  419. //sendScrol.ScrollToBottom();//发送数据区滚动到底部

  420. }


  421. #

    endif


  422. #

    if

    MULTITHREAD



  423. private


    void


    ComReceive

    (


    object

    sender, SerialDataReceivedEventArgs e

    )

    //接收数据 中断只标志有数据需要读取,读取操作在中断外进行

  424. {


  425. if

    (WaitClose)

    return

    ;

    //如果正在关闭串口,则直接返回

  426. Thread.Sleep(

    10

    );

    //发送和接收均为文本时,接收中为加入判断是否为文字的算法,发送你(C4E3),接收可能识别为C4,E3,可用在这里加延时解决


  427. if

    (recStaus)

    //如果已经开启接收

  428. {


  429. byte

    [] recBuffer;

    //接收缓冲区


  430. try

  431. {

  432. recBuffer =

    new


    byte

    [ComPort.BytesToRead];

    //接收数据缓存大小

  433. ComPort.Read(recBuffer,

    0

    , recBuffer.Length);

    //读取数据

  434. recQueue.Enqueue(recBuffer);

    //读取数据入列Enqueue(全局)

  435. }


  436. catch

  437. {

  438. UIAction(() =>

  439. {


  440. if

    (ComPort.IsOpen ==

    false

    )

    //如果ComPort.IsOpen == false,说明串口已丢失

  441. {

  442. SetComLose();

    //串口丢失后相关设置

  443. }


  444. else

  445. {

  446. MessageBox.Show(

    “无法接收数据,原因未知!”

    );

  447. }

  448. });

  449. }

  450. }


  451. else


    //暂停接收

  452. {

  453. ComPort.DiscardInBuffer();

    //清接收缓存

  454. }

  455. }



  456. void


    ComRec

    (


    )

    //接收线程,窗口初始化中就开始启动运行

  457. {


  458. while

    (

    true

    )

    //一直查询串口接收线程中是否有新数据

  459. {


  460. if

    (recQueue.Count >

    0

    )

    //当串口接收线程中有新的数据时候,队列中有新进的成员recQueue.Count > 0

  461. {


  462. string

    recData;

    //接收数据转码后缓存


  463. byte

    [] recBuffer = (

    byte

    [])recQueue.Dequeue();

    //出列Dequeue(全局)

  464. recData = System.Text.Encoding.Default.GetString(recBuffer);

    //转码

  465. UIAction(() =>

  466. {


  467. if

    (recModeCheck.IsChecked ==

    false

    )

    //接收模式为ASCII文本模式

  468. {

  469. recTBox.Text += recData;

    //加显到接收区

  470. }


  471. else

  472. {

  473. StringBuilder recBuffer16 =

    new

    StringBuilder();

    //定义16进制接收缓存


  474. for

    (

    int

    i =

    0

    ; i < recBuffer.Length; i++)

  475. {

  476. recBuffer16.AppendFormat(

    “{0:X2}”

    +

    ” ”

    , recBuffer[i]);

    //X2表示十六进制格式(大写),域宽2位,不足的左边填0。

  477. }

  478. recTBox.Text += recBuffer16.ToString();

    //加显到接收区

  479. }

  480. recCount.Text = (Convert.ToInt32(recCount.Text) + recBuffer.Length).ToString();

    //接收数据字节数

  481. recScrol.ScrollToBottom();

    //接收文本框滚动至底部

  482. });

  483. }


  484. else

  485. Thread.Sleep(

    100

    );

    //如果不延时,一直查询,将占用CPU过高

  486. }

  487. }


  488. #

    else



  489. private


    void


    ComReceive

    (


    object

    sender, SerialDataReceivedEventArgs e

    )

    //接收数据 数据在接收中断里面处理

  490. {


  491. if

    (WaitClose)

    return

    ;

    //如果正在关闭串口,则直接返回


  492. if

    (recStaus)

    //如果已经开启接收

  493. {


  494. try

  495. {

  496. Listening =

    true

    ;


    ///

    /设置标记,说明我已经开始处理数据,一会儿要使用系统UI的。

  497. Thread.Sleep(

    10

    );

    //发送和接收均为文本时,接收中为加入判断是否为文字的算法,发送你(C4E3),接收可能识别为C4,E3,可用在这里加延时解决


  498. string

    recData;

    //接收数据转码后缓存


  499. byte

    [] recBuffer =

    new


    byte

    [ComPort.BytesToRead];

    //接收数据缓存

  500. ComPort.Read(recBuffer,

    0

    , recBuffer.Length);

    //读取数据

  501. recData = System.Text.Encoding.Default.GetString(recBuffer);

    //转码


  502. this

    .recTBox.Dispatcher.Invoke(

    //WPF为单线程,此接收中断线程不能对WPF进行操作,用如下方法才可操作


  503. new

    Action(


  504. delegate

  505. {

  506. recCount.Text = (Convert.ToInt32(recCount.Text) + recBuffer.Length).ToString();

    //接收数据字节数


  507. if

    (recModeCheck.IsChecked ==

    false

    )

    //接收模式为ASCII文本模式

  508. {

  509. recTBox.Text += recData;

    //加显到接收区

  510. }


  511. else

  512. {

  513. StringBuilder recBuffer16 =

    new

    StringBuilder();

    //定义16进制接收缓存


  514. for

    (

    int

    i =

    0

    ; i < recBuffer.Length; i++)

  515. {

  516. recBuffer16.AppendFormat(

    “{0:X2}”

    +

    ” ”

    , recBuffer[i]);

    //X2表示十六进制格式(大写),域宽2位,不足的左边填0。

  517. }

  518. recTBox.Text += recBuffer16.ToString();

    //加显到接收区

  519. }

  520. recScrol.ScrollToBottom();

    //接收文本框滚动至底部

  521. }

  522. )

  523. );

  524. }


  525. finally

  526. {

  527. Listening =

    false

    ;

    //UI使用结束,用于关闭串口时判断,避免自动发送时拔掉串口,陷入死循环

  528. }

  529. }


  530. else


    //暂停接收

  531. {

  532. ComPort.DiscardInBuffer();

    //清接收缓存

  533. }

  534. }


  535. #

    endif



  536. void


    UIAction

    (

    Action action

    )

    //在主线程外激活线程方法

  537. {

  538. System.Threading.SynchronizationContext.SetSynchronizationContext(

    new

    System.Windows.Threading.DispatcherSynchronizationContext(App.Current.Dispatcher));

  539. System.Threading.SynchronizationContext.Current.Post(_ => action(),

    null

    );

  540. }



  541. private


    void


    SetAfterClose

    (


    )

    //成功关闭串口或串口丢失后的设置

  542. {

  543. openBtn.Content =

    “打开串口”

    ;

    //按钮显示为“打开串口”

  544. OpenImage.Source =

    new

    BitmapImage(

    new

    Uri(

    “image\\Off.png”

    , UriKind.Relative));

  545. ComPortIsOpen =

    false

    ;

    //串口状态设置为关闭状态

  546. sendBtn.IsEnabled =

    false

    ;

    //失能发送数据按钮

  547. defaultSet.IsEnabled =

    true

    ;

    //打开串口后使能重置功能

  548. AvailableComCbobox.IsEnabled =

    true

    ;

    //使能可用串口控件

  549. RateListCbobox.IsEnabled =

    true

    ;

    //使能可用波特率下拉控件

  550. ParityComCbobox.IsEnabled =

    true

    ;

    //使能可用校验位下拉控件

  551. DataBitsCbobox.IsEnabled =

    true

    ;

    //使能数据位下拉控件

  552. StopBitsCbobox.IsEnabled =

    true

    ;

    //使能停止位下拉控件

  553. }



  554. private


    void


    SetComLose

    (


    )

    //成功关闭串口或串口丢失后的设置

  555. {

  556. autoSendTick.Stop();

    //串口丢失后要关闭自动发送

  557. autoSendCheck.IsChecked =

    false

    ;

    //自动发送改为未选中

  558. WaitClose =

    true

    ;

    //;//激活正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口


  559. while

    (Listening)

    //判断invoke是否结束

  560. {

  561. DispatcherHelper.DoEvents();

    //循环时,仍进行等待事件中的进程,该方法为winform中的方法,WPF里面没有,这里在后面自己实现

  562. }

  563. MessageBox.Show(

    “串口已丢失”

    );

  564. WaitClose =

    false

    ;

    //关闭正在关闭状态字,用于在串口接收方法的invoke里判断是否正在关闭串口

  565. GetPort();

    //刷新可用串口

  566. SetAfterClose();

    //成功关闭串口或串口丢失后的设置

  567. }



  568. private


    void


    AvailableComCbobox_PreviewMouseDown

    (


    object

    sender, MouseButtonEventArgs e

    )

    //刷新可用串口

  569. {

  570. GetPort();

    //刷新可用串口

  571. }



  572. private


    void


    GetPort

    (


    )

    //刷新可用串口

  573. {

  574. comList.Clear();

    //情况控件链接资源

  575. AvailableComCbobox.DisplayMemberPath =

    “com1”

    ;

  576. AvailableComCbobox.SelectedValuePath =

    null

    ;

    //路径都指为空,清空下拉控件显示,下面重新添加

  577. ports =

    new


    string

    [SerialPort.GetPortNames().Length];

    //重新定义可用串口数组长度

  578. ports = SerialPort.GetPortNames();

    //获取可用串口


  579. if

    (ports.Length >

    0

    )

    //有可用串口

  580. {


  581. for

    (

    int

    i =

    0

    ; i < ports.Length; i++)

  582. {

  583. comList.Add(

    new

    customer() { com = ports[i] });

    //下拉控件里添加可用串口

  584. }

  585. AvailableComCbobox.ItemsSource = comList;

    //可用串口下拉控件资源路径

  586. AvailableComCbobox.DisplayMemberPath =

    “com”

    ;

    //可用串口下拉控件显示路径

  587. AvailableComCbobox.SelectedValuePath =

    “com”

    ;

    //可用串口下拉控件值路径

  588. }

  589. }



  590. private


    void


    sendClearBtn_Click

    (


    object

    sender, RoutedEventArgs e

    )

    //清空发送区

  591. {

  592. sendTBox.Text =

    “”

    ;

  593. }



  594. private


    void


    recClearBtn_Click

    (


    object

    sender, RoutedEventArgs e

    )

    //清空接收区

  595. {

  596. recTBox.Text =

    “”

    ;

  597. }



  598. private


    void


    countClearBtn_Click

    (


    object

    sender, RoutedEventArgs e

    )

    //计数清零

  599. {

  600. sendCount.Text =

    “0”

    ;

  601. recCount.Text =

    “0”

    ;

  602. }



  603. private


    void


    stopRecBtn_Click

    (


    object

    sender, RoutedEventArgs e

    )

    //暂停/开启接收按钮事件

  604. {


  605. if

    (recStaus ==

    true

    )

    //当前为开启接收状态

  606. {

  607. recStaus =

    false

    ;

    //暂停接收

  608. stopRecBtn.Content =

    “开启接收”

    ;

    //按钮显示为开启接收

  609. recPrompt.Visibility = Visibility.Visible;

    //显示已暂停接收提示

  610. recBorder.Opacity =

    0

    ;

    //接收区透明度改为0

  611. }


  612. else


    //当前状态为关闭接收状态

  613. {

  614. recStaus =

    true

    ;

    //开启接收

  615. stopRecBtn.Content =

    “暂停接收”

    ;

    //按钮显示状态改为暂停接收

  616. recPrompt.Visibility = Visibility.Hidden;

    //隐藏已暂停接收提示

  617. recBorder.Opacity =

    0.4

    ;


    ///

    /接收区透明度改为0.4

  618. }

  619. }



  620. private


    void


    autoSendCheck_Click

    (


    object

    sender, RoutedEventArgs e

    )

    //自动发送控件点击事件

  621. {


  622. if

    (autoSendCheck.IsChecked ==

    true

    && ComPort.IsOpen ==

    true

    )

    //如果当前状态为开启自动发送且串口已打开,则开始自动发送

  623. {

  624. autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));

    //设置自动发送间隔

  625. autoSendTick.Start();

    //开始自动发送定时器

  626. }


  627. else


    //点击之前为开启自动发送状态,点击后关闭自动发送

  628. {

  629. autoSendTick.Stop();

    //关闭自动发送定时器

  630. }

  631. }



  632. private


    void


    Time_KeyDown

    (


    object

    sender, KeyEventArgs e

    )

    //发送周期文本控件-键盘按键事件

  633. {


  634. if

    (e.Key >= Key.D0 && e.Key <= Key.D9 || e.Key >= Key.NumPad0 && e.Key <= Key.NumPad9)

    //只能输入数字

  635. {

  636. e.Handled =

    false

    ;

  637. }


  638. else

    e.Handled =

    true

    ;


  639. if

    (e.Key == Key.Enter)

    //输入回车

  640. {


  641. if

    (Time.Text.Length ==

    0

    || Convert.ToInt32(Time.Text) ==

    0

    )

    //时间为空或时间等于0,设置为1000

  642. {

  643. Time.Text =

    “1000”

    ;

  644. }

  645. autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));

    //设置自动发送周期

  646. }

  647. }



  648. private


    void


    Time_TextChanged

    (


    object

    sender, TextChangedEventArgs e

    )

    //发送周期文本控件-文本内容改变事件,与上面Time_KeyDown事件相比,可以防止粘贴进来非数字数据

  649. {


  650. //只允许输入数字

  651. TextBox textBox = sender

    as

    TextBox;

  652. TextChange[] change =

    new

    TextChange[e.Changes.Count];


  653. byte

    [] checkText =

    new


    byte

    [textBox.Text.Length];


  654. bool

    result =

    true

    ;

  655. e.Changes.CopyTo(change,

    0

    );


  656. int

    offset = change[

    0

    ].Offset;

  657. checkText = System.Text.Encoding.Default.GetBytes(textBox.Text);


  658. for

    (

    int

    i =

    0

    ; i < textBox.Text.Length; i++)

  659. {

  660. result &=

    0x2F

    < (Convert.ToInt32(checkText[i])) & (Convert.ToInt32(checkText[i])) <

    0x3A

    ;

    //0x2f-0x3a之间是数字0-10的ASCII码

  661. }


  662. if

    (change[

    0

    ].AddedLength >

    0

    )

  663. {


  664. if

    (!result || Convert.ToInt32(textBox.Text) >

    100000

    )

    //不是数字或数据大于100000,取消本次change

  665. {

  666. textBox.Text = textBox.Text.Remove(offset, change[

    0

    ].AddedLength);

  667. textBox.Select(offset,

    0

    );

  668. }

  669. }

  670. }



  671. private


    void


    Time_LostFocus

    (


    object

    sender, RoutedEventArgs e

    )

    //发送周期文本控件-失去事件

  672. {


  673. if

    (Time.Text.Length ==

    0

    || Convert.ToInt32(Time.Text) ==

    0

    )

    //时间为空或时间等于0,设置为1000

  674. {

  675. Time.Text =

    “1000”

    ;

  676. }

  677. autoSendTick.Interval = TimeSpan.FromMilliseconds(Convert.ToInt32(Time.Text));

    //设置自动发送周期

  678. }


  679. //模拟 Winfrom 中 Application.DoEvents() 详见 http://www.silverlightchina.net/html/study/WPF/2010/1216/4186.html?1292685167


  680. public


    static


    class


    DispatcherHelper

  681. {

  682. [

    SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)

    ]



  683. public


    static


    void


    DoEvents

    (


    )

  684. {

  685. DispatcherFrame frame =

    new

    DispatcherFrame();

  686. Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,

    new

    DispatcherOperationCallback(ExitFrames), frame);


  687. try

    { Dispatcher.PushFrame(frame); }


  688. catch

    (InvalidOperationException) { }

  689. }



  690. private


    static


    object


    ExitFrames

    (


    object

    frame

    )

  691. {

  692. ((DispatcherFrame)frame).Continue =

    false

    ;


  693. return


    null

    ;

  694. }

  695. }



  696. private


    void


    defaultSet_Click

    (


    object

    sender, RoutedEventArgs e

    )

    //重置按钮click事件

  697. {

  698. RateListCbobox.SelectedValue =

    “9600”

    ;

    //波特率默认设置9600

  699. ParityComCbobox.SelectedValue =

    “0”

    ;

    //校验位默认设置值为0,对应NONE

  700. DataBitsCbobox.SelectedValue =

    “8”

    ;

    //数据位默认设置8位

  701. StopBitsCbobox.SelectedValue =

    “1”

    ;

    //停止位默认设置1

  702. }



  703. private


    void


    FileOpen

    (


    object

    sender, ExecutedRoutedEventArgs e

    )

    //打开文件快捷键事件crtl+O

  704. {

  705. OpenFileDialog open_fd =

    new

    OpenFileDialog();

    //调用系统打开文件窗口

  706. open_fd.Filter =

    “TXT文本|*.txt”

    ;

    //文件过滤器


  707. if

    (open_fd.ShowDialog() ==

    true

    )

    //选择了文件

  708. {

  709. sendTBox.Text = File.ReadAllText(open_fd.FileName);

    //读TXT方法1 简单,快捷,为StreamReader的封装


  710. //StreamReader sr = new StreamReader(open_fd.FileName);//读TXT方法2 复杂,功能强大


  711. //sendTBox.Text = sr.ReadToEnd();//调用ReadToEnd方法读取选中文件的全部内容


  712. //sr.Close();//关闭当前文件读取流

  713. }

  714. }



  715. private


    void


    FileSave

    (


    object

    sender, RoutedEventArgs e

    )

    //保存数据按钮crtl+S

  716. {

  717. SaveModWindow SaveMod =

    new

    SaveModWindow();

    //new保存数据方式窗口

  718. SaveMod.Owner =

    this

    ;

    //赋予主窗口,子窗口打开后,再次点击主窗口,子窗口闪烁

  719. SaveMod.ShowDialog();

    //ShowDialog方式打开保存数据方式窗口


  720. if

    (SaveMod.mode ==

    “new”

    )

    //保存为新文件

  721. {

  722. SaveNew();

    //保存为新文件

  723. }


  724. else


    if

    (SaveMod.mode ==

    “old”

    )

    //保存到已有文件

  725. {

  726. SaveOld();

    //保存到已有文件

  727. }


  728. else


    //取消

  729. {


  730. return

    ;

  731. }

  732. }



  733. private


    void


    SaveNew_Click

    (


    object

    sender, RoutedEventArgs e

    )

    //文件-保存-保存为新文件click事件

  734. {

  735. SaveNew();

    //保存为新文件

  736. }



  737. private


    void


    SaveOld_Click

    (


    object

    sender, RoutedEventArgs e

    )

    //文件-保存-保存到已有文件click事件

  738. {

  739. SaveOld();

    //保存到已有文件

  740. }



  741. private


    void


    SaveNew

    (


    )

    //保存为新文件

  742. {


  743. if

    (recTBox.Text ==

    string

    .Empty)

    //接收区数据为空

  744. {

  745. MessageBox.Show(

    “接收区为空,无法保存!”

    );

  746. }


  747. else

  748. {

  749. SaveFileDialog Save_fd =

    new

    SaveFileDialog();

    //调用系统保存文件窗口

  750. Save_fd.Filter =

    “TXT文本|*.txt”

    ;

    //文件过滤器


  751. if

    (Save_fd.ShowDialog() ==

    true

    )

    //选择了文件

  752. {

  753. File.WriteAllText(Save_fd.FileName, recTBox.Text);

    //写入新的数据

  754. File.AppendAllText(Save_fd.FileName,

    “\r\n——”

    + DateTime.Now.ToString() +

    “\r\n”

    );

    //数据后面写入时间戳

  755. MessageBox.Show(

    “保存成功!”

    );

  756. }

  757. }

  758. }



  759. private


    void


    SaveOld

    (


    )

    //保存到已有文件

  760. {


  761. if

    (recTBox.Text ==

    string

    .Empty)

    //接收区数据为空

  762. {

  763. MessageBox.Show(

    “接收区为空,无法保存!”

    );

  764. }


  765. else

  766. {

  767. OpenFileDialog Open_fd =

    new

    OpenFileDialog();

    //调用系统保存文件窗口

  768. Open_fd.Filter =

    “TXT文本|*.txt”

    ;

    //文件过滤器


  769. if

    (Open_fd.ShowDialog() ==

    true

    )

    //选择了文件

  770. {

  771. File.AppendAllText(Open_fd.FileName, recTBox.Text);

    //在打开文件末尾写入数据

  772. File.AppendAllText(Open_fd.FileName,

    “\r\n——”

    + DateTime.Now.ToString() +

    “\r\n”

    );

    //数据后面写入时间戳

  773. MessageBox.Show(

    “添加成功!”

    );

  774. }

  775. }

  776. }



  777. private


    void


    info_click

    (


    object

    sender, RoutedEventArgs e

    )

    //帮助-关于click事件

  778. {

  779. InfoWindow info =

    new

    InfoWindow();

    //new关于窗口

  780. info.Owner =

    this

    ;

    //赋予主窗口,子窗口打开后,再次点击主窗口,子窗口闪烁

  781. info.Show();

    //ShowDialog方式打开关于窗口

  782. }



  783. private


    void


    feedBack_Click

    (


    object

    sender, RoutedEventArgs e

    )

    //帮助-反馈click事件

  784. {

  785. FeedBackWindow feedBack =

    new

    FeedBackWindow();

    //new反馈窗口

  786. feedBack.Owner =

    this

    ;

    //赋予主窗口,子窗口打开后,再次点击主窗口,子窗口闪烁

  787. feedBack.ShowDialog();

    //ShowDialog方式打开反馈窗口

  788. }

  789. }

  790. }


个人分类:


上位机