I am creating a serial communication app in my WPF application.
I'm using SerialDataReceivedEventHandler as a data reception event from a communication device, and I want to pass the received data to the UI, but I'm having trouble figuring out how to do it.
My source code is below.
I thought it would be better to separate the communication logic part from the UI, so I wrote the communication processing part in Port.cs. (I'm not very knowledgeable about this, so I could be wrong...)
However, with this configuration, you cannot pass values from Port.cs to MainWindow.xaml.cs.
Is there a way to pass the data received by EventReciveAsync() in this Port.cs to the UI?
Currently, I don't know how to do it, so I'm displaying it in a message box, but I want to display it in the UI textBlock.
MainWindow.xaml
<Window x:Class="SerialConnectWPF.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:SerialConnectWPF"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="17*"/>
<RowDefinition Height="14*"/>
</Grid.RowDefinitions>
<Border BorderThickness="1" BorderBrush="Black" HorizontalAlignment="Left" Height="167" Margin="40,10,0,0" Grid.Row="1" VerticalAlignment="Top" Width="743"/>
<ComboBox x:Name="cmbComPort" HorizontalAlignment="Left" Height="26" Margin="186,86,0,0" VerticalAlignment="Top" Width="356"/>
<Button x:Name="btnOpen" Content="Connect Open" HorizontalAlignment="Left" Height="26" Margin="582,92,0,0" VerticalAlignment="Top" Width="173" Click="btnOpen_Click"/>
<TextBox x:Name="txtSendData" HorizontalAlignment="Left" Height="36" Margin="186,169,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="356" Cursor="Arrow"/>
<Label Content="COM Port:" HorizontalAlignment="Left" Height="33" Margin="80,95,0,0" VerticalAlignment="Top" Width="78"/>
<Label Content="Send Data" HorizontalAlignment="Left" Height="29" Margin="80,187,0,0" VerticalAlignment="Top" Width="69"/>
<Button x:Name="btnSend" Content="Send" HorizontalAlignment="Left" Height="30" Margin="582,183,0,0" VerticalAlignment="Top" Width="173" Background="#FF301253" Click="btnSend_Click"/>
<TextBlock x:Name="txtLog" HorizontalAlignment="Center" Height="144" Margin="0,20,0,0" Grid.Row="1" TextWrapping="Wrap" VerticalAlignment="Top" Width="739"/>
</Grid>
MainWindow.xaml.cs
namespace SerialConnectWPF
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
Port p = new Port();
public MainWindow()
{
InitializeComponent();
btnOpen.IsEnabled = true;
btnSend.IsEnabled = false;
cmbComPort.ItemsSource = p.GetPortNum();
}
private void btnOpen_Click(object sender, RoutedEventArgs e)
{
if (cmbComPort.Text == "") return;
if (p.PortOpen(cmbComPort.Text))
{
btnSend.IsEnabled = true;
}
else
{
btnSend.IsEnabled = false;
}
}
private void btnSend_Click(object sender, RoutedEventArgs e)
{
btnSend.IsEnabled = false;
if (txtSendData.Text == "") return;
p.Send(txtSendData.Text);
btnSend.IsEnabled = true;
}
}
}
Port.cs
namespace SerialConnectWPF
{
class Port
{
private SerialPort _serialPort = null;
public bool PortOpen(string comNum)
{
_serialPort ??= new SerialPort
{
PortName = comNum,
BaudRate = 9600,
DataBits = 8,
Parity = Parity.None,
StopBits = StopBits.One,
Handshake = Handshake.None,
Encoding = Encoding.UTF8,
WriteTimeout = 100000,
ReadTimeout = 100000,
NewLine = "\r\n",
};
_serialPort.DataReceived = new SerialDataReceivedEventHandler(EventReciveAsync);
if (!_serialPort.IsOpen)
{
try
{
_serialPort.Open();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
return false;
}
}
return true;
}
public void PortClose()
{
_serialPort?.Close();
_serialPort = null;
}
public void Send(string serialTxt)
{
if (!_serialPort.IsOpen) return;
try
{
var tmp = serialTxt.Split("-").Select(hex => Convert.ToByte(hex, 16));
byte[] sendData = tmp.ToArray();
// データ書き込み
_serialPort.Write(sendData, 0, sendData.Length);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public void EventReciveAsync(object sender, SerialDataReceivedEventArgs e)
{
if (!_serialPort.IsOpen) return;
var srtDataReceived = "";
var buf = new byte[_serialPort.BytesToRead];
try
{
_serialPort.Read(buf, 0, buf.Length);
srtDataReceived = BitConverter.ToString(buf);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
if (_serialPort.IsOpen)
{
MessageBox.Show(srtDataReceived);
}
}
public string[] GetPortNum()
{
return SerialPort.GetPortNames();
}
}
}
CodePudding user response:
You can pass the UI updating code to the port via an action..
Port p = new Port();
public MainWindow()
{
InitializeComponent();
btnOpen.IsEnabled = true;
btnSend.IsEnabled = false;
cmbComPort.ItemsSource = p.GetPortNum();
p.UpdateUiAction = data => {
Application.Current.Dispatcher.Invoke(() => txtLog.Text = data);
};
}
In Port.cs, add the action property
public Action<string> UpdateUiAction {set; get;}
public void EventReciveAsync(object sender, SerialDataReceivedEventArgs e)
{
if (!_serialPort.IsOpen) return;
var srtDataReceived = "";
var buf = new byte[_serialPort.BytesToRead];
try
{
_serialPort.Read(buf, 0, buf.Length);
srtDataReceived = BitConverter.ToString(buf);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
if (_serialPort.IsOpen)
{
UpdateUiAction?.Invoke(srtDataReceived);
}
}