I know this question has been asked several times an I spent all day trying to understand other answers, but since I am very new to C# and WPF nothing helped me so far. I will try to explain my exact problem as much as I can so it will directly help me.
In my MainWindow.xaml I have a progress bar and some button starting a new thread and a long calculation:
<ProgressBar Height="....... Name="progressBar1"/>
<Button Content="Button" Name="button1" Click="button1_Click" />
Now within my MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
Thread thread = new Thread(new ParameterizedThreadStart(MyLongCalculation));
ParameterClass myParameters = new ParameterClass();
thread.Start(myParameters);
}
public void MyLongCalculations(object myvalues)
{
ParameterClass values = (ParameterClass)myvalues;
//some calculations
}
}
public class ParameterClass
{
//public variables...
}
Now somehow I have to include somethign in my method MyLongCalculations that will keep updating progressBar1. However, I just can't manage to get it working.
I know all this is very simple, but unfortunately it is the level I am at the moment on with C# so I hope an answer not too complicated and as detailed as possible would be great.
Background worker is well suited for this.
try this:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
// Initialize UI
InitializeComponent();
// Process data
ProcessDataAsync(new ParameterClass { Value = 20 });
}
/// <summary>
/// Processes data asynchronously
/// </summary>
/// <param name="myClass"></param>
private void ProcessDataAsync(ParameterClass myClass)
{
// Background worker
var myWorker = new BackgroundWorker
{
WorkerReportsProgress = true,
};
// Do Work
myWorker.DoWork += delegate(object sender, DoWorkEventArgs e)
{
// Set result
e.Result = MyLongCalculations(myClass);
// Update progress (50 is just an example percent value out of 100)
myWorker.ReportProgress(50);
};
// Progress Changed
myWorker.ProgressChanged += delegate(object sender, ProgressChangedEventArgs e)
{
myProgressBar.Value = e.ProgressPercentage;
};
// Work has been completed
myWorker.RunWorkerCompleted += delegate(object sender, RunWorkerCompletedEventArgs e)
{
// Work completed, you are back in the UI thread.
TextBox1.Text = (int) e.Result;
};
// Run Worker
myWorker.RunWorkerAsync();
}
/// <summary>
/// Performs calculations
/// </summary>
/// <param name="myvalues"></param>
/// <returns></returns>
public int MyLongCalculations(ParameterClass myvalues)
{
//some calculations
return (myvalues.Value*2);
}
}
/// <summary>
/// Custom class
/// </summary>
public class ParameterClass
{
public int Value { get; set; }
}
You can use Dispatcher.BeginInvoke() to push UI changes on the UI thread rather than worker thread. Most important thing - you need to access Dispatcher which is associated with UI thread not a worker thread you are creating manually. So I would suggest cache Dispatcher.Current and then use in a worker thread, you can do this via ParametersClass or just declaring a dispatchr field on a class level.
public partial class MainWindow
{
private Dispatcher uiDispatcher;
public MainWindow()
{
InitializeComponents();
// cache and then use in worker thread method
this.uiDispatcher = uiDispatcher;
}
public void MyLongCalculations(object myvalues)
{
ParameterObject values = (ParameterObject)myvalues;
this.uiDispatcher.BeginInvoke(/*a calculations delegate*/);
}
}
Also if you need to pass a UI dispatcher in some service/class (like ParametersClass) I would suggest take a look at this nice SO post which show how you can abstract it by an interfaces with ability to push UI changes synchronously/asynchronously so it would be up to a caller (basically use Invoke() or BeginInvoke() to queue a delegate in the UI messages pipeline).
Related
There is a code represents Console Application where I've done new Thread for the form and displaying a CustomForm on our new thread, I've also tried some kind of data transfer but I haven't successed.
Program.cs code ...
class Program {
public static CustomForm _customForm {
get {
return customForm;
}
set {
customForm = value;
customForm.Show();
}
}
private static CustomForm customForm;
/// <summary>
/// Static method which constains all the magic for the console!
/// </summary>
/// <param name="args"></param>
static void Main(string[] args) {
// Declaring Thread for the FormThread.
Thread formThread = new Thread(new ThreadStart(FormThread));
// Fires out the work of the thread.
formThread.Start();
Console.ReadKey();
// And console is still running?
// Thread formThread is still running too, thats the reason bruh!
}
/// <summary>
/// Static method which constains all the magic for the form!
/// </summary>
static void FormThread() {
customForm.lbl.Text = "Yolo, it wurks!";
Application.Run(new CustomForm());
}
}
CustomForm.cs code ...
public partial class CustomForm : Form {
public string lblText {
get {
return lbl.Text;
}
set {
lbl.Text = value;
}
}
/// <summary>
/// Just initializer, something what we'll never understand.
/// </summary>
public CustomForm() {
InitializeComponent();
}
/// <summary>
/// When the form is loaded.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnLoad(object sender, EventArgs e) {
Program._customForm = this;
}
}
The only thing I want to do is call the lbl's text property and set some value in a program.cs, not in the customform.cs
Sometimes form wont to show or the lbl in the form isn't changed.
customForm.lbl.Text = "Yolo, it wurks!"; executes before you are creating CustomForm.
Probably, you need to create your form in the main and pass it into Application.Run(CustomForm);
static void Main(string[] args) {
// Declaring Thread for the FormThread.
Thread formThread = new Thread(new ThreadStart(FormThread));
// Fires out the work of the thread.
customForm = new CustomForm();
formThread.Start();
Console.ReadKey();
// And console is still running?
// Thread formThread is still running too, thats the reason bruh!
}
Also, you can't change a control property from other threads. In order to change property from other thread use Invoke method.
public partial class CustomForm : Form {
public string lblText
{
get
{
return lbl.Text;
}
set
{
if (lbl.InvokeRequired)
lbl.Invoke((MethodInvoker) (() => lbl.Text = value));
else
lbl.Text = value;
}
}
}
I want to make a render loop to render on a WPF Window or a WinForm. Therefore I want to use SharpGL (https://sharpgl.codeplex.com/). To make my loop I made a thread:
public void Run()
{
IsRunning = true;
this.Initialize();
while (IsRunning)
{
Render(/* arguments here */);
// pausing and stuff
}
Dispose();
}
In Render I want to send the Draw Calls to the GPU. So far there is no problem. But Winforms and WPF need their own thread and loop. So I can't just create a Window and draw onto like in Java with LWJGL (https://www.lwjgl.org/), which I used before. I have to start another thread, that runs the Form in an Application (I cut out error handling to make it short):
[STAThread]
private void HostWinFormsApplication()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
// get Display
Display = Engine.MainModule.Resolve<IDisplay>();
Display.Initialize();
Display.Closing += (s, e) => Stop();
Application.Run(Display as Form);
}
When my Renderer wants to access the OpenGL-Control on my Form and use it, an error occurs, as WinForms (and WPF) don't want their Controls to be manipulated by other Threads. So maybe an Invoke is an option, but this would delay my drawcalls and become a bottleneck.
A timer isn't an option, too, as it isn't accurate and unflexible... And I simply don't like it.
And doing everything inside the Window Code may be possible, but I want an application being independent of its Display, so that it can be changed. It should be an application having a Display not a Display running an application.
In LWJGL I just had the possibility to create and initialize a Display and then simply use it. The only thing to consider was updating it and everything went fine.
So I just want to create a Window in my render thread and draw onto. If I do it this way, the window just gets unusable and greyish as it needs this .Net-Loop. Is there any possibility to realize that or does anybody know another way to create Windows? Can I handle the window loop manually?
Any idea is welcome.
If there would be a way to do this with a WPF Window it would be awesome. Then I could have an OpenGL Control and all WPF-Stuff to make an Editor!
The solution was to call
Application.DoEvents();
in my Render Loop manually, as someone told me. So I didn't had to use Application.Run and were able to use my Form in the thread of the loop:
public void Run()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(true);
IsRunning = true;
this.Initialize();
MyForm = new CoolRenderForm();
MyForm.Show();
while (IsRunning)
{
Render(/* arguments here */);
Application.DoEvents();
// refresh form
// pausing and stuff
}
Dispose();
}
Based on my question: How to make a render loop in WPF?
WPF render loop
The best way to do this is to use the per-frame callbacks provided by the static
CompositionTarget.Rendering event.
WinForms render loop
Below is the code of a WinForms render loop class that I made based on this blog post:
Just use:
WinFormsAppIdleHandler.Instance.ApplicationLoopDoWork += Instance_ApplicationLoopDoWork;
WinFormsAppIdleHandler.Instance.Enabled = true;
Class:
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
namespace Utilities.UI
{
/// <summary>
/// WinFormsAppIdleHandler implements a WinForms Render Loop (max FPS possible).
/// Reference: http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
/// </summary>
public sealed class WinFormsAppIdleHandler
{
private readonly object _completedEventLock = new object();
private event EventHandler _applicationLoopDoWork;
//PRIVATE Constructor
private WinFormsAppIdleHandler()
{
Enabled = false;
SleepTime = 5; //You can play/test with this value
Application.Idle += Application_Idle;
}
/// <summary>
/// Singleton from:
/// http://csharpindepth.com/Articles/General/Singleton.aspx
/// </summary>
private static readonly Lazy<WinFormsAppIdleHandler> lazy = new Lazy<WinFormsAppIdleHandler>(() => new WinFormsAppIdleHandler());
public static WinFormsAppIdleHandler Instance { get { return lazy.Value; } }
/// <summary>
/// Gets or sets if must fire ApplicationLoopDoWork event.
/// </summary>
public bool Enabled { get; set; }
/// <summary>
/// Gets or sets the minimum time betwen ApplicationLoopDoWork fires.
/// </summary>
public int SleepTime { get; set; }
/// <summary>
/// Fires while the UI is free to work. Sleeps for "SleepTime" ms.
/// </summary>
public event EventHandler ApplicationLoopDoWork
{
//Reason of using locks:
//http://stackoverflow.com/questions/1037811/c-thread-safe-events
add
{
lock (_completedEventLock)
_applicationLoopDoWork += value;
}
remove
{
lock (_completedEventLock)
_applicationLoopDoWork -= value;
}
}
/// <summary>
/// FINALMENTE! Imagem ao vivo sem travar! Muito bom!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Application_Idle(object sender, EventArgs e)
{
//Try to update interface
while (Enabled && IsAppStillIdle())
{
OnApplicationIdleDoWork(EventArgs.Empty);
//Give a break to the processor... :)
//8 ms -> 125 Hz
//10 ms -> 100 Hz
Thread.Sleep(SleepTime);
}
}
private void OnApplicationIdleDoWork(EventArgs e)
{
var handler = _applicationLoopDoWork;
if (handler != null)
{
handler(this, e);
}
}
/// <summary>
/// Gets if the app still idle.
/// </summary>
/// <returns></returns>
private static bool IsAppStillIdle()
{
bool stillIdle = false;
try
{
Message msg;
stillIdle = !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
}
catch (Exception e)
{
//Should never get here... I hope...
MessageBox.Show("IsAppStillIdle() Exception. Message: " + e.Message);
}
return stillIdle;
}
#region Unmanaged Get PeekMessage
// http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
[System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
#endregion
}
}
I tried to set Text property of TextBox from another thread. I got this exception below;
"Cross-thread operation not valid: Control 'recTpcTxt' accessed from a thread other than the thread it was created on."
Then, I used BackgroundWorker to solve this issue. However, I faced with the same exception message.
EDIT[1]:
Actually, I take a guide myself this link ; https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx. I can solve my problem by using invokeproperty. However, I cannot solve my problem with backgroundworker.
Is there something wrong in my solution? How do I fix my solution to set some property of UI variable?
EDIT[2]: More code to clarify the issue;
MqttManager.cs;
public partial class MqttManager : Form
{
MqttHandler mqttHandler = new MqttHandler();
public static MqttManager managerInst;
public MqttManager()
{
InitializeComponent();
managerInst = this;
...
}
...
private BackgroundWorker backgroundWorker;
public void NotifyUIForRecMsg(string topic, string message)
{
object[] objArr = { topic, message };
this.backgroundWorker.RunWorkerAsync(objArr);
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(5000);
e.Result = e.Argument;
}
private void backgroundWorker_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
object[] res = (object[])e.Result;
this.recTpcTxt.Text = (String)res[0];
}
}
MqttManager.Design.cs;
partial class MqttManager
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
...
this.backgroundWorker = new System.ComponentModel.BackgroundWorker();
this.backgroundWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker_DoWork);
this.backgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker_RunWorkerCompleted);
}
#endregion
...
}
MqttHandler.cs;
class MqttHandler
{
MqttClient client;
...
/// <summary>
/// Publish received event handler.
/// </summary>
private void client_MqttMsgPublishReceived(Object sender, MqttMsgPublishEventArgs e)
{
MqttManager.managerInst.NotifyUIForRecMsg(e.Topic, Encoding.UTF8.GetString(e.Message));
}
}
check this:
https://msdn.microsoft.com/en-us/library/ms171728(v=vs.110).aspx
Basically, to set a control propertiy you have to be in the same UI thread.
This simple solution move the call to textbox1.Text = someText in the UI thread
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.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
also, you can use textBox1.BeginInvoke instead of Invoke: it will run in UI thread, without locking the caller thread waiting for SetText delegate to be completed
[Edit] to do it in your backgroundWorker:
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
object[] arg = (object[])e.Argument;
SetTextToTextBox(recTpcTxt, (string)arg[0]);
SetTextToTextBox(recMsgTxt, (string)arg[1]);
}
private void SetTextToTextBox(TextBox toSet, 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 (toSet.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
toSet.Invoke(d, new object[] { text });
}
else
{
toSet.Text = text;
}
}
[Edit 2]
To properly use backgroundworker
Register for events DoWork and RunWorkerCompleted
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
Before exiting backgroundWorker1_DoWork, set result property of eventArgs, and read them in backgroundWorker1_RunWorkerCompleted
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
System.Threading.Thread.Sleep(5000);
e.Result = new string[] { "one", "two" };
}
private void backgroundWorker1_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
string[] res = (string[])e.Result;
this.textBox1.Text = res[0];
}
I want to display an animated loading form while executing some code in the main form. The animated form is used only to show the user that an operation is executing and I want to close it once the operation finishes. The code that I'm using is:
public partial class Form_main_admin : Form
{
private Thread loadingThread;
private string loadingText;
public Form_main_admin()
{
InitializeComponent();
}
private void main_tabControl_SelectedIndexChanged(object sender, EventArgs e)
{
switch (main_tabControl.SelectedIndex)
{
case 0:
// ...
break;
case 1:
showLoadingForm("Loading");
// Load a datagridview (load data, adjust column widths) in Form_main_admin
closeLoadingForm();
break;
}
}
private void showLoadingForm(string text)
{
loadingText = text;
loadingThread = new Thread(new ThreadStart(openLoadingForm));
loadingThread.Start();
}
private void openLoadingForm()
{
try
{
Form_loading loadingForm = new Form_loading(loadingText);
loadingForm.ShowDialog();
}
catch
{
Thread.ResetAbort();
}
}
private void closeLoadingForm()
{
try
{
loadingThread.Abort();
}
catch
{
Thread.ResetAbort();
}
}
}
The problem is that I get a "Thread was being aborted" exception when I quickly change between tabs (see image in link below).
http://postimg.org/image/bvre2bmi5/
I do not want the user to see this exception if he chages tabs too fast. After reading other posts on this forum I realized that my implementation is not recommended. Could someone please show me how to properly implement this functionality?
If you need an animated progress form, try to use BackgroundWorker class to perform loading in an additional thread:
public partial class MainForm : Form
{
/// <summary>
/// Some progress form
/// </summary>
WaitForm waitForm = new WaitForm();
/// <summary>
/// https://msdn.microsoft.com/library/cc221403(v=vs.95).aspx
/// </summary>
BackgroundWorker worker = new BackgroundWorker();
public MainForm()
{
InitializeComponent();
worker.DoWork += (sender, args) => PerformReading();
worker.RunWorkerCompleted += (sender, args) => ReadingCompleted();
}
/// <summary>
/// This method will be executed in an additional thread
/// </summary>
void PerformReading()
{
//some long operation here
Thread.Sleep(5000);
}
/// <summary>
/// This method will be executed in a main thread after BackgroundWorker has finished
/// </summary>
void ReadingCompleted()
{
waitForm.Close();
}
private void button1_Click(object sender, EventArgs e)
{
//Run reading in an additional thread
worker.RunWorkerAsync();
//Show progress form in a main thread
waitForm.ShowDialog();
}
}
I have a number of classes that do stuff, typically step through a recordset and call a webservice or two for each record.
At the moment this all runs in the GUI thread and hangs painting. First thought was to use a BackgroundWorker and implement a nice progress bar, handle errors, completion etc. All the nice things a Background worker enables.
As soon as the code hit the screen it started to smell. I was writing a lot of the background worker into each class, repeating most of the ProcessRows method in a bw_DoWork method and thinking there should be a better way, and it's probably already been done.
Before I go ahead and reinvent the wheel is there a pattern or implementation for a class that seperates out the background worker? It would take classes that implement an interface such as ibackgroundable, but the classes could still be run standalone, and would require minimal change to implement the interface.
Edit: A simplified example requested by #Henk:
I have:
private void buttonUnlockCalls_Click(object sender, EventArgs e)
{
UnlockCalls unlockCalls = new UnlockCalls();
unlockCalls.MaxRowsToProcess = 1000;
int processedRows = unlockCalls.ProcessRows();
this.textProcessedRows.text = processedRows.ToString();
}
I think I want:
private void buttonUnlockCalls_Click(object sender, EventArgs e)
{
UnlockCalls unlockCalls = new UnlockCalls();
unlockCalls.MaxRowsToProcess = 1000;
PushToBackground pushToBackground = new PushToBackground(unlockCalls)
pushToBackground.GetReturnValue = pushToBackground_GetReturnValue;
pushToBackground.DoWork();
}
private void pushToBackground_GetReturnValue(object sender, EventArgs e)
{
int processedRows = e.processedRows;
this.textProcessedRows.text = processedRows.ToString();
}
I could go ahead and do this, but don't want to reinvent.
The answer I'm looking for would along the lines of "Yes, Joe did a good implementation of that (here)" or "That's a Proxy Widget pattern, go read about it (here)"
Each operation needs to implement the following interface:
/// <summary>
/// Allows progress to be monitored on a multi step operation
/// </summary>
interface ISteppedOperation
{
/// <summary>
/// Move to the next item to be processed.
/// </summary>
/// <returns>False if no more items</returns>
bool MoveNext();
/// <summary>
/// Processes the current item
/// </summary>
void ProcessCurrent();
int StepCount { get; }
int CurrentStep { get; }
}
This seperates the enumeration of the steps from the processing.
Here is a sample operation:
class SampleOperation : ISteppedOperation
{
private int maxSteps = 100;
//// The basic way of doing work that I want to monitor
//public void DoSteppedWork()
//{
// for (int currentStep = 0; currentStep < maxSteps; currentStep++)
// {
// System.Threading.Thread.Sleep(100);
// }
//}
// The same thing broken down to implement ISteppedOperation
private int currentStep = 0; // before the first step
public bool MoveNext()
{
if (currentStep == maxSteps)
return false;
else
{
currentStep++;
return true;
}
}
public void ProcessCurrent()
{
System.Threading.Thread.Sleep(100);
}
public int StepCount
{
get { return maxSteps; }
}
public int CurrentStep
{
get { return currentStep; }
}
// Re-implement the original method so it can still be run synchronously
public void DoSteppedWork()
{
while (MoveNext())
ProcessCurrent();
}
}
This can be called from the form like this:
private void BackgroundWorkerButton_Click(object sender, EventArgs eventArgs)
{
var operation = new SampleOperation();
BackgroundWorkerButton.Enabled = false;
BackgroundOperation(operation, (s, e) =>
{
BackgroundWorkerButton.Enabled = true;
});
}
private void BackgroundOperation(ISteppedOperation operation, RunWorkerCompletedEventHandler runWorkerCompleted)
{
var backgroundWorker = new BackgroundWorker();
backgroundWorker.RunWorkerCompleted += runWorkerCompleted;
backgroundWorker.WorkerSupportsCancellation = true;
backgroundWorker.WorkerReportsProgress = true;
backgroundWorker.DoWork += new DoWorkEventHandler((s, e) =>
{
while (operation.MoveNext())
{
operation.ProcessCurrent();
int percentProgress = (100 * operation.CurrentStep) / operation.StepCount;
backgroundWorker.ReportProgress(percentProgress);
if (backgroundWorker.CancellationPending) break;
}
});
backgroundWorker.ProgressChanged += new ProgressChangedEventHandler((s, e) =>
{
var progressChangedEventArgs = e as ProgressChangedEventArgs;
this.progressBar1.Value = progressChangedEventArgs.ProgressPercentage;
});
backgroundWorker.RunWorkerAsync();
}
I haven't done it yet but I'll be moving BackgroundOperation() into a class of its own and implementing the method to cancel the operation.
I would put my non-UI code into a new class and use a Thread (not background worker). To show progress, have the new class fire events back to the UI and use Dispatcher.Invoke to update the UI.
There is a bit of coding in this, but it is cleaner and works. And is more maintainable than using background worker (which is only really intended for small tasks).