I want to display graph of data coming from serial port.This data is continues having \r in between. For drawing graph I am using ZedGraph.dll. My code is as follows
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 ZedGraph;
using System.IO.Ports;
namespace FilterRealTimeCSharp
{
public partial class Form1 : Form
{
List<double> measures;
/// The ZedGraph curve
LineItem myCurve;
BackgroundWorker worker;
// ZedGraphControl zedGraphControl1;
GraphPane myPane;
public Form1()
{
InitializeComponent();
//create an empty list
measures = new List<double>();
myPane = zedGraphControl2.GraphPane;
//init your zegGraphControl here
//create an empty curve: it will be filled later
myCurve = myPane.AddCurve("Porsche", null, Color.Red, SymbolType.Diamond);
//create the worker
worker = new BackgroundWorker();
// set this to true so that you can cancel the worker
worker.WorkerSupportsCancellation = true;
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//the worker has completed
//do whatever you want here
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
//put all your serial port code here
SerialPort sprt = new SerialPort("COM1");
sprt.BaudRate = 9600;
sprt.Parity = Parity.None;
sprt.StopBits = StopBits.One;
sprt.DataBits = 8;
sprt.Handshake = Handshake.None;
try
{
sprt.Open();
}
catch (Exception)
{
MessageBox.Show("Check port");
return;
}
//worker.CancellationPending will change to true when CancelAsync is called
//(so when the user clicks button2).
//while the worker should still continue, read incoming data
while (!worker.CancellationPending)
{
//wait for data to come...
System.Threading.Thread.Sleep(100);
string indata = sprt.ReadExisting();
//extract the values from the read data
//be careful here: make sure the read data is complete...
string[] splt = indata.Split('\r');
// string chop = splt[2];
//// string final = chop.Remove(5);
// float d = Convert.ToSingle(chop);
//update the measures
//measures is shared by several threads: you must lock it to access it safely
lock (measures)
{
for (int i = 1; i < splt.Length-2; i++)
{
string chop = splt[i];
// string final = chop.Remove(5);
float d = Convert.ToSingle(chop);
measures.Add(d);
}
}
//update the graph
BeginInvoke((Action)(() => UpdateGraph()));
}
//user wants to stop the worker
sprt.Close();
}
/// This function is called when the graph should be updated
private void UpdateGraph()
{
//messures is shared by several threads: you must lock it to access it safely
lock (measures)
{
//add each measure into the curve
for (int i = 0; i < measures.Count; i++)
{
//fill each with what ever you want
double x = myCurve.Points.Count;
double y = measures[i];
//add a new point to the curve
myCurve.AddPoint(x, y);
}
//all measures have been added
//we can empty the list
measures.Clear();
}
//the curve has been updated so refresh the graph
zedGraphControl2.AxisChange();
zedGraphControl2.Invalidate();
}
private void button1_Click(object sender, EventArgs e)
{
worker.RunWorkerAsync();
}
private void button2_Click(object sender, EventArgs e)
{
worker.CancelAsync();
}
}
}
But It is not displaying continuous graph. I don't know where is the problem in my code.
Please help me to solve this problem.
Any help will be appreciated.
Why don't you try RollingPointPairList instead of list...?
there's an example for Real Time Plot using ZedGraph, Good Luck..:)
It may be that you are clearing your measures list every time you plot the curve. Each time, ZedGraph is plotting a single point and erasing the rest of the curve. Maybe?
Related
I am wondering how to constantly update an readonly textbox.
The text box displays a text that always changes.
My problem is if I create an loop the application won't start and if I start the loop using a button my application freezes and only it only runs the loop.
I also can't use a new thread or the thread that I use to change the variables that are displayed within the text because in this case I just get an error System.InvalidOperationException
I was searching for anwser but I couldn't find one.
When using a thread you have to cause your ui update work to run on the UI thread, and that's where you use an "invoke".
There are many ways to achieve your goal, I'll show you two ways you can do it:
using a thread (BackgroundWorker is just a fancier way to do that)
a Timer (it might be overkill to use a thread just to update a
counter if that is what you are intending).
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;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
bool m_shutdown = false;
int m_counter = 0;
Timer m_timer = new Timer();
BackgroundWorker m_backgroundworker = new BackgroundWorker();
bool m_usetimerway = false; // change this to true to try the timer way
Action m_actionupdatecounter;
public Form1()
{
InitializeComponent();
m_actionupdatecounter = new Action(() =>
{
UpdateCounter();
});
}
private void Form1_Load(object sender, EventArgs e)
{
if (m_usetimerway)
{
m_timer.Interval = 50;
m_timer.Tick += M_timer_Tick;
m_timer.Enabled = true;
}
else
{
m_backgroundworker.DoWork += M_backgroundworker_DoWork;
m_backgroundworker.RunWorkerCompleted += M_backgroundworker_RunWorkerCompleted;
m_backgroundworker.RunWorkerAsync();
}
}
void UpdateCounter()
{
if (this.InvokeRequired)
{
// Get it to be run on the UI thread
this.BeginInvoke( m_actionupdatecounter );
}
else
{
m_counter++;
textBoxCounter.Text = string.Format("{0}", m_counter);
}
}
private void M_timer_Tick(object sender, EventArgs e)
{
// This is already on the UI thread because it's a "WinForms" timer
UpdateCounter();
}
private void M_backgroundworker_DoWork(object sender, DoWorkEventArgs e)
{
while (!m_shutdown)
{
UpdateCounter();
System.Threading.Thread.Sleep(50);
}
}
private void M_backgroundworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
m_shutdown = true;
// To have a more graceful shutdown, you might want to wait for the
// background worker to have "completed" before you actually exit
// your winforms app.
}
}
}
I know this questions gets asked a bit (at least from what I found here so far), but I can't really wrap my head around it. Already tried it with the example from msdn but still no succes. Here is what I'm trying to do: I have a USB-Counter connected to a TLL-ruler. I want to read the value constantly in a loop and write the readout to a textbox without blocking the main UI. I know from other questions that I should use Invoke or Backgroundworker, but have not really found an example which I understood and could use to adjust my code accordingly. The code without any modification, to keep it simple, is as follows:
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.Runtime.InteropServices;
using System.Windows.Forms;
namespace USB_Counter
{
public partial class Form1 : Form
{
[DllImport("HS_UC.dll", EntryPoint = "HS_UC_Close")] //+further DLL imports (driver for USBCounter)
public static extern int HS_UC_Close(int CounterNo);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e) //initialize COunter
{
int A = HS_UC_Init(1, 3);
string B = Convert.ToString(A);
MessageBox.Show(B); //to check if there is no error
}
private void button2_Click(object sender, EventArgs e)
{
HS_UC_SetDistCode(1, 20, 40, 4, 0, 0); //set Reference
HS_UC_SetRefMode(1, 1);
}
private void button3_Click(object sender, EventArgs e)
{
int a = 1;
int A = 0; //variable that takes counter value (the one I want)
int B = 0; //variable that takes counter status
do
{
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
textBox1.Text = "Das ist: " + C;
textBox1.Update();
} while (a == 1);
}
}
}
Now this works as intendet, but as mentioned it blocks the main UI thread (obviously). If anyone found a similar question with some helpful tips to get started with this multithreading topic or any helpful tips regarding my question directly, that would be greatly appreciated.
Best regards from Berlin,
Chris
Update: got it working with following attempt:
private void Counter_Read()
{
int a = 1;
do
{
int A = 0;
int B = 0;
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
UpdateTextBox(C);
} while (a == 1);
}
public void UpdateTextBox(decimal C)
{
if (InvokeRequired)
{
this.Invoke(new Action<decimal>(UpdateTextBox), new object[] { C });
return;
}
textBox1.Text = "Das ist: " + C;
textBox1.Update();
}
private void button3_Click(object sender, EventArgs e)
{
System.Threading.Thread t = new System.Threading.Thread(() => Counter_Read());
t.Start();
}
From that I get a decimal output which i constantly updating and still am able to utilize the other buttons.
outsource the loop code into a method. Inside the method you will need to use BeginInvoke to write to the TextBox
private void DoTheLoop()
{
int a = 1;
int A = 0; //variable that takes counter value (the one I want)
int B = 0; //variable that takes counter status
do
{
HS_UC_GetCounter(1, ref A, ref B);
decimal C = (Convert.ToDecimal(A) / 100);
textBox1.BeginInvoke(new Action(()=>{textBox1.Text = "Das ist: " + C;}));
} while (a == 1);
}
First version using a normal Thread:
Create a Thread and start it with the new method when the button3 is clicked
private void button3_Click(object sender, EventArgs e)
{
System.Threading.Thread t = new System.Threading.Thread(()=>DoTheLoop());
t.Start();
}
This should not block your GUI and the textbox will show the values
Second Version using a BackgroundWorker:
Create a BackgroundWorker and register the DoWork event:
System.ComponentModel.BackgroundWorker worker = new System.ComponentModel.BackgroundWorker();
private void Form1_Load(object sender, EventArgs e)
{
worker.DoWork += Worker_DoWork;
}
inside the eventhandler call the same method DoTheLoop():
private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
DoTheLoop();
}
start the worker in the button click event:
private void button1_Click(object sender, EventArgs e)
{
worker.RunWorkerAsync();
}
Same result in the end :)
You may want to take a look a this link MSDN.
However, a quick tip would be to register a method for the DoWork event and then execute the RunAsynchronously method.
What the application should do
This application should take the input of time (seconds, minutes and hours) and shutdown the computer after that time. It should also update the text box with how long left until the computer has shut down.
What the application actually does
I had an issue that I 'fixed' where the called ac across threads weren't safe, so I fixed it and I don't get that error now. However, the updateThread doesn't update and print the time left; and the text box doesn't get "test" appended to it. The UI also becomes Not Responding. Any help would be much appreciated.
Also, if you see anything else that could be done better, please comment and explain. Thanks!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ShutdownPC
{
public partial class Form1 : Form
{
int inputHours;
int inputMinutes;
int inputSeconds;
Thread sleepingThread;
Thread updatingThread;
NotifyIcon shutdownPCIcon;
Icon defaultIcon;
public Form1()
{
InitializeComponent();
defaultIcon = new Icon("defaultIcon.ico");
shutdownPCIcon = new NotifyIcon();
shutdownPCIcon.Icon = defaultIcon;
shutdownPCIcon.Visible = true;
MenuItem progNameMenuItem = new MenuItem("ShutdownPC by Conor");
MenuItem breakMenuItem = new MenuItem("-");
MenuItem quitMenuItem = new MenuItem("Quit");
ContextMenu contextMenu = new ContextMenu();
contextMenu.MenuItems.Add(progNameMenuItem);
contextMenu.MenuItems.Add(breakMenuItem);
contextMenu.MenuItems.Add(quitMenuItem);
shutdownPCIcon.ContextMenu = contextMenu;
shutdownPCIcon.Text = "ShutdownPC";
quitMenuItem.Click += QuitMenuItem_Click;
}
private void QuitMenuItem_Click(object sender, EventArgs e)
{
shutdownPCIcon.Dispose();
sleepingThread.Abort();
updatingThread.Abort();
this.Close();
}
public void sleepThread()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(sleepThread));
}
else {
textBox1.Enabled = false;
textBox2.Enabled = false;
textBox3.Enabled = false;
button1.Enabled = false;
int totalMilliseconds = ((inputHours * 3600) + (inputMinutes * 60) + inputSeconds) * 1000;
Thread.Sleep(totalMilliseconds);
//Process.Start("shutdown", "/s /t 0");
richTextBox1.AppendText(String.Format("test"));
}
}
public void updateThread()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(updateThread));
}
else {
int totalSeconds = (inputHours * 3600) + (inputMinutes * 60) + inputSeconds;
while (totalSeconds > 0)
{
TimeSpan time = TimeSpan.FromSeconds(totalSeconds);
string timeOutput = time.ToString(#"hh\:mm\:ss");
richTextBox1.AppendText(String.Format(timeOutput));
Thread.Sleep(1000);
richTextBox1.Clear();
totalSeconds--;
}
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
inputHours = Convert.ToInt32(textBox1.Text);
inputHours = int.Parse(textBox1.Text);
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
inputMinutes = Convert.ToInt32(textBox2.Text);
inputMinutes = int.Parse(textBox2.Text);
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
inputSeconds = Convert.ToInt32(textBox3.Text);
inputSeconds = int.Parse(textBox3.Text);
}
private void button1_Click(object sender, EventArgs e)
{
updatingThread = new Thread(new ThreadStart(updateThread));
updatingThread.Start();
sleepingThread = new Thread(new ThreadStart(sleepThread));
sleepingThread.Start();
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
}
}
}
Using Invoke in the beginning of method that runs in separate thread is bad idea, because all code runs in GUI thread and lock it.
You should Invoke only GUI updating code!!!
I'm new to C# and object-oriented programming in general. I've been trying to implement a "Cancel" button into my GUI so that the user can stop it mid-process.
I read this question: How to implement a Stop/Cancel button? and determined that a backgroundWorker should be a good option for me, but the example given doesn't explain how to hand arguments to the backgroundWorker.
My problem is that I do not know how to pass an argument into backgroundWorker such that it will stop the process; I have only been able to get backgroundWorker to stop itself.
I created the following code to try to learn this, where my form has two buttons (buttonStart and buttonStop) and a backgroundWorker (backgroundWorkerStopCheck):
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
using System.Timers;
namespace TestBackgroundWorker
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Set the background worker to allow the user to stop the process.
backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
}
private System.Timers.Timer myTimer;
private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
{
//If cancellation is pending, cancel work.
if (backgroundWorkerStopCheck.CancellationPending)
{
e.Cancel = true;
return;
}
}
private void buttonStart_Click(object sender, EventArgs e)
{
// Notify the backgroundWorker that the process is starting.
backgroundWorkerStopCheck.RunWorkerAsync();
LaunchCode();
}
private void buttonStop_Click(object sender, EventArgs e)
{
// Tell the backgroundWorker to stop process.
backgroundWorkerStopCheck.CancelAsync();
}
private void LaunchCode()
{
buttonStart.Enabled = false; // Disable the start button to show that the process is ongoing.
myTimer = new System.Timers.Timer(5000); // Waste five seconds.
myTimer.Elapsed += new ElapsedEventHandler(myTimer_Elapsed);
myTimer.Enabled = true; // Start the timer.
}
void myTimer_Elapsed(object sender, ElapsedEventArgs e)
{
buttonStart.Enabled = true; // ReEnable the Start button to show that the process either finished or was cancelled.
}
}
}
The code, if it worked properly, would just sit there for five seconds after the user clicked "Start" before re-enabling the Start button, or would quickly reactivate the Start button if the user clicked "Stop".
There are two problems with this code that I am not sure how to handle:
1) The "myTimer_Elapsed" method results in an InvalidOperationException when it attempts to enable the Start button, because the "cross-thread operation was not valid". How do I avoid cross-thread operations?
2) Right now the backgroundWorker doesn't accomplish anything because I don't know how to feed arguments to it such that, when it is canceled, it will stop the timer.
I'd appreciate any assistance!
First of all, the problem to avoid "cross-thread operation was not valid" is use Invoke on controls. You cannot use a control from a different thread.
About the second issue, I would implement it in the following way. This is a minimum background worker implementation with cancel support.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
namespace WindowsFormsApplication5
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Set the background worker to allow the user to stop the process.
backgroundWorkerStopCheck.WorkerSupportsCancellation = true;
backgroundWorkerStopCheck.DoWork += new DoWorkEventHandler(backgroundWorkerStopCheck_DoWork);
}
private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
{
try
{
for (int i = 0; i < 50; i++)
{
if (backgroundWorkerStopCheck.CancellationPending)
{
// user cancel request
e.Cancel = true;
return;
}
System.Threading.Thread.Sleep(100);
}
}
finally
{
InvokeEnableStartButton();
}
}
private void buttonStart_Click(object sender, EventArgs e)
{
//disable start button before launch work
buttonStart.Enabled = false;
// start worker
backgroundWorkerStopCheck.RunWorkerAsync();
}
private void buttonStop_Click(object sender, EventArgs e)
{
// Tell the backgroundWorker to stop process.
backgroundWorkerStopCheck.CancelAsync();
}
private void InvokeEnableStartButton()
{
// this method is called from a thread,
// we need to Invoke to avoid "cross thread exception"
if (this.InvokeRequired)
{
this.Invoke(new EnableStartButtonDelegate(EnableStartButton));
}
else
{
EnableStartButton();
}
}
private void EnableStartButton()
{
buttonStart.Enabled = true;
}
}
internal delegate void EnableStartButtonDelegate();
}
About passing arguments to the worker, you can pass any object in the RunWorkerAsync() method, and its reveived in the backgroundWorkerStopCheck_DoWork method:
...
backgroundWorkerStopCheck.RunWorkerAsync("hello");
...
private void backgroundWorkerStopCheck_DoWork(object sender, DoWorkEventArgs e)
{
string argument = e.Argument as string;
// argument value is "hello"
...
}
Hope it helps.
try this example and you will see how to pass data to and from the BackgroundWorker:
public partial class Form1 : Form
{
BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.WorkerSupportsCancellation = true;
}
private void button1_Click(object sender, EventArgs e)
{
btnStart.Enabled = false;
btnCancel.Enabled = true;
double[] data = new double[1000000];
Random r = new Random();
for (int i = 0; i < data.Length; i++)
data[i] = r.NextDouble();
bw.RunWorkerAsync(data);
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
btnStart.Enabled = true;
btnCancel.Enabled = false;
if (!e.Cancelled)
{
double result = (double)e.Result;
MessageBox.Show(result.ToString());
}
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
double[] data = (double[])e.Argument;
for (int j = 0; j < 200; j++)
{
double result = 0;
for (int i = 0; i < data.Length; i++)
{
if (bw.CancellationPending)
{
e.Cancel = true;
return;
}
result += data[i];
}
e.Result = result;
}
}
private void btnCancel_Click(object sender, EventArgs e)
{
bw.CancelAsync();
btnStart.Enabled = true;
btnCancel.Enabled = false;
}
}
I'm attempting to code an application that reads in the values from an IMU. I'm trying to get the different values of the attitude (i.e. direction) of the IMU for 1 second when using the getAtr_Click method. However, while this is calling the Get_Attitude function, it only changes the textbox values once on my form. How do I make it change each time? (I want to see 10 different values flash by on the textbox).
Here's my 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.Threading;
using System.Timers;
using VectorNav.Devices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public static Vn100 vn100 = new Vn100("COM5", 9600);
// New Vn100 on COM5
private void Get_Attitude()
//gets the current yaw, pitch, roll in degrees, and displays
{
var attitude = vn100.CurrentAttitude;
yaw.Text = Convert.ToString(attitude.Ypr.YawInDegs);
pitch.Text = Convert.ToString(attitude.Ypr.PitchInDegs);
roll.Text = Convert.ToString(attitude.Ypr.RollInDegs);
}
public Form1()
//connect to the Vn100, set its output to YPR, output at 10Hz
{
InitializeComponent();
vn100.Connect();
vn100.SetAsyncDataOutputType(Vn100.AsyncOutputType.Ypr, true);
vn100.SetAsyncDataOutputFreq(10, true);
}
private void Form1_Load(object sender, EventArgs e)
{
Get_Attitude();
}
private void tare_Click(object sender, EventArgs e)
{
vn100.Tare(true);
vn100.Tare(true); //for some reason it doesn't display the correct Attitude values w/out a double Tare
Get_Attitude();
}
private void getAtr_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
while (vn100.CurrentAttitude == null)
Thread.Sleep(10);
Get_Attitude();
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
//disconnect from the Vn100 when the box is closed
{
vn100.Disconnect();
base.OnFormClosing(e);
}
}
}
This is because you are haulting the UI thread:
private void getAtr_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
while (vn100.CurrentAttitude == null)
Thread.Sleep(10); // < - UI can't respond in a sleep
Get_Attitude();
}
}
You could do this:
private void getAtr_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
while (vn100.CurrentAttitude == null)
{
Thread.Sleep(10); // < - UI can't respond in a sleep
Application.DoEvents();
}
Get_Attitude();
}
}
Or you could force the update to happen synchronously:
private void tare_Click(object sender, EventArgs e)
{
vn100.Tare(true);
vn100.Tare(true); //for some reason it doesn't display the correct Attitude values w/out a double Tare
Get_Attitude();
this.Update();
}
Or you could use a timeout to update the form instead of a loop.
private void tare_Click(object sender, EventArgs e)
{
System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
myTimer.Tick += new EventHandler(TimerEventProcessor);
myTimer.Interval = 10;
myTimer.Start();
}
void TimerEventProcessor(object sender, EventArgs e)
{
Get_Attitude();
}
Do you want to see 10 (possibly) different values displayed in your textboxes during 1s, at an interval of 10ms, once you call getAttr_Click()?
If yes, then that would be the way to do it:
private void getAtr_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
while (vn100.CurrentAttitude == null)
Thread.Sleep(10);
Get_Attitude();
Thread.Sleep(10);
}
}
In your version, the thread that called the getAttr_Click() method just checks vn100.CurrentAttutude at 10ms intervals. Once that has a non-null value, I suspect it remains non-null, meaning your while() loop will be skipped in every for() iteration so Get_Attitude() will be called 10 times in a row (probably) so fast that you only get to see the last values on the screen.
Thing is, this will keep your UI unresponsive each 10ms during the click, so you might consider calling this getAtr_Click() asynchronously or other trivial solution.
EDIT: Actually, it would help knowing the behaviour of the vn100 component. In the case it is unpredictable, the only thing you can be sure of is displaying 10 different values at no less than 10ms distance in time, regardless if you do everything on the UI thread or on a different thread. It is all related to that vn100.CurrentAttitude behaviour...
change it to:
private void Get_Attitude()
//gets the current yaw, pitch, roll in degrees, and displays
{
var attitude = vn100.CurrentAttitude;
yaw.Text = Convert.ToString(attitude.Ypr.YawInDegs);
pitch.Text = Convert.ToString(attitude.Ypr.PitchInDegs);
roll.Text = Convert.ToString(attitude.Ypr.RollInDegs);
vn100.CurrentAttitude = null;
}
private void getAtr_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
while (vn100.CurrentAttitude == null)
Thread.Sleep(10);
Get_Attitude();
Application.DoEvents();
}
}