Implement a serial port selection box in WPF

3 minute read

Such a guy
The insertion and removal of the port is also reflected properly.

image.png

Creating a project

Select New → Project and select WPF application (.NET Framawork) to create a project.
The project name is SerialPortComboBox.

From the toolbox, place the ComboBox and Button.
Renamed to SerialPortComboBox and ConnectButton, respectively

MainWindow.xaml


<Window x:Class="SerialPortComboBox.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:SerialPortComboBox"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <ComboBox x:Name="SerialPortComboBox" HorizontalAlignment="Left" Margin="168,50,0,0" VerticalAlignment="Top" Width="120" SelectionChanged="comboBox_SelectionChanged"/>
        <Button x:Name="ConnectButton" Content="Connect" HorizontalAlignment="Left" Margin="300,53,0,0" VerticalAlignment="Top" Width="75" Click="ConnectButton_Click"/>
    </Grid>
</Window>

Implementation

Right-click in Solution Explorer and select Add → New Item to create a class file. Name the file SerialPortComboBox.cs

SerialPortComboBox.cs


using System;
using System.Windows.Controls;
using System.IO.Ports;
using System.Windows.Threading;
using System.ComponentModel;

using SerialPortComboBox;

delegate void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e);
static class COMPortSelector
{
    public static SerialPort port;
    private static int BAUDRATE = 1000000;
    private static MainWindow mainWindow;
    private static ComboBox SerialPortComboBox;
    private static Button ConnectButton;
    private static DispatcherTimer _timer;
    private static bool isConnected = false;


    private static DataReceivedHandler data_received_handle_;

    public static void Init()
    {
        mainWindow = (MainWindow)App.Current.MainWindow;
        SerialPortComboBox = mainWindow.SerialPortComboBox;
        ConnectButton = mainWindow.ConnectButton;
        SerialPortComboBox.SelectedIndex = 0;
        SetTimer();
    }
    public static void SetBaudrate(int baudrate)
    {
        BAUDRATE = baudrate;
    }
    public static void PushConnectButton()
    {
        if (IsConnected())
        {
            DisconnectPort();
        }
        else
        {
            ConnectPort();
        }
    }

    public static void ConnectPort()
    {
        if (isConnected) return;

        UpdateSerialPortComboBox();
        string port_name = SerialPortComboBox.Text;
        if (String.IsNullOrEmpty(port_name)) return;
        port = new SerialPort(port_name, BAUDRATE, Parity.None, 8, StopBits.One);
        try
        {
            port.Open();
            port.DtrEnable = true;
            port.RtsEnable = true;
            isConnected = true;
            ConnectButton.Content = "Disconnect";
            Console.WriteLine("Connected.");
            port.DataReceived += new SerialDataReceivedEventHandler(data_received_handle_);
        }
        catch (Exception err)
        {
            Console.WriteLine("Unexpected exception : {0}", err.ToString());
        }
    }
    public static void DisconnectPort()
    {
        if (isConnected)
        {
            port.Close();
            port.Dispose();
            isConnected = false;
            ConnectButton.Content = "Connect";
            Console.WriteLine("Disconnected.");
        }
    }
    public static bool IsConnected()
    {
        return isConnected;
    }

    public static void SetDataReceivedHandle(DataReceivedHandler data_received_handle)
    {
        data_received_handle_ = data_received_handle;
    }

    private static void UpdateSerialPortComboBox()
    {
        //Get the previously selected port
        string prev_selected_port = "";
        if (SerialPortComboBox.SelectedItem != null)
            prev_selected_port = SerialPortComboBox.SelectedItem.ToString();

        //Update port list
        string[] port_list = SerialPort.GetPortNames();
        SerialPortComboBox.Items.Clear();
        foreach (var i in port_list) SerialPortComboBox.Items.Add(i);

        //Reselect the previously selected port
        for (int i = 0; i < SerialPortComboBox.Items.Count; i++)
        {
            if (SerialPortComboBox.Items[i].ToString() == prev_selected_port)
                SerialPortComboBox.SelectedIndex = i;
        }
        //Select 0 if the number of ports is 1 or less
        if (SerialPortComboBox.Items.Count <= 1)
            SerialPortComboBox.SelectedIndex = 0;
    }
    private static void SetTimer()
    {
        _timer = new DispatcherTimer();
        _timer.Interval = new TimeSpan(0, 0, 1);
        _timer.Tick += new EventHandler(OnTimedEvent);
        _timer.Start();
        mainWindow.Closing += new CancelEventHandler(StopTimer);
    }
    private static void OnTimedEvent(Object source, EventArgs e)
    {
        UpdateSerialPortComboBox();
    }
    private static void StopTimer(object sender, CancelEventArgs e)
    {
        _timer.Stop();
    }
}

Main

Write as follows

MainWindow.xaml.cs


using System.Windows;
using System.Windows.Controls;
using System.IO.Ports;

namespace SerialPortComboBox
{
    /// <summary>
    /// MainWindow.xaml interaction logic
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            COMPortSelector.Init();
            COMPortSelector.SetDataReceivedHandle(aDataReceivedHandler);
        }

        private static void aDataReceivedHandler(
                    object sender,
                    SerialDataReceivedEventArgs e)
        {
            //Reception processing
        }

        private void comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {

        }

        private void ConnectButton_Click(object sender, RoutedEventArgs e)
        {
            COMPortSelector.PushConnectButton();
        }
    }
}

Commentary

Call COMPortSelector.Init (); and it will work.
You can switch between Connect and Disconnect by calling COMPortSelector.PushConnectButton (); when the button is pressed.

Below is an explanation of the processing performed by the COMPortSelector class.

You can get the list of connected ports with SerialPort.GetPortNames (), but this is done in UpdateSerialPortComboBox () and reflected in the item list of CmboBox.

Also, since I want to reflect the list of items according to the port insertion / removal, I implement it by using DispatcherTimer and calling UpdateSerialPortComboBox () every second.

Alternatively, you can use SetDataReceivedHandle (); to set the method to call by the received event.

In the main, COMPortSelector.SetDataReceivedHandle (aDataReceivedHandler);
The aDataReceivedHandler is called for each reception.