NullReferenceException # Dispatcher & Other GUI Issues - c#

I've been trying to implement multi-class-thread GUI management. As In I want different threads spread across multiple classes in different .cs files to update the UI as needed.
I've searched stackoverflow and other sources and found that most people use Dispatcher.Invoke or something similar. So I decided to start testing...
So below is a thread in a class called wThread.cs,
public class wThread
{
public EventHandler SignalLabelUpdate;
public Dispatcher uiUpdate;
public wThread()
{
uiUpdate = Program.myForm.dispat;
//the uiUpdate seems to be null for some reason... If i am doing it wrong how do i get the dispatcher?
Thread myThread = new Thread(run);
myThread.Start();
}
Action myDelegate = new Action(updateLabel);
// is there a way i can pass a string into the above so updatelabel will work?
public void updateLabel(String text)
{
if (SignalLabelUpdate != null)
SignalLabelUpdate(this, new TextChangedEvent(text));
}
public void run()
{
while (uiUpdate == null)
Thread.Sleep(500);
for (int i = 0; i < 1000; i++)
{
//I hope that the line below would work
uiUpdate.BeginInvoke(new Action(delegate() { Program.myForm.label1.Text = "count at " + i; }));
// was also hoping i can do the below commented code
// uiUpdate.Invoke(myDelegate)
Thread.Sleep(1000);
}
}
}
Below is my form1.cs it's the pre-loaded code from visual studio 2012,
public partial class Form1 : Form
{
public Dispatcher dispat;
public Form1()
{
dispat = Dispatcher.CurrentDispatcher;
InitializeComponent();
wThread worker = new wThread();
}
}
Most of my questions are in the comments above but here are them listed:
The uiUpdate seems to be null for some reason... If i am doing it wrong how do i get the dispatcher? (wThread.cs problem)
uiUpdate = Program.myForm.dispat'
Is there a way i can pass a string into the above so updatelabel will work?
Action myDelegate = new Action(updateLabel);
I hope that the line below would work
uiUpdate.BeginInvoke(new Action(delegate() { Program.myForm.label1.Text = "count at " + i; }));
Was also hoping i can do the below commented code
uiUpdate.Invoke(myDelegate)
EDIT: I moved the wThread constructor wThread worker = new wThread() out of the form1 initialization area... and it fixed my nullpointer. Instead I move the wThread constructor into the static main void where the form is constructed... like Application.Run(myForm);
Unfortunately the wThread will not start until I close the UI.. What is the best thing to do about this? Make another thread before the Application.Run starts my Form and use that thread to start my real thread?

#1: In Form1 constructor, place a breakpoint at dispat = Dispatcher.CurrentDispatcher; and check what's the value. It may be just that the Dispatcher.CurrentDispatcher is null and you have to get it i.e. a bit later, not right in the ctor
#2 yes. Use Func<string> instead of action. The Invoke and BeginInvoke take a delegate, and also a set of params object[] that form the parameters of invocation of the delegate. Of course, the passed parameters array must exactly match the delegate signature, so when using Func<string> use a object[] with just one item: the string.
#3 - yes, that would work, as long as the i is a simple local variable thatcan be captured by the delegate closure. If it's foreach iterator or some nonlocal member, it can have problems, but, well, different story.
#4 - yes it would work, just remember about passing the params for Func<string> too.

Related

How to use delegate?

I use delegate in different threads.
This is work:
delegate void AppendTextDelegate(string text);
public void appendTextData(string text)
{
if (richBox1.InvokeRequired)
{
richBox1.Invoke(new AppendTextDelegate(appendTextData), new object[] { text });
}
else
{
richBox1.AppendText(text);
}
}
This is work too:
delegate void ChangeValDelegate(int value);
progressBar1.Invoke(new ChangeValDelegate((s) => progressBar1.Value = s), 0);
This is not work:
delegate void ClearDelegate();
listView.Invoke(new ClearDelegate()); <-- "Does not contain a constructor that takes 0 arguments"
I think you are creating lots of delegates to work with your controls. Also,
you are using each control (listview, richtextbox...) when you can use only one of them, for example, your form.
The problem here is that you must work with your controls in the same thread in which they was created. You use InvokeRequired to check this and run the code in other thread (main thread) when you are running code in a different thread.
Usually, all your controls and forms are created in the main thread, so you can check only with one of your controls to know when it's safe to access to the controls.
Here you have a way to do the same in a more compact and simple way. First, add this field to your form:
private static SynchronizationContext Context;
And set it's value in your form contructor:
Context = SynchronizationContext.Current;
SynchronizationContext.Current it's different in differents contexts but in the form contructor you know that the context it's the Form context (your main thread). So, we save in Context the context in which we can access to the form (and all it's controls).
Add this method to simplify the work with controls:
private static void RunInMainThread(Action operation)
{
if (Context != SynchronizationContext.Current)
{
Context.Post(o =>
{
try
{
operation();
}
catch (ObjectDisposedException)
{
}
}, null);
}
else
{
operation();
}
}
It's similar to InvokeRequired but with SynchronizationContext. If you are in the right place (same context of your form), simply run the action. In other case, run the action in the form context. Here you can see that it's done using Post. In this way, you avoid deadlocks and accept that the code may run with a bit delay (because is enqueued).
And why an Action? because you can run anything using the action, leaving the parameters outside of the method invocation. You don't need lots of methods with different parameters to control any access to your controls.
For example:
public void appendTextData(string text)
{
RunInMainThread(() =>
{
richBox1.AppendText(text);
});
// Beeing a single line, you can compact the code
//RunInMainThread(() => richBox1.AppendText(text));
}
You run your AppendText with the text parameter, but you don't need this parameter in your RunInMainThread method.
Another example, to set progress bar to start and clear the listview:
RunInMainThread(() =>
{
progressBar1.Value = 0;
listview.Clear();
});
And about the delegates: they are like a contract, like an interface. A delegate hasn't code, only tell you about the arguments... but without code.
In your code:
progressBar1.Invoke(
new ChangeValDelegate(
(s) => progressBar1.Value = s), 0);
You are running an implementation that match with your delegate (return void and get an integer as a parameter).
Is like:
var d = new ChangeValDelegate(OnChangeValDelegate);
BeginInvoke(d, new object[] { 0 });
Having:
public void OnChangeValDelegate(int value)
{
progressBar1.Value = value;
}
As you see, you need an implementation. Here:
listView.Invoke(
new ClearDelegate(/* NO IMPLEMENTATION */));
You need the code (matching your delegate) that clear the listview.

Form Show not working with threads

I'm creating an application where it starts a FormSelect() asking the user to select a item in the list, when user press the "Select" button it will create a new instance of MainForm(), hide the FormSelect and show the MainForm, but it's not working, I'm getting an exception with all tests I made.
Here are some codes:
static class Program {
public static MainForm mainForm;
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new FormSelect());
}
}
Nothing special on my FormSelect, so here is the button click where it start the MainForm:
private async void btnSelect_Click(object sender, EventArgs e) {
loadingSpiner.Visible = true;
btnSelect.Enabled = false;
int index = listProcess.SelectedIndex;
await startMainForm(index);
this.Hide();
Program.mainForm.Show();
}
private async Task startMainForm(int index) {
await Task.Run(() => {
Program.mainForm = new MainForm(runningProcess[index]);
});
}
As you can see above, I'm using a Task to start the MainForm so it does not freeze my UI and my "loading spiner"
But when it try to use the .Show() I get a cross-thread exception, so I tried to Invoke that action using:
this.Invoke(new MethodInvoker(delegate () { Program.mainForm.Show(); }));
But using the method above I get an exception saying:
An exception of type 'System.ComponentModel.Win32Exception' occurred in System.Windows.Forms.dll but was not handled in user code
Additional information: Error creating window handle.
If I remove the "await Task.Run()..." in the startMainForm method everything works fine, but it blocks totally the FormSelect UI.
How can I avoid this problem? Thank you!
You can't do it that way.
Without a good Minimal, Complete, and Verifiable code example that reliably reproduces the problem, it's impossible to say for sure what the best solution for your scenario is. But UI objects must be created and used only in the UI thread. You can't create your MainForm instance in a worker thread.
Because your code example is incomplete, it's not clear why you are trying to create the MainForm instance using Task.Run() in the first place. All that code does is call a constructor, and constructors should not be time-consuming operations anyway.
If you have some time-consuming initialization to perform, you should abstract that out of the MainForm class, putting it into some other class that can be passed to MainForm once it's been fully initialized. Then you can initialize that other class with e.g. a call to Task.Run() (but still not in a constructor…create the object and then if you need to asynchronously call a method that initializes it, do that).
E.g.:
private async void btnSelect_Click(object sender, EventArgs e) {
loadingSpiner.Visible = true;
btnSelect.Enabled = false;
int index = listProcess.SelectedIndex;
MainFormData data = await startMainForm(index);
Program.mainForm = new MainForm(data);
this.Hide();
Program.mainForm.Show();
}
private async Task<MainFormData> startMainForm(int index) {
await Task.Run(() => {
MainFormData data = new MainFormData();
data.Initialize(runningProcess[index]);
return data;
});
}
class MainFormData
{
public MainFormData() { ... }
// You don't say what "runningProcess[index]" is, so placeholder here...
public void Initialize(object o) { ... }
}
Something like that. Adjust to suit your needs, of course.
Of course, that other class must initialize only non-UI aspects. I.e. the data underlying your UI. Since UI objects generally aren't time-consuming to create, I assume that's feasible in your case. If you feel it's the initialization of the UI itself that is time-consuming, then you should be asking for help figuring out what that is the case and fixing that problem. Of course, again without a good MCVE here, there's nothing I can comment on specifically with respect to that.

Progress declaration at the Form level gives an exception

In a WPF window, started within a Console application, I tried this example for the use of IProgress in async-await:
namespace Test
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Main : Window
{
// class level declaration
Progress<string> Progress1;
public Main()
{
InitializeComponent();
// initialization of class level field
Progress1 = new Progress<string>(value =>
{
Print(value);
});
}
private async void Button_Click(object sender, RoutedEventArgs e)
{
Print("Start test");
// local variable
Progress<string> Progress2 = new Progress<string>(value =>
{
Print(value);
});
// interface (normally Progress1/2 would be passed as a parameter)
// Progress2 works fine, Progress1 gives an exception
var progress = Progress1 as IProgress<string>;
await Task.Run(() =>
{
for (int i = 0; i != 5; ++i)
{
if (progress != null) progress.Report("\nStage " + i);
Thread.Sleep(1000);
}
});
Print("\nCompleted.");
}
void Print(string text)
{
//Output is a WPF TextBlock
Output.Inlines.Add(new Run(text) { Foreground = Brushes.Blue, FontWeight = FontWeights.Normal });
}
}
The local variable Progress2 works fine: the callback is in the UI thread.
The class level field Progress1 gives an exception. This callback is clearly in the background thread.
The calling thread cannot access this object because a different
thread owns it.
It probably has to do with the way anonymous functions and callbacks work.
Can somebody explain the problem?
EDIT
The problem is not reproduciblein a normal WPF solution.
It is reproducible in a Console application where the WPF windows is started as in:
Application app = new Application ();
app.Run(new Main());
In that case Synchronization.Current == null in the constructor and it is != null in e.g. the Loaded event. As explained in the answers and comments.
Yes, buy Microsoft a cigar for building this diagnostic into Winforms because this would otherwise a completely undebuggable problem, one that crashes your code only occasionally.
This is an initialization order problem, it is caused by your Progress variable being initialized too soon. Keep in mind what the Progress<> class does, it knows how to run the target of the delegate you pass on the "right thread". Specially, you want it to run on your UI thread so you can safely update the label. Progress<> does so by making a copy of SynchronizationContext.Current, using it later to run the delegate target with its Post() method.
Problem is, the Current property does not have a value yet. That happens a few microseconds later, when the Form base class constructor runs. Progress<> has copied a null, the only thing it can do now is to run the Post() method target on a thread pool thread. Kaboom!
The fix is simple:
Progress<string> Progress;
public Form1() {
InitializeComponent();
Progress = new Progress<string>(value => {
label.Text = value;
});
}
Now it is initialized later, Synchronization.Current has a value.

Cross-thread operation not valid: Control 'Form1' accessed from a thread other than the thread it was created on

Im pretty new to C# but I have been playing around with it to learn. So far I have a nice app that does things like starts up the screen saver and controls the windows system volume.
However I'm having some trouble and I'm not sure what is wrong. I have already gone through a bunch of similar questions on the site but I don't think any of them apply to my specific case.
So I wanted to control the app from the web. My plan is to have the app check a webpage on my site for a command every couple seconds. Depending on what the site returns the app will do different things(mute, vol up, etc.) and reset the command on the website to blank. The website command part is all done in PHP and is quite simple, there is no problem with that part of it.
I created a button that calls a function that checks the page and performs the action. It worked fine. But when I tried to make it automatically check the site I'm getting errors. I'm pretty sure it is because I moved the check and perform action to a new thread. So lets move on to the code. These are not the full files but what I think you need to know to help. If you need anything more just let me know.
Form1.cs
public Form1(Boolean init = true)
{
if (init)
{
InitializeComponent(); //initialize UI
startWebMonitor(); //starts web monitor thread for remote web commands
}
}
private void startWebMonitor()
{
Thread t = new Thread(WebMonitor.doWork);
t.Start();
}
public IntPtr getWindowHandle()
{
return this.Handle;
}
WebMonitor.cs
public static void doWork()
{
while(true)
{
checkForUpdate();
Thread.Sleep(1000);
}
}
private static void checkForUpdate()
{
lastCommand = getLastCommand();
if (lastCommand.Equals(""))
{
//No Update
}
else
{
processCommand(lastCommand);
}
}
public static void processCommand(String command)
{
if(command.Equals("mute"))
{
VolumeCtrl.mute(Program.form1.getWindowHandle());
}
HTTPGet req2 = new HTTPGet();
req2.Request("http://mywebsite.com/commands.php?do=clearcommand");
}
VolumeCtrl.cs
private static IntPtr getWindowHandle()
{
Form1 form1 = new Form1(false); //false = do not initialize UI again
return form1.getWindowHandle();
}
public static void mute(IntPtr handle)
{
SendMessageW(getWindowHandle(), WM_APPCOMMAND, getWindowHandle(), (IntPtr)APPCOMMAND_VOLUME_MUTE);
}
Alright so basically the mute function requires the window handle in order to work. But when I try to get the window handle from the new thread it throws the error:
Cross-thread operation not valid: Control 'Form1' accessed from a
thread other than the thread it was created on.
So how do I get around this? I have read other answers on here saying you need to use Invoke or Delegate but I'm not sure how those work or where to use them. I tried to follow one of the answers but I got even more errors.
In this case, write invoke operation inside of method. Then you can access both from control's thread and other threads.
delegate IntPtr GetWindowHandleDelegate();
private IntPtr GetWindowHandle() {
if (this.InvokeRequired) {
return (IntPtr)this.Invoke((GetWindowHandleDelegate)delegate() {
return GetWindowHandle();
});
}
return this.Handle;
}
or, if you want to avoid delegate hell, you could write in more few code with built-in delegate and lambda.
private IntPtr GetWindowHandle() {
if (this.InvokeRequired)
return (IntPtr)this.Invoke((Func<IntPtr>)(GetWindowHandle));
return this.Handle;
}
Try this code it will surely help you
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += (s, e) => { };
bw.RunWorkerCompleted += (s, e) =>
{
//Call you windowsform method or anything which you want to do
};
bw.RunWorkerAsync();

Multithreading Calling a Delegate

To quote Marc Gravell:
///...blah blah updating files
string newText = "abc"; // running on worker thread
this.Invoke((MethodInvoker)delegate {
someLabel.Text = newText; // runs on UI thread
});
///...blah blah more updating files
I'm looking to do this with WPF so can't use the invoke method. Any thoughts? This Threading stuff is doing my head in :/
MORE DETAIL
I began my new Thread like so
Thread t = new Thread (LoopThread);
t.Start();
t.Join();
But throughout LoopThread, I want to write to the UI.
UPDATE
Thanks to Jon Skeet for the Dispatcher.Invoke bit. Seems MethodInvoker is WinForms also. WPF equivalent?
UPDATE 2
Thanks Adriano for suggesting instead of System.Windows.Forms.MethodInvoker, using System.Action.
(You guys were right about the this parameter confusion, just need to build to remove errors.)
Bus since adding the SimpleInvoke, now I'm hit with
Extension method must be defined in a non-generic static class
on the line
public partial class MainWindow : Window
Any thoughts?
In WPF, you just use Dispatcher.Invoke instead of Control.Invoke.
The DispatcherObject class (which WPF classes derive from) exposes a Dispatcher property, so you just need:
Dispatcher.Invoke((Action) delegate {
someLabel.Text = newText; // runs on UI thread
});
If you're using C# 3 or higher (and .NET 3.5 or higher) you might want to add an extension method to DispatcherObject:
// Make this a new top-level class
public static class DispatcherObjectExtensions
{
public static void SimpleInvoke(this DispatcherObject dispatcherObject,
Action action)
{
dispatcherObject.Dispatcher.Invoke(action);
}
}
So you can just use:
// From within your UI code
this.SimpleInvoke(() => someLabel.Text = newText);
as compromise using SynchronizationContext:
// gui thread
var sc = SynchronizationContext.Current;
// work thread
sc.Post(s =>
{
someLabel.Text = newText
}, null);
Continuing from Jon Skeet comment, and you can call your extension like below
DispatcherObject.SimpleInvoke(() => someLabel.Text = newText);

Categories

Resources