I just answered a question about whether a Task can update the UI. As I played with my code, I realized I'm not clear myself on a few things.
If I have a windows form with one control txtHello on it, I'm able to update the UI from a Task, it seems, if I immediately do it on Task.Run:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Task.Run(() =>
{
txtHello.Text = "Hello";
});
}
}
However if I Thread.Sleep for even 5 milliseconds, the expected CrossThread error is thrown:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Task.Run(() =>
{
Thread.Sleep(5);
txtHello.Text = "Hello"; //kaboom
});
}
}
I'm not sure why that happens. Is there some sort of optimization for an extremely short running Task?
You didn't post the exception stack trace, but I expect that it looked something like this:
System.InvalidOperationException: Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.
at System.Windows.Forms.Control.get_Handle()
at System.Windows.Forms.Control.set_WindowText(String value)
at System.Windows.Forms.TextBoxBase.set_WindowText(String value)
at System.Windows.Forms.Control.set_Text(String value)
at System.Windows.Forms.TextBoxBase.set_Text(String value)
at System.Windows.Forms.TextBox.set_Text(String value)
at WindowsFormsApplicationcSharp2015.Form1.<.ctor>b__0_0() in D:\test\WindowsFormsApplicationcSharp2015\Form1.cs:line 27
We can see that the exception is thrown from the Control.Handle getter property. And in fact, if we look at the source code for that property, there it is, as expected:
public IntPtr Handle {
get {
if (checkForIllegalCrossThreadCalls &&
!inCrossThreadSafeCall &&
InvokeRequired) {
throw new InvalidOperationException(SR.GetString(SR.IllegalCrossThreadCall,
Name));
}
if (!IsHandleCreated)
{
CreateHandle();
}
return HandleInternal;
}
}
The interesting part is when we look at the code that calls Control.Handle. In this case, that's the Control.WindowText setter property:
set {
if (value == null) value = "";
if (!WindowText.Equals(value)) {
if (IsHandleCreated) {
UnsafeNativeMethods.SetWindowText(new HandleRef(window, Handle), value);
}
else {
if (value.Length == 0) {
text = null;
}
else {
text = value;
}
}
}
}
Notice that the Handle property is only invoked if IsHandleCreated is true.
And for completeness, if we look at the code for IsHandleCreated we see the following:
public bool IsHandleCreated {
get { return window.Handle != IntPtr.Zero; }
}
So, the reason you don't get the exception, is because by the time the Task executes, the window handle hasn't been created yet, which is to be expected since the Task starts in the form's constructor, that is, before the form is even displayed.
Before the window handle is created, modifying a property doesn't yet require any work from the UI thread. So during this small time window at the start of your program, it would seem that it is possible to invoke the methods on control instances from a non-UI thread without getting the "cross thread" exception. But clearly, the existence of this special small time window doesn't change the fact that we should always make sure to invoke control methods from the UI thread to be safe.
To prove the point that the timing of the window handle creation is the determining factor in getting (or not) the "cross thread" exception, try modifying your example to force the creation of the window handle before you start the task, and notice how you will now consistently get the expected exception, even without a sleep:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Force creation of window handle
var dummy = txtHello.Handle;
Task.Run(() =>
{
txtHello.Text = "Hello"; // kaboom
});
}
}
Relevant documentation: Control.Handle
If the handle has not yet been created, referencing this property will force the handle to be created.
Related
I think I'm missing something here. I've got a WPF form that has some methods on it I need to call from an external source (usually on a non-UI thread). I retrieve a reference to the form, then attempt to call the method via Dispatcher.Invoke so it's marshalled to the UI thread. The problem is that this code won't work as the Invoke fires an Action, so the result is always an empty string (even though the docs say Invoke is supposed to be synchronous).
public string GetValueById(string id, string value)
{
Application.Current.Dispatcher.Invoke(() =>
{
var main = Application.Current.MainWindow as MainWindow;
if (main != null)
{
return main.GetValue(id);
}
});
return "";
}
I can't quite wrap my head around how to make this work.
If you look at the documentation for that Dispatcher.Invoke overload, you'll see that if you pass it a Func<TResult> callback then it will return the TResult returned by executing that callback. All you have to do is actually make use the return value:
public string GetValueById(string id, string value)
{
return Application.Current.Dispatcher.Invoke(() =>
{
var main = Application.Current.MainWindow as MainWindow;
if (main != null)
{
return main.GetValue(id);
}
});
}
I'm working on a app, where I'm adding items to a listview from a RunWorkerCompleted method using BackgroundWorker. From the RunWorkerCompleted method I'm adding ListViewItems, where I'm setting .Content and .Background.
However when setting the .Background property and calling the .UpdateLayout() method of the ListView class, I'm getting an exception: "Cannot use a DependencyObject that belongs to a different thread than its parent Freezable.".
private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
try
{
if (e.Cancelled == true)
{
System.Windows.MessageBox.Show("Something..cancelled");
}
else if (e.Error != null)
{
System.Windows.MessageBox.Show("Something..error " + e.Error.Message);
}
else
{
workLoad_listView.Items.Clear();
workLoad_listView.Height = 23;
foreach (Workload.workload element in Workload.Get())
{
System.Windows.Controls.ListViewItem item = new System.Windows.Controls.ListViewItem();
item.Content = (string)element.name;
item.Background = element.brush; // if I outcomment this line, no exception is thrown!
workLoad_listView.Items.Add(item);
}
workLoad_listView.UpdateLayout(); //exception is thrown here!
while (FindVisualChild<ScrollViewer>(workLoad_listView).ComputedVerticalScrollBarVisibility == Visibility.Visible)
{
workLoad_listView.Height += 1;
workLoad_listView.UpdateLayout();
}
}
}
catch (Exception ex)
{
System.Windows.MessageBox.Show(string.Format("An exception was thrown!\n{0}", ex), "Exception caught", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
This is the Workload class:
public static class Workload
{
public struct workload
{
public string name;
public System.Windows.Media.SolidColorBrush brush;
}
private static List<workload> workload_list = new List<workload>();
public static void Add(string name, int colorNumber)
{
workload tmp_workload = new workload();
tmp_workload.name = name;
System.Drawing.Color color = System.Drawing.ColorTranslator.FromWin32(colorNumber);
tmp_workload.brush = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Color.FromArgb(255, (byte)color.R, (byte)color.G, (byte)color.B));
workload_list.Add(tmp_workload);
}
public static void Clear()
{
workload_list.Clear();
}
public static List<workload> Get()
{
return workload_list;
}
}
Any suggestions would be appreciated :)
Best regards.
The System.Windows.Media.SolidColorBrush type is derived from DependencyObject. The DependencyObject type has thread-affinity; that is, it may be used only in the thread where it was created.
You haven't shown a complete code example; in particular, it's not clear from the example where your Workload.Add() method is being called. But given the error message, it seems extremely likely you are calling this from the BackgroundWorker's DoWork event handler. Of course, this handler is operating in a different thread from the UI thread where the RunWorkerCompleted event handler is executed, and so the brush object is being created in a thread different from the one where you want to actually use it.
Basically: this is prohibited by the framework.
Again, without a complete code example, it's hard to know for sure what the best solution would be. However, based on the code shown here, it looks to me as though you should store the System.Windows.Media.Color value for the brush in your workload struct instead of a Brush value, and then create the Brush itself in the RunWorkerCompleted event handler.
I would also suggest changing the type names so that you don't have both a Workload class and a workload struct. Having two type names which are different only in the case of the first letter is potentially very confusing to others who may want to read the code (or to yourself, months later after you've forgotten how you implemented it).
Use case
I'm developing a small application in C# that is called by another application to retrieve data from the Internet. It runs as a process on its own, but almost all of the interaction with it, is managed by the calling application. Therefor it does not have a GUI. However I'd like to add a progress bar using WPF that is shown during certain data retrievals that could take up to a minute. It's fairly easy to make an estimate of how much work is done and how much is left and therefor I find a progress bar suitable.
Research done
I have a fair understanding of threading after reading large parts of Albahari's pdf on threading (http://www.albahari.info/threading/threading.pdf). I have also read through a lot of posts on SO and MSDN in this matter. Most posts suggest the use of a background worker for the time consuming data retrieval while keeping the GUI in the main thread and therefor suggest solutions using a background worker. That feels awkward in this scenario though, where the main task is data retrieval and not GUI interaction.
I've spend a bunch of hours trying to make sense of different tutorials and forum posts while trying to conform them to my problem, but I have not succeeded and now I'm pretty much back to square one. Basically I'd like to end up with the following two classes outlined below:
ProgressBarWindow
public partial class ProgressBarWindow : Window
{
public ProgressBarWindow()
{
InitializeComponent();
}
public void setValue(int value)
{
// This function should be available from the main thread
}
}
Querier
Public class Querier
{
public List<Item> getItems()
{
// call ProgressBarWindow.setValue(0);
...
// call ProgressBarWindow.setValue(100);
// call ProgressBarWindow.Close();
}
}
It's my understanding that UI must run under single threads and therefor my ProgressBarWindow object could not be instantiated in a new thread while at the same time be available to the main thread (kind of).
Dispatcher.BeginInvoke appears to be my savior here but so far I haven't been able to figure out what should go into the Querier class and what to go in the ProgressBarWindow class. How can I make the two threads interact with the same instance of ProgressBarWindow?
Please ask if you need more details and I will try to clarify.
You can use the Progress class to update the UI with the current progress of a long running operation.
First create an instance of Progress in your UI:
Progress<int> progress = new Progress<int>(currentProgress =>
{
progressBar.Value = currentProgress;
//todo do other stuff
});
Then pass it to the long running process:
public List<Item> getItems(IProgress<int> progress)
{
progress.Report(0);
//todo do something
progress.Report(100);
}
Here is a generic function which i generally use:
public static void Invoke(this UIElement element,Action action)
{
element.Dispatcher.Invoke(action, null);
}
And to use it, simply call:
this.Invoke(() => ProgressBarWindow.SetValue(0));
So, in the getItems() function, you would have something along the lines of:
public List<Item> getItems()
{
ProgressBarWindow wnd;
MainWindow.Invoke(() => wnd = new ProgressBarWindow())
MainWindow.Invoke(() => wnd.SetValue(0))
...
MainWindow.Invoke(() => wnd.SetValue(100))
MainWindow.Invoke(() => wnd.Close())
}
Make sure you always have a way to get to the main window is anything (the one running from either App.xml, or App.Run(...). You can then issue any GUI actions through it (even if you have to create a new Loader window for example, as long as it's done within the main thread)
App.xaml
public partial class App : Application
{
private void Application_Startup_1(object sender, StartupEventArgs e)
{
Task.Factory.StartNew<List<int>>(() => Querier.GetItems());
}
}
ProgressBarWindow.xaml.cs
public partial class ProgressWindow : Window
{
public ProgressWindow()
{
InitializeComponent();
Querier.Start +=()=> Visibility = Visibility.Visible;
Querier.Stop += () => Visibility = Visibility.Collapsed;
Querier.ReportProgress +=OnReportProgress;
}
public void OnReportProgress(int value)
{
txtBox.Text = value.ToString();
}
}
ProgressBarWindow.xaml
<Grid>
<TextBox x:Name="txtBox"></TextBox>
</Grid>
Querier
public class Querier
{
public static event Action Start;
public static event Action Stop;
public static event Action<int> ReportProgress;
public static List<int> GetItems()
{
if (Start != null)
App.Current.Dispatcher.BeginInvoke(Start,null);
for (int index = 0; index <= 10; index++)
{
Thread.Sleep(200);
if (ReportProgress != null)
App.Current.Dispatcher.BeginInvoke(ReportProgress, index*10);
}
if (Stop != null)
App.Current.Dispatcher.BeginInvoke(Stop, null);
return Enumerable.Range(1, 100).ToList();
}
}
I am just trying to give an idea hope this will help.
How do I compare SynchronizationContext? It seems that the same Dispatcher can create different SynchronizationContext when using BeginInvoke. When I drill down into the two (unequal) contexts, I see that the dispatcher Thread ID is the same, yet they are not Equal to each other.
public partial class MainWindow : Window
{
private SynchronizationContext contexta;
private SynchronizationContext contextb;
private SynchronizationContext contextc;
private SynchronizationContext contextd;
public MainWindow()
{
InitializeComponent();
contexta = SynchronizationContext.Current;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
contextb = SynchronizationContext.Current;
Dispatcher.Invoke(() =>
{
contextc = SynchronizationContext.Current;
});
Dispatcher.BeginInvoke(new Action(() =>
{
contextd = SynchronizationContext.Current;
}));
Debug.Assert(contexta != contextb);
Debug.Assert(contexta == contextc); // fails... why?!?!?
Debug.Assert(contexta == contextd); // fails... why?!?!?
Debug.Assert(contextc == contextd); // fails... why?!?!?
}
Maybe the two of them cannot be used together. I noticed that this actually works:
contexta.Send(new SendOrPostCallback((s) =>
{
contexte = SynchronizationContext.Current;
}), null);
Update But strangely, it doesn't always work.
public override void AddRange(IEnumerable<T> items)
{
if (SynchronizationContext.Current == _context)
{
base.AddRange(items);
}
else
{
_context.Send(new SendOrPostCallback((state) =>
{
AddRange(state as IEnumerable<T>);
}), items);
}
}
never gets a matched _context and goes on forever, for example. Even though it shouldn't. This latter example the threads actually end up being the same, and there is a context, but it is different.
Update2 Ok, I got it to work, but I really feel uncomfortable about it. Apparently, when you Post or Send, your task is run from the right thread, but if you aren't coming from the UI, it seems that a new SynchronizationContext is generated.
public override void AddRange(IEnumerable<T> items)
{
if (SynchronizationContext.Current == _context)
{
base.AddRange(items);
}
else
{
_context.Post(new SendOrPostCallback((state) =>
{
if (SynchronizationContext.Current != _context)
SynchronizationContext.SetSynchronizationContext(_context); // called every time.. strange
AddRange(items);
}), null);
}
}
And look at this:
"Requires full trust for the immediate caller. This member cannot be used by partially trusted or transparent code." :(
I think you are interested in
BaseCompatibilityPreferences.ReuseDispatcherSynchronizationContextInstance.
This setting dictates whether a single SynchronizationContext instance is used for a given Dispatcher object or not. It is true by default until .net 4, and false in .net 4.5 (this is the behavior change that LukeN observes).
Now if your goal is just to make a direct call rather than calling .Send() I'd say:
when calling .Send(), the DispatcherSynchronizationContext actually just makes a direct call if you are on the correct thread (doesn't use the dispatcher Queue) so you're not gaining much anyway (a few checks and calls from the extra layers of indirection).
if you only code against WPF, you can use Dispatcher.CheckAccess() and Dispatcher.Invoke() to do what you want.
In the general case, there is no way to "compare" two SynchronizationContexts, so you should just call .Send(). It's not likely to be a performance issue anyway, remember that premature optimization is the root of all evil -> measure first.
I have some code that takes a form object (winforms) and calls form.Show() on a dedicated thread (it spins a new thread).
This worked fine in Visual Studio 2008 (framework 3.5). I now migrated to 2010 and it fails with
InvalodOperationException: "Cross-thread operation not valid: Control '' accessed from a thread other than the thread it was created on."
(of course half our files are checked out for the migration and the whole team is waiting...)
Here is some code (this is the running code so fogive me for some redundant level of detail):
private void ShowForm(object container)
{
FormContainer formContainer = (FormContainer)container;
Process sboProcess = GetSboProcess();
if (sboProcess != null)
{
WindowWrapper MyWindow = new WindowWrapper(sboProcess.MainWindowHandle);
if (formContainer.ShowDialog)
{
formContainer.Form.ShowDialog(MyWindow);
}
else
{
//formContainer.Form.Invoke((MethodInvoker)delegate() { formContainer.Form.Show(MyWindow); });
formContainer.Form.Show(MyWindow);
//Run thread while form is opened:
System.Windows.Forms.Application.Run(formContainer.Form);
}
}
}
public class FormContainer
{
private readonly Form form;
private readonly bool showDialog;
public FormContainer(Form form, bool showDialog)
{
this.form = form;
this.showDialog = showDialog;
}
public bool ShowDialog
{
get { return showDialog; }
}
public Form Form
{
get { return form; }
}
}
public class WindowWrapper : IWin32Window
{
private readonly IntPtr handle;
public WindowWrapper(IntPtr handle)
{
this.handle = handle;
}
#region Implementation of IWin32Window
/// <summary>
/// Gets the handle to the window represented by the implementer.
/// </summary>
/// <returns>
/// A handle to the window represented by the implementer.
/// </returns>
/// <filterpriority>1</filterpriority>
public IntPtr Handle
{
get { return handle; }
}
#endregion
}
Anyone with an idea?
Thanks,
Asher
In a sentence; the dedicated thread must call back to the UI thread that owns the form. Cross-thread UI operations are illegal, because there are certain operations that could cause the UI control involved to become detached from the Windows message pump. This is a bad thing because then Windows cannot tell the window to do anything, including draw itself, move, or even close. The window becomes "rogue", and all Windows can do is terminate the entire process that spawned the window.
The invocation of the form method can be a synchronous or asynchronous operation, but either way it has to be queued up to be run by the UI thread.
Here's a way to make calls from background threads SEEM like they're executed by the background thread, without violating cross-threading rules:
//in your form class
public new void Show()
{
if(!InvokeRequired)
base.Show();
else
this.Invoke((MethodInvoker)(()=>base.Show()));
}
Understand that you cannot deal with the form as its base class System.Windows.Form; it has to be the concrete form class you create. Otherwise the method hiding is ignored and the base implementation is used by default.
By setting it up this way, whenever an action might be about to run from a background thread, you avoid cross-threading operations. You can change this method to run asynchronously if desired by calling BeginInvoke() on the control instead of Invoke(), but as a call to Show() would normally run synchronously, I would stick with Invoke.