No read with delegate multithread fix and no form pops up - c#

I need to update an windows form label with a string received from the serial port. I've got two problems with the code I made already.
Because the reading of the serial port needs another thread I use a delegate method to update the label text.
The first problem is that the form window wont open when I start the program (it does open when I don't call initSerialPort() in Form1_Load()).
The second problem is that it seems like it doesn't reach Debug.Write(message) when I call _self.SetText(message) in Read(). When I comment out _self.SetText(message) it does log the message but also doesn't open the form window because initSerialPort() is called in Form1_Load()
I'm kind of an noob with C#, just so you know ;)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Threading;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
delegate void SetTextCallback(string text);
private static SerialPort _serialPort;
private static Boolean _continue;
private static StringComparer stringComparer = StringComparer.OrdinalIgnoreCase;
private static Thread readThread = new Thread(Read);
private static Form1 _self;
private static Label _lbl;
public Form1()
{
InitializeComponent();
_self = this;
_lbl = label1;
}
private void Form1_Load(object sender, EventArgs e)
{
initSerialPort();
}
public void setMessage(string mes)
{
label1.Text = mes;
}
private static void initSerialPort()
{
// Create a new SerialPort object with default settings.
_serialPort = new SerialPort("COM3", 9600, Parity.None, 8, StopBits.One);
// Set the read/write timeouts
_serialPort.ReadTimeout = 500;
_serialPort.WriteTimeout = 500;
_serialPort.Open();
_continue = true;
readThread.Start();
readThread.Join();
_serialPort.Close();
_serialPort = null;
}
public static void Read()
{
Debug.Write("testread");
while (_continue)
{
try
{
String message = _serialPort.ReadLine();
_self.SetText(message);
Debug.Write(message);
}
catch (TimeoutException) { }
}
}
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.label1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.label1.Text = text;
}
}
}
}

Don't Join to the new thread you create. That is going to block the thread until your Read method completes, which means it never completes.

Related

C#, need to receive data from microcontroller

Trying to receive data, from mk, using DataReceived and handler event, what i do is -
push a button on a program (code is below) then LED on mk will turn on, then the data should be sent back to program (expecting 1, on byte value, but also tried string value, doesn't work). Sending side is working, but receiving....not
seems like i'm missing something. Any help apreciate it. Thx in Further
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
namespace WindowsFormsApplication11
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e) // As i understood, here we configure where i data will be shown,
// trying to get it on TextBox1
{
SerialPort sp = (SerialPort)sender;
richTextBox1.Text += sp.ReadExisting() + "\n";
}
private void button1_Click(object sender, EventArgs e) // There are a main actions, first i receive data then send data by a click.
{
serialPort1.Write("\u0001");
serialPort1.Close();
System.ComponentModel.IContainer components = new System.ComponentModel.Container(); //
serialPort1 = new System.IO.Ports.SerialPort(components);
serialPort1.PortName = "COM4";
serialPort1.BaudRate = 9600;
serialPort1.DtrEnable = true;
serialPort1.Open();
serialPort1.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
}
}
}
The serial port is on a different thread than the UI. So when you receive a character, as you haven't invoked the UI, you get an exception and the UI is not updated.
Invoke the UI first in your DataReceivedHandler. You could do something like that:
public static class ControlExt
{
public static void InvokeChecked(this Control control, Action method)
{
try
{
if (control.InvokeRequired)
{
control.Invoke(method);
}
else
{
method();
}
}
catch { }
}
}
public partial class Form1 : Form
{
private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
{
this.InvokeChecked(delegate
{
richTextBox1.Text += serialPort1.ReadExisting() + "\n";
richTextBox1.SelectionStart = Text.Length;
richTextBox1.ScrollToCaret();
});
}
}

Dialogbox doesn't show orginal form

I have a Windows Mobile 6.5 App under development. When the user opens the App a dialog box appears with the login form. User logs in and then after 30 seconds (small time in production) when the timer has run out without activity I show the login Dialog box again using events:
static private void _TimerTick(object state)
{
// the user has been inactive for 30 secs; log him out
MainForm.timer = null;
using (LoginForm LoginForm = new LoginForm())
{
if (LoginForm.ShowDialog() == DialogResult.OK)
{
MainForm.timer = new System.Threading.Timer(_TimerTick, null, 1000 * 30 * 1, Timeout.Infinite);
}
else
{
Application.Exit();
}
}
}
But once I press login and return with an ok button from the login form the original form does show. Although it is still in the task manager. I have tried:
.TopMost = true; but then I can't assess the windows button in the bar at the bottom of the app and no other apps can run as form in my app is always in front of it.
A simple solution as this is only for the login and one main form:
LoginForm.cs (with two textboxes and main menu with Login and Exit):
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace LoginFormTest
{
public partial class LoginForm : Form
{
public LoginForm()
{
InitializeComponent();
}
public void doShow(bool bShow)
{
if(bShow)
Invoke(new Action(() => this.Show()));
else
Invoke(new Action(() => this.Hide()));
}
public void doClose()
{
Invoke(new Action(() => this.Close()));
}
private void mnuLogin_Click(object sender, EventArgs e)
{
MainForm mainForm = new MainForm();
mainForm.Show();
System.Diagnostics.Debug.WriteLine("mainForm started");
}
}
}
Nothing special there.
The MainForm has code that will close the form if no activity:
using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace LoginFormTest
{
public partial class MainForm : Form
{
int countDown = 3; //number of seconds for timeout
System.Threading.Timer timer;
object lockCounter = new object(); //to sync access to counter var
public MainForm()
{
InitializeComponent();
//start a independent timer after 1000ms and with a 1000ms interval
timer = new System.Threading.Timer(new TimerCallback(this.timerCallback), null, 1000, 1000);
}
private void mnuExit_Click(object sender, EventArgs e)
{
doClose();
}
private void mnuLogout_Click(object sender, EventArgs e)
{
doClose();
}
private void doClose()
{
System.Diagnostics.Debug.WriteLine("mainForm closing");
try
{
timer.Dispose(); //else timer thread will continue running!
Invoke(new Action(() => this.Close()));
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine("Exception in doClose(): " + ex.Message);
}
}
private void MainForm_MouseMove(object sender, MouseEventArgs e)
{
resetTimeout();
}
private void MainForm_KeyPress(object sender, KeyPressEventArgs e)
{
resetTimeout();
}
private void MainForm_Click(object sender, EventArgs e)
{
resetTimeout();
}
public void resetTimeout()
{
System.Diagnostics.Debug.WriteLine("resetTimeout()");
lock(lockCounter)
countDown = 3;
}
public void timerCallback(object stateInfo)
{
lock (lockCounter)
countDown--;
if (countDown == 0)
{
System.Diagnostics.Debug.WriteLine("timeout->doClose()");
doClose();
}
}
private void MainForm_Closed(object sender, EventArgs e)
{
System.Diagnostics.Debug.WriteLine("mainForm CLOSED");
}
}
}
Specials:
a lock object to sync access to the counter var
a threading timer that runs independent of the message pump
a delegate to by called from the TimerCallback function

Display String in Windows Forms Textbox

I have a string named " e.RequestMessage.Text " . In a class this string I want to show has a value int a textbox in a windows form .
I have the project that contains a class which shows my string in a console. So I want to show this sting in a textbox on a windows form. I added a windows form to it (the class runs in front)
How can I accomplish this?
using System;
using Eneter.Messaging.DataProcessing.Serializing;
using Eneter.Messaging.EndPoints.TypedMessages;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
using Eneter.ProtoBuf;
using message.declarations;
namespace ServiceExample
{
class Program
{
private static IDuplexTypedMessageReceiver<MyResponse, MyRequest> myReceiver;
static void Main(string[] args)
{
// Instantiate Protocol Buffer based serializer.
ISerializer aSerializer = new ProtoBufSerializer();
// Create message receiver receiving 'MyRequest' and receiving 'MyResponse'.
// The receiver will use Protocol Buffers to serialize/deserialize messages.
IDuplexTypedMessagesFactory aReceiverFactory = new DuplexTypedMessagesFactory(aSerializer);
myReceiver = aReceiverFactory.CreateDuplexTypedMessageReceiver<MyResponse, MyRequest>();
// Subscribe to handle messages.
myReceiver.MessageReceived += OnMessageReceived;
// Create TCP messaging.
IMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();
IDuplexInputChannel anInputChannel
= aMessaging.CreateDuplexInputChannel("tcp://127.0.0.1:8060/");
// Attach the input channel and start to listen to messages.
myReceiver.AttachDuplexInputChannel(anInputChannel);
Console.WriteLine("The service is running. To stop press enter.");
Console.ReadLine();
// Detach the input channel and stop listening.
// It releases the thread listening to messages.
myReceiver.DetachDuplexInputChannel();
}
// It is called when a message is received.
private static void OnMessageReceived(object sender, TypedRequestReceivedEventArgs<MyRequest> e)
{
Console.WriteLine("Received: " + **e.RequestMessage.Text**);
// Create the response message.
MyResponse aResponse = new MyResponse();
aResponse.Length = e.RequestMessage.Text.Length;
// Send the response message back to the client.
myReceiver.SendResponseMessage(e.ResponseReceiverId, aResponse);
}
}
}
Windows form code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace ServiceExample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
}
}
private void button1_Click(object sender, EventArgs e)
{
sum = x + y;
MessageBox.Show("Ans=" + sum);
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
string myString = sum.ToString();
textBox3.Text = myString;
}
Where do you construct the form?
One thing you can do is create a custom constructor for the form that takes in a String and just pass e.RequestMessage.Text to it.
Something like:
public Form1(String messageText)
{
InitializeComponent();
aTextBox.Text = messageText;
}
And then in ServiceExample:
Form1 form1 = new Form1(e.RequestMessage.Text);
Have you tried to update the value of your textbox as part of the event? This would be accomplished as:
public void OnMessageReceived(params)
{
yourTextBox.Text = e.RequestMessage.Text;
}
Unless I misunderstood your question, this should do what you expect.

How to terminate a thread from the UI using C#

I have a thread started and I want the user to be able to interrupt it by clicking a button on the form. I found the following code and it demonstrates what I want nicely.
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using System.Threading;
namespace ExThread {
public partial class MainForm : Form {
public int clock_seconds=0;
[STAThread]
public static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
public MainForm() {
InitializeComponent();
Thread thread_clock = new Thread(new ThreadStart(Thread_Clock));
thread_clock.IsBackground = true;
thread_clock.Start();
}
delegate void StringParameterDelegate (string value);
public void Update_Label_Seconds(string value) {
if (InvokeRequired) {
BeginInvoke(new StringParameterDelegate(Update_Label_Seconds), new object[]{value});
return;
}
label_seconds.Text= value + " seconds";
}
void Thread_Clock() {
while(true) {
clock_seconds +=1;
Update_Label_Seconds(clock_seconds.ToString());
Thread.Sleep(1000);
}
}
private void btnStop_Click(object sender, EventArgs e)
{
}
}
}
I have added the btnStop method. What code needs to be added to stop the thread_clock thread.
Thanks.
First, the thread needs to be able to recognize that it should end. Change
void Thread_Clock() {
while(true) {
to
bool endRequested = false;
void Thread_Clock() {
while(!endRequested) {
And then set endRequested to True in your button click handler.
private void btnStop_Click(object sender, EventArgs e)
{
endRequested = true;
}
Note that for this specific case, it is probably more appropriate to use a Timer
http://msdn.microsoft.com/en-us/library/system.windows.forms.timer.aspx
Simply start and stop the timer as desired. You would update the clock from the timer's Tick() event.

SerialPort Buffer on New Thread

I have a MySerialPort class/object accessed from FormGUI. After creating a MySerialPort object, I want to open it, and keep receiving data continuously. The data will be stored and managed in the object's data buffer. FormGUI's memoEdit will show the received codes from the MySerialPort object's buffer.
How can I use "new Thread()" with the [port].ReadExisting method?
using System;
using System.IO.Ports;
using System.Threading;
class MySerialPort
{
public SerialPort CreatePort(string portName, int portSpeed, int portParity, int portDataSize, int portStopBits)
{
// fixed values while testing
var port = new SerialPort("COM6", 9600, Parity.None, 8, StopBits.One);
return port;
}
public void OpenPort(SerialPort port)
{
port.Open();
new Thread(() => port.ReadExisting).Start();
while (true)
{
// Send to buffer
// Maybe some break condition
}
}
The Observer pattern is working now, I got the data I was expecting from the serial port, thanks Henk.
public partial class MyGUI : Form
{
private MySerialPort MyPort = new MySerialPort();
public MyGUI()
{
InitializeComponent();
MyPort.OnDataBufferUpdate += DisplayNewData;
}
public void DisplayNewData(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
BeginInvoke(new MethodInvoker(delegate() { DisplayNewData(sender, e); }));
}
else
{
memoEdit.Text = MyPort.DataBuffer;
}
}
}
public class MySerialPort
{
public MySerialPort()
{
// Initialize serial port
_Port.DataReceived += _Port_DataReceived;
}
public delegate void HandleDataBufferUpdate(object sender, EventArgs e);
public event HandleDataBufferUpdate OnDataBufferUpdate = delegate {};
private void _Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
// Set _Port.DataBuffer from serial port buffer, then activate event below to signal form
OnDataBufferUpdate(this, EventArgs.Empty);
}
}

Categories

Resources