My application connects to a Bluetooth module that is transmitting numbers. I want to grab this data and plot it in realtime point by point on a zedgraph window. I am using threads to make my UI more faster. When it comes to plotting the points, I receive the following error, "thread operation not valid: Control 'hScrollBar1' accessed from a thread other than the thread it was created on." I understand that I cannot access this control if it was not created on the same thread but I just do not know how to implement the BackgroundWorker or other solutions to fix this problem. See below for the 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;
using System.IO.Ports;
using System.Globalization;
using System.Threading;
using ZedGraph;
using System.IO;
using InTheHand;
using InTheHand.Net.Bluetooth;
//using InTheHand.Net.Bluetooth.AttributeIds;
using InTheHand.Net.Ports;
using InTheHand.Net.Sockets;
using InTheHand.Net;
using InTheHand.Windows.Forms;
namespace MPS430_Serial_Data
{
public partial class frmMain : Form
{
int tickStart = 0;
string initText = "waiting...";
const string ID_TEMP = "C";
const string UNIT_TEMP = " lb";
GraphPane myPane1;
GraphPane myPaneBT;
private static UInt32 cnt_Temp = 0;
private static UInt32 cnt_All = 0;
const int XAXIS_SCALE = 60; //sets the number of seconds unit on the x-axis
const int YAXIS_MINVALUE = 0; //sets the minimum value of the y-axis
const int YAXIS_MAXVALUE = 3000; //sets the maximum value of the y-axis
IPointListEdit listBT;//list to store the data and timestamp
LineItem curveBT;
List<string> items;
BluetoothDeviceInfo[] devices;
byte[] received;
//Initializes values for controls on the main form
public frmMain()
{
items = new List<string>();
string[] myPort = SerialPort.GetPortNames();
InitializeComponent();
//When our form loads, auto detect all serial ports in the system and populate the cmbPort Combo box.
foreach (string port in myPort)
{
cmbPort.Items.Add(port);
}
//Populate the cmbBaud Combo box to common baud rates used
//cmbBaud.Items.Add(2400);
cmbBaud.Items.Add(9600);
cmbModel.Items.Add("41000-200");
cmbModel.Items.Add("41000-300");
cmbModel.Items.Add("41000-400");
cmbModel.Items.Add("41000-500");
//Checks if the load monitor is connected to the PC using a USB cable
try
{
cmbPort.SelectedIndex = 0; //Set cmbPort text to the first COM port detected
}
catch (ArgumentOutOfRangeException)
{
MessageBox.Show("Please connect USB cable", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);
}
cmbBaud.SelectedIndex = 0; //Set cmbBaud text to the first Baud rate on the list
cmbModel.SelectedIndex = 0; //Set cmbModel text to the first model on the list
btnDisconnect.Enabled = false;
textTemp.Text = initText;
tabPage1.Text = "USB Connection";
tabPage3.Text = "Bluetooth Connection";
}
//Initializes the graph and timer when the form loads
private void frmMain_Load(object sender, EventArgs e)
{
myPaneBT = zgControlBT.GraphPane;
myPaneBT.Chart.Fill = new Fill(Color.White, Color.SteelBlue, 45.0F);
myPaneBT.Title.Text = "Dynamic Display of Load Cell Values";
myPaneBT.XAxis.Title.Text = "Time (Seconds)";
myPaneBT.YAxis.Title.Text = "Load (lb)";
LineItem curveBT = myPaneBT.AddCurve("Load", listBT, Color.Black, SymbolType.Circle);
timer1.Interval = 1;
timer1.Enabled = true;
timer1.Start();
}
//Plots the received data on the graph versus the instantaneous time
private void Display_Data(String sTemp, LineItem curve, IPointListEdit list, ZedGraphControl zgControl)
{
double currentForce = 0;
double maxForce = 0;
if (zgControl.GraphPane.CurveList.Count <= 0) //Make sure that the curvelist has at least one curve
return;
curve = zgControl.GraphPane.CurveList[0] as LineItem; //Get the first CurveItem in the graph
if (curve == null)
return;
list = curve.Points as IPointListEdit; //Get the PointPairList
if (list == null) //If this is null, it means the reference at curve.Points does not
return; //support IPointListEdit, so we won't be able to modify it
double time = (Environment.TickCount - tickStart) / 1000.0; //Time is measured in seconds
DateTime now = new DateTime(); //Get current time
now = DateTime.Now;
double timestamp = now.ToOADate();
list.Add(timestamp, Convert.ToDouble(sTemp));
currentForce = Convert.ToDouble(sTemp);
if (currentForce > maxForce) //Compares the current force with the highest force
maxForce = currentForce;
//txtMaxForce.Text = maxForce.ToString(); //Displays the maximum force
XDate dateTime = curve[0].X; //Converts the date and time to a readable format
//txtStartTime.Text = dateTime.DateTime.ToString(); //Displays the starting time
zgControl.IsShowHScrollBar = true;
zgControl.ScrollGrace = 0.1; //Add 10% to scale range
zgControl.IsEnableHPan = false; //Horizontal pan and zoom not allowed
zgControl.IsEnableHZoom = false;
zgControl.IsEnableVPan = false; //Vertical pan and zoom not allowed
zgControl.IsEnableVZoom = false;
zgControl.IsAutoScrollRange = true; //the scrollbars on the x-axis will scroll automatically
zgControl.AxisChange(); //changes the x-axis
zgControl.Invalidate(); //Force a redraw
}
//Sets the size of the user interface
private void Form1_Resize(object sender, EventArgs e)
{
SetSize(zgControlBT);
}
// Set the size and location of the ZedGraphControl
private void SetSize(ZedGraphControl zgControl)
{
Rectangle formRect = this.ClientRectangle;
formRect.Inflate(-10, -10); // Control is always 10 pixels inset from the client rectangle of the form
if (zgControl.Size != formRect.Size)
{
zgControl.Location = formRect.Location;
zgControl.Size = formRect.Size;
}
}
//Sets the axis and scales of the real-time graph
private void setGraphAxis(GraphPane myPane, ZedGraphControl zgControl)
{
myPane.XAxis.Type = AxisType.Date; //sets the date on the x-axis
myPane.XAxis.MinorGrid.IsVisible = true; //displays gridlines
myPane.XAxis.MajorGrid.IsVisible = true;
myPane.YAxis.MajorGrid.IsVisible = true;
myPane.XAxis.Scale.Min = new XDate(DateTime.Now); //sets the current time as the min value on the x-axis scale
myPane.XAxis.Scale.Max = new XDate(DateTime.Now.AddSeconds(XAXIS_SCALE)); //adds 120 seconds (5 minutes) to the current value
//to get the max value on the x-axis scale
myPane.YAxis.Scale.Min = YAXIS_MINVALUE; //sets the min value on the y-axis scale
myPane.YAxis.Scale.Max = YAXIS_MAXVALUE; //sets the max value on the y-axis scale
myPane.XAxis.Scale.Format = "HH:mm:ss \n MMM dd yyyy"; //sets the date format for the x-axis
myPane.XAxis.Scale.MinorUnit = DateUnit.Second; //sets the minimum x unit to time/seconds
myPane.XAxis.Scale.MajorUnit = DateUnit.Second; //sets the maximum x unit to time/minutes
zgControl.AxisChange(); //Scale the axes
}
//Sets the axis and scale for the graph when data is retrieved from the data logger
private void setDataLoggerGraphAxis(GraphPane myPane)
{
myPane.XAxis.Type = AxisType.Date; //sets the date on the x-axis
myPane.XAxis.MinorGrid.IsVisible = true; //displays gridlines
myPane.XAxis.MajorGrid.IsVisible = true;
myPane.YAxis.MajorGrid.IsVisible = true;
myPane.XAxis.Scale.Min = 0;
myPane.XAxis.Scale.Max = 30;
myPane.YAxis.Scale.Min = YAXIS_MINVALUE; //sets the min value on the y-axis scale
myPane.YAxis.Scale.Max = YAXIS_MAXVALUE; //sets the max value on the y-axis scale
myPane.XAxis.Scale.Format = "HH:mm:ss \n MMM dd yyyy"; //sets the date format for the x-axis
myPane.XAxis.Scale.MinorUnit = DateUnit.Second; //sets the minimum x unit to time/seconds
myPane.XAxis.Scale.MajorUnit = DateUnit.Second; //sets the maximum x unit to time/minutes
zgControlUSB.AxisChange(); //Scale the axes
}
//Guid mUUID = new Guid("643F6826-1EC5-4509-BBC4-0CC795C576F4");
Guid mUUID = BluetoothService.SerialPort;
private void btnFindDevice_Click(object sender, EventArgs e)
{
startScan();
}
private void startScan()
{
listBox1.DataSource = null;
listBox1.Items.Clear();
items.Clear();
Thread bluetoothScanThread = new Thread(new ThreadStart(scan));
bluetoothScanThread.Start();
}
private void scan()
{
BluetoothClient client = new BluetoothClient();
devices = client.DiscoverDevicesInRange();
foreach (BluetoothDeviceInfo d in devices)
{
items.Add(d.DeviceName);
updateDeviceList();
}
}
private void updateDeviceList()
{
Func<int> del = delegate()
{
cbDevices.DataSource = items;
listBox1.DataSource = items;
txtDevice.Text = items[0].ToString();
return 0;
};
Invoke(del);
}
private void ClientConnect()
{
BluetoothClient client = new BluetoothClient();
client.BeginConnect(deviceInfo.DeviceAddress, mUUID, this.BluetoothClientConnectCallback, client);
}
void BluetoothClientConnectCallback(IAsyncResult result)
{
BluetoothClient client = (BluetoothClient)result.AsyncState;
received = new byte[1024];
client.EndConnect(result);
string btValue = "";
byte[] temp = new byte[1024];
byte[] receivedValue = new byte[1024];
int i = 0;
Thread.Sleep(10000);
Stream btstream = client.GetStream();
btstream.ReadTimeout = 1000;
while (true)
{
// while (!ready) ;
Array.Clear(received, 0, received.Length);
try
{
byte btByte;
btstream.Read(received, 0, received.Length);
// MessageBox.Show(btstream.Length.ToString());
foreach (Byte b in received)
{
//MessageBox.Show(b.ToString());
temp[i] = b;
if (b == 10)
{
break;
}
i++;
}
i = 0;
MessageBox.Show(Encoding.ASCII.GetString(temp));
Display_Data(Encoding.ASCII.GetString(temp), curveBT, listBT, zgControlBT);
Array.Clear(temp, 0, temp.Length);
}
catch (IOException e)
{
}
//finally { }
//index++;
}
btstream.Flush();
btstream.Close();
client.Close();
}
string myPin = "1234";
private bool pairDevice()
{
if (!deviceInfo.Authenticated)
{
if (!BluetoothSecurity.PairRequest(deviceInfo.DeviceAddress, myPin))
{
return false;
}
}
return true;
}
private void btnBTConnect_Click(object sender, EventArgs e)
{
deviceInfo = devices.ElementAt(listBox1.SelectedIndex);
if (pairDevice())
{
ClientConnect();
}
else
{
//updateUI("Pair failed");
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//put the important, processor-intensive stuff
Thread.Sleep(1000); //one second
/**********************************************************************************
* e.Argument is used to get the parameter reference received by RunWorkerAsync
* or gets a value that represents the argument of an asynchronous operation
* ***********************************************************************************/
/************************************************************************
* e.Result is used to see what the BackgroundWorker processing did
* or gets or sets a value that represents the result of an asynchronous operation
* *************************************************************************************/
/**************************************************************************************
* backgroundWorker1.RunWorkerAsync is called to start a process on the worker thread
* or starts execution of a background operation
* **************************************************************************************/
}
}
}
I receive the error at this line in the code: zgControl.IsShowHScrollBar = true;
Any help is appreciated. Thanks in advance!