Updating UI with invoke or a background worker - c#

Im working on a school project where I need to do some temperature measurements. The task is to randomly create some temperatures and then calculate the average. My code is as following, but I have a problem with the Threads. It can't be called a object before a window handle is created. I searched the net and found out that a background worker is more useful for updating the UI. I'm not that skilled in programming yet, because i just started school.
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.Collections;
using System.IO;
using System.Threading;
namespace Temperatur
{
public partial class Form1 : Form
{
static Random rnd = new Random();
static ArrayList tempList = new ArrayList();
static SemaphoreSlim w, e, b;
public Form1()
{
InitializeComponent();
w = new SemaphoreSlim(1);
e = new SemaphoreSlim(0);
b = new SemaphoreSlim(6);
Thread t1 = new Thread(randomNr);
t1.Start();
Thread t2 = new Thread(gennemsnit);
t2.Start();
}
public void randomNr()
{
//Thread.Sleep(100);
for (int i = 0; i < 10; i++)
{
//b.Wait();
//w.Wait();
int number = rnd.Next(36, 42);
tempList.Add(number);
listBox1.BeginInvoke((MethodInvoker)delegate
{
listBox1.Items.Add(number);
});
//w.Release();
//e.Release();
}
}
public void gennemsnit()
{
double avg = 0;
double nb = 0;
//Thread.Sleep(200);
for (int i = 0; i < tempList.Count; i++) //i < tempList.Count
{
//e.Wait();
//w.Wait();
nb += Convert.ToDouble(tempList[i]);
avg = nb / tempList.Count;
listBox2.Invoke((MethodInvoker)delegate //listbox2.invoke
{
listBox2.Items.Add(avg);
});
//w.Release();
//b.Release();
}
}
}
}

It can't be called a object before a window handle is created.
Don't start the threads in the constructor of the Form. Use either the Load() or Shown() events instead:
public Form1()
{
InitializeComponent();
w = new SemaphoreSlim(1);
e = new SemaphoreSlim(0);
b = new SemaphoreSlim(6);
this.Load += new EventHandler(Form1_Load);
}
void Form1_Load(object sender, EventArgs e)
{
Thread t1 = new Thread(randomNr);
t1.Start();
Thread t2 = new Thread(gennemsnit);
t2.Start();
}

BackgroundWorker is indeed what you need. If you "report progress" you can pass an object to the GUI thread in the progress report. Set up progress reporting like this:
BackgroundWorker bw = new BackgroundWorker();
...
bw.WorkerReportsProgress = true;
bw.DoWork += bw_DoWork;
bw.ProgressChanged += bw_ProgressChanged;
...
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Here you have passed yourself any object you like. Could be your own class. Could be a string, etc.
MyClass myObject = e.UserState as MyClass;
// Then you can add it to your GUI as necessary, for example
listbox2.Items.Add(myObject);
}
You probably just want to pass a string as your object and then add that string to your listbox.

I'm not sure what your code is trying to do, but your task is quite easy. You don't really have to worry about threads. For simplicity you could schedule a timer to run every say 1s and pick a random number / read temperature and update it on the UI. If you select the timer from the forms controls (System.Windows.Forms.Timer) then you can directly update the UI from the function it calls when it fires. If you use a timer from System.Timers.Timer, you should use BeginInvoke to update the list.

Related

How to make input line at the bottom of console using C#?

I want to make console like Minecraft server's one, where you see logs, and you can type in commands. I want see something like this this. I tried from another site, but still do not work. Maybe do it using events? But it will be too unefficient. Main wrong thing in my code is HandleInput(), I tried make it using lock, but it do not work. I need, to change the input line (with everything, that user enters) when there is a log in a console. I need to handle input(at the buttom of console), and "server" at one time(server sending output to console also).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
using CraftCoin;
using System.Threading;
using Snowy;
using Snowy.Data;
using Snowy.Server;
namespace LAVA2
{
public static class ConsoleWriter
{
static object lockObject = new object();
public static void WriteLine(string value) { lock (lockObject) { System.Console.WriteLine(value); } }
public static void Write(string value) { lock (lockObject) { System.Console.Write(value); } }
public static string ReadLine(string value) { lock (lockObject) { return System.Console.ReadLine(); } }
}
class Program
{
static SnowyServer snowy = new SnowyServer();
static object locker = new object();
static void Main(string[] args)
{
WriteLine("Starting server...");
SnowyEvents events = new SnowyEvents();
snowy.setEvents(events);
List<SnowyPlugin> plugins = new List<SnowyPlugin> { new LavaPluginOnSnowy("LOLOLOLOL", events) };
snowy.setPlugins(plugins);
snowy.getPluginsAsSnowy().ForEach(delegate (SnowyPlugin plugin)
{
plugin.setServer(snowy);
});
snowy.getEvents().onPluginsLoad += CreateCoin;
//Thread thread = new Thread(new ParameterizedThreadStart(HandleInput));
Thread thread = new Thread(() => HandleInput(ref snowy, ref plugins));
thread.Start();
snowy.Start();
thread.Join();
}
static void HandleInput(ref SnowyServer server, ref List<SnowyPlugin> plugins)
{
List<Command> commands = new List<Command> { new StopCommand(/*(SnowyServer)*/server), new RestartCommand(server, plugins) };
CommandHandler handler = new CommandHandler(commands);
while (server.getServerState() != SnowyServerState.Running)
{ Thread.Sleep(50); }
while (true)
{
string result;
lock (locker)
{
Write("> ");
result = ReadLine();
}
string[] tmp = result.Split(' ');
string[] arr = new string[tmp.Length - 1];
for (int i = 0; i < tmp.Length; i++)
{
if (i == 0) continue;
arr[i-1] = tmp[i];
}
handler.Run(tmp[0], arr);
}
}
static void CreateCoin()
{
SnowCoin coin = new SnowCoin("SnowCoin", snowy);
coin.BlockCoinState(Snowy.LAVA2.COIN_BLOCK_STATE.Permament_block);
User user1 = new User(new Wallet("user1", "SnowCoin", 150), "UserName", "User1uid");
User user2 = new User(new Wallet("user2", "SnowCoin"), "UserName", "User2uid");
WriteLine($"{user1.getWallet().getWalletMoney()}\n---\n{user2.getWallet().getWalletMoney()}");
UserSender userSender = new UserSender(user1, "user1");
UserTarget userTarget = new UserTarget(user2, "user2");
coin.SendCoins(ref userSender, ref userTarget, "", 100, new string[] { "user1", "user2" });
user1 = new User(userSender.getWallet().toWallet("user1"), userSender.getUser());
user2 = new User(userTarget.getWallet().toWallet("user2"), userTarget.getUser());
WriteLine($"{user1.getWallet().getWalletMoney()}\n---\n{user2.getWallet().getWalletMoney()}");
}
}
}
my output:
Starting server...
> YAY!
First tick!
tick №2!
tick №3!
tick №4!
tick №5!
stick №6!
tick №7!
tick №8!
ttick №9!
tick №10!
otick №11!
tick №12!
ptick №13!
tick №14!
tick №15!
tick №16!
tick №17!
tick №18!
tick №19!
tick №20!
tick №21!
tick №22!
tick №23!
Expected output:
Starting server...
YAY!
First tick!
tick №2!
tick №3!
tick №4!
tick №5!
tick №6!
tick №7!
tick №8!
ttick №9!
tick №10!
tick №11!
tick №12!
tick №13!
tick №14!
tick №15!
tick №16!
tick №17!
tick №18!
tick №19!
tick №20!
tick №21!
tick №22!
tick №23!
> stop

How to create and add controls in run time with BackgroundWorker

I don't know how to implement a method with a separate thread using the BackgroundWorker in WinForms.
I want this method (after every click on a button) to perform:
create ProgressBar (each new one under the previous one)
create Bitmap and BackgroundWorker
set color of every pixel in that Bitmap in the separate thread using BackgroundWorker
display a precentage progress on the ProgressBar
after completing: create a new form with bitmap on the background
after completing: remove the ProgressBar
My code:
List<BackgroundWorker> Workers;
List<ProgressBar> Progress;
int OperationsCount = 0;
private void ShowProgress(int n, int percent)
{
Progress[n].Value = percent;
}
private void Blend(object sender, DoWorkEventArgs e)
{
Bitmap BlendedImage = ... // creates a bitmap
for (int i = 0; i < BlendedImage.Width; i++)
{
for (int j = 0; j < BlendedImage.Height; j++)
{
... //changing colour of every pixel
}
this.Invoke(new Action(()=>ShowProgress((int)e.Argument, (int)(100 * (double)(i/BlendedImage.Width)))));
}
Form BlendedImage_Form = new Form();
BlendedImage_Form.Size = new Size(BlendedImage.Width, BlendedImage.Height);
BlendedImage_Form.BackgroundImage = BlendedImage;
BlendedImage_Form.BackgroundImageLayout = ImageLayout.Stretch;
this.Invoke(new Action(() => BlendedImage_Form.Show()));
}
private void PerformBlending_Button_Click(object sender, EventArgs e)
{
int n = OperationsCount++;
Progress.Add(new ProgressBar());
Progress[n].Size = ...
Progress[n].Location = ...
Progress[n].Maximum = 100;
this.Controls.Add(Progress[n]);
Workers.Add(new BackgroundWorker());
Workers[n].DoWork += new DoWorkEventHandler(Blend);
Workers[n].RunWorkerCompleted += (object _sender, RunWorkerCompletedEventArgs _e) =>
{
//OperationsCount--;
//Progress[n].Dispose();
//this.Controls.Remove(Progress[n]);
//Progress.RemoveAt(n);
};
Workers[n].RunWorkerAsync(n);
}
When I click the button only once then everything seems to be good but when I click the button two times then program:
creates the first ProgressBar which shows progress correctly and the new form and bitmap are displayed also correctly
creates the second ProgressBar but it doesn't show the progress at all and no form and no bitmap is displayed.
PS I'd rather use BackgroundWorker than other tools.
As per your comment here is the solution
public void DoSomething()
{
if (this.InvokeRequired)
{
this.Invoke(new Action(()=> DoSomething()));
}
else
{
// update the ui from here, no worries
}
}
In this code, I am modifying the object on main thread. If the calls made from non-UI thread this will goes in InvokeRequired.
// From this code you given in comment
https://pastebin.com/45jQXCt9
Please try with making instance inside invoke. It should work.

Stop midi notes buffering in c#

I have created a program in C# for my children to test them on reading music. It displays random notes at set intervals (determined by a timer) and then checks the currently displayed note against input from the keyboard to see if they got it right. It is working well, apart from the fact that if two notes are accidentally pressed at once, the second of them is taken to be the intended answer for the subsequent note, which hasn't even been displayed yet! I assume that the notes that are played on the musical keyboard are stored in a buffer and then used when next needed. I would like to be able to "wipe the slate clean" when a new note is displayed on the screen, so that any previous input is disregarded. I'd be grateful for any thoughts as to how I can do this please.
Some of the code is below:
using System;
using Midi;
using System.Media;
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.Threading;
namespace WindowsFormsApplication6
{
public partial class Form1 : Form
{
private void Form1_Load(object sender, EventArgs e)
{
// Create persistent delegates which we can add and remove to events.
noteOnHandler = new InputDevice.NoteOnHandler(this.NoteOn);
noteOffHandler = new InputDevice.NoteOffHandler(this.NoteOff);
InputDevice preferredInputDevice = InputDevice.InstalledDevices[0];
UseInputDevice(InputDevice.InstalledDevices[0]);
}
private void UseInputDevice(InputDevice newInputDevice)
{
if (newInputDevice == inputDevice)
{
return;
}
if (inputDevice != null)
{
if (inputDevice.IsOpen)
{
if (inputDevice.IsReceiving)
{
inputDevice.StopReceiving();
}
inputDevice.Close();
}
inputDevice.NoteOn -= noteOnHandler;
inputDevice.NoteOff -= noteOffHandler;
}
inputDevice = newInputDevice;
inputDevice.NoteOn += noteOnHandler;
inputDevice.NoteOff += noteOffHandler;
inputDevice.Open();
inputDevice.StartReceiving(null);
}
// Method called when the input device receives a NoteOn message. Updates
// the input status label. Respects GUI thread affinity by invoking to the
// GUI thread if necessary.
public void NoteOn(NoteOnMessage msg)
{
if (button1.Text == "STOP")
{
if (InvokeRequired)
{
BeginInvoke(noteOnHandler, msg);
return;
}
if (string.Equals(String.Format("{0}", msg.Pitch), notename)) { currentscore++; correct[numberofnotes] = true; using (SoundPlayer player = new SoundPlayer("chime_up.wav")) { player.PlaySync(); } }
else { using (SoundPlayer player = new SoundPlayer("klaxon_ahooga.wav")) { player.PlaySync(); } correct[numberofnotes] = false; }
outof++;
score.Text = String.Format("{0}", currentscore);
notesshown.Text = String.Format("{0}", outof);
numberofticks = 0;
if (outof < 20) { generate_note(); }
Redrawit();
Invalidate();
if (outof == 20) { reset_screen(); }
}
}
// Method called when the input device receives a NoteOff message. Updates
// the input status label. Respects GUI thread affinity by invoking to the
// GUI thread if necessary.
public void NoteOff(NoteOffMessage msg)
{
if (InvokeRequired)
{
BeginInvoke(noteOffHandler, msg);
return;
}
}
// Persistent delegate objects for the note handlers.
private InputDevice.NoteOnHandler noteOnHandler;
private InputDevice.NoteOffHandler noteOffHandler;
}
}

Making an updating winform multi threaded

I have been breaking my head over this for a long time now and I can't seem to find how to solve this. Simply put I need to present Parallel programming to my class and i am making a demo that clearly shows the diffrence between single threaded and multi threaded.
First the program takes a screenshot and loads that in this class into a bitmap.
Then it loads the winform with a picturebox called screenshot. Next it does Form1_Load and loads the screenshot into the picturebox.
Then Form1_Shown Runs the for loop which just scrambles the pixels around based on a randomizer and updates the image from the left to the right.
example: http://gyazo.com/ab04583bb33de59d08407886da1c4870
This all works.
I now want to make it so that the screenshot is being updated from the left to the middle by the first thread, and from the middle to the right with the second thread.
But when i put it in 2 separate threads it says "An unhandled exception of type 'System.InvalidOperationException' occurred in System.Windows.Forms.dll"
The error implies I am making illegal cross thread calls. Particulary on screenshot and probably also on mybitmap.
Visual studio helps me by linking me to "How to: Make Thread-Safe Calls to Windows Forms Controls"
But i am not getting any wiser out of this information which is probably because i am not that fluent with the C# terminology yet.
How should i approach/fix this ?
This is the class where everything happens (except taking the screenshot):
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ScreenOutput
{
public partial class Form1 : Form
{
PictureBox screenshot;
Bitmap myBitmap = new Bitmap(#".\screenshot.jpg");
public Form1()
{
InitializeComponent();
}
private int Xcount;
private int Ycount;
private int maxXValue = Screen.PrimaryScreen.Bounds.Width - 1;
private int maxXValueT1 = 960;
private int maxXValueT2 = 1919;
private int maxYValue = Screen.PrimaryScreen.Bounds.Height - 1;
private int maxYValueT1 = 539;
private int maxYValueT2 = 1079;
private void Form1_Load(object sender, EventArgs e)
{
screenshot.Image = myBitmap;
}
private void Form1_Shown(object sender, EventArgs e)
{
Thread startThread1 = new Thread(new ThreadStart(thread1));
Thread startThread2 = new Thread(new ThreadStart(thread2));
startThread1.Start();
startThread2.Start();
Thread.Sleep(10000); //waiting for completion
startThread1.Abort();
startThread2.Abort();
//this is how it would work without multithreading
/*Random random = new Random();
for (Xcount = 0; Xcount < maxXValue; Xcount++)
{
screenshot.Refresh();
for (Ycount = 0; Ycount < maxYValue; Ycount++)
{
int calculatedX = Xcount + random.Next(0, maxXValue);
if (calculatedX > maxXValue) calculatedX = maxXValue;
myBitmap.SetPixel(Xcount, Ycount, myBitmap.GetPixel(calculatedX, Ycount));
}
}
Thread.Sleep(2000);*/
Application.Exit();
}
public void thread1()
{
Random random = new Random();
for (Xcount = 0; Xcount < maxXValueT1; Xcount++)
{
screenshot.Refresh();
for (Ycount = 0; Ycount < maxYValueT1; Ycount++)
{
int calculatedX = Xcount + random.Next(0, maxXValueT1);
if (calculatedX > maxXValue) calculatedX = maxXValueT1;
myBitmap.SetPixel(Xcount, Ycount, myBitmap.GetPixel(calculatedX, Ycount));
}
}
}
public void thread2()
{
Random random = new Random();
for (Xcount = 0; Xcount < maxXValueT2; Xcount++)
{
screenshot.Refresh();
for (Ycount = 0; Ycount < maxYValueT2; Ycount++)
{
int calculatedX = Xcount + random.Next(0, maxXValueT2);
if (calculatedX > maxXValueT2) calculatedX = maxXValueT2;
myBitmap.SetPixel(Xcount, Ycount, myBitmap.GetPixel(calculatedX, Ycount));
}
}
}
}
}
You will need to Invoke the GUI thread.
I prefer the following solution.
Using a Form member ('screenshot' in this case) check to see if InvokeRequired = true. If so then Invoke a delegate using that members BeginInvoke() function as the following example demonstrates:
private void ScreenshotRefresh()
{
if(screenshot.InvokeRequired)
{
screenshot.BeginInvoke(new MethodInvoker(this.ScreenshotRefresh));
}
else
{
screenshot.Refresh();
}
}

The calling thread cannot access this object because a different thread owns it

So I am making a simple brick breaking game in c#/wpf. I am running into an issue using timers, I feel like it is probably a simple fix but here is whats happening. Whenever t_Elapsed is fired it attempts to call Update() but when it does its like OMG Im not in the right thread so I cant do that sir. How do I invoke the method from the Game from the proper thread? (And yes I know the code is ugly and has magic numbers but I just kinda chugged it out without putting a lot of effort in. And yes I have zero experience programming games)
public partial class Game : Grid
{
public bool running;
public Paddle p;
public Ball b;
Timer t;
public Game()
{
Width = 500;
Height = 400;
t = new Timer(20);
p = new Paddle();
b = new Ball();
for (int i = 15; i < 300; i += 15)
{
for (int j = 15; j < 455; j += 30)
{
Brick br = new Brick();
br.Margin = new Thickness(j, i, j + 30, i + 15);
Children.Add(br);
}
}
Children.Add(p);
Children.Add(b);
p.Focus();
t.AutoReset = true;
t.Start();
t.Elapsed += new ElapsedEventHandler(t_Elapsed);
}
void t_Elapsed(object sender, ElapsedEventArgs e)
{
if (running)
{
Update();
}
}
void Update()
{
b.Update(); //Error here when Update is called from t_Elapsed event
}
void Begin()
{
running = true;
b.Initiate();
}
}
You should use the DispatcherTimer object instead, it will ensure that the timer events are published to the correct thread.
Timer elapsed events fire on a thread from the thread pool (http://www.albahari.com/threading/part3.aspx#_Timers) and not on the UI thread. Your best approach is to invoke the control's dispatcher through a call like this:
yourControl.Dispatcher.BeginInvoke(
System.Windows.Threading.DispatcherPriority.Normal
, new System.Windows.Threading.DispatcherOperationCallback(delegate
{
// update your control here
return null;
}), null);
The calling thread cannot access this object because a different thread owns it
this.Dispatcher.Invoke((Action)(() =>
{
...// your code here.
}));

Categories