I try to export in Png a WPF UserControl that I made in a background Thread.
The rendering works great in the UI thread, but I can't run my spinner for user waiting correctly because the two controls run in the UI thread.
When I run my code in the backgroung thread I have the following exception :
System.Windows.Markup.XamlParseException: 'The calling thread cannot access this object because a different thread owns it'
I use the following code to create the thread :
List<Byte[]> images = null;
Thread thread = new Thread(() =>
{
HorizontalViewDesigner horizontalViewDesigner = new HorizontalViewDesigner(true);
horizontalViewDesigner.ItemsSource = new ObservableCollection<ICanvasSelection>(elements);
horizontalViewDesigner.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
horizontalViewDesigner.Arrange(new Rect(new Size(10_000, 10_000)));
Size size = new Size(1024d, 1024d);
images = horizontalViewDesigner.GetImages(size);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
thread = null;
return (images);
The exception occurs in the GetImages method on the call of
userControl.UpdateLayout();
It's seems that this method update Dependency Properties from binding.
Any solution ?
Thanks
Related
I'm beginner in c#,and i write this code for start the new thread:
Thread workerThread = new Thread(DoWork);
workerThread.Priority = ThreadPriority.Highest;
workerThread.Start();
in the up thread process some thing and show into the chart,every thing is okay,but when run and finish DoWork method,chart control visible set to false automatically!,my DoWork method is:
public void DoWork()
{
//.....some process and show into the process result into the chart
chart1.Visible = true;//this code not run
}
how can solve that?
You do not have access to UI elements from a different thread.
For Winforms:
How to update the GUI from another thread in C#?
For WPF:
Change WPF controls from a non-main thread using Dispatcher.Invoke
chart1.Dispatcher.Invoke(() =>chart1.Visible = true);
Change your Dowork method signature to accept object as parameter and pass Synchronization context to it:
void DoWork(object o)
{
SynchronizationContext cont = o as SynchronizationContext;
// your logic gere
cont.Post(delegate
{
// all your UI updates here
}, null);
}
Thread workerThread = new Thread(DoWork);
workerThread.Priority = ThreadPriority.Highest;
workerThread.Start(SynchronizationContext.Current);
I want to show progress bar while my application create complex screen. my code is:
protected override void LoadSubject(object sender)
{
var win = new Spinner();
win.Show();
Thread th = new Thread(() =>
{
LoadSubjectImpl(sender);
win.Dispatcher.BeginInvoke(new Action(() => win.Close()));
});
th.Start();
}
private void LoadSubjectImpl(object sender)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
StartServiceWorkflow(sender);
})
);
}
its works fine, but the progress bar freeze...
I must use the dispatcher in background thread because of "InvalidOperationException" and I think that the problem, but what can I do?
spinner = costum progressbar.
You will have no luck trying to achieve what you have described. If you'd read closely that InvalidOperationException you would know that you cannot manipulate UI from background thread. What you have done using Dispatcher is synchronizing StartServiceWorkflow to UI thread. So your code is executing in it - that causes the freeze.
To achieve some user experience enhancement you can delegate to background thread tasks like reading form database or processing data that is to be displayed.
I am trying to create dynamically custom userControl in background thread.
This is my method where I am creating new thread:
var thread = new Thread(CreateItemInBackgroundThread);
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
thread.Join();
And this is method CreateItemInBackgroundThread:
var uc = new MyUserControl();
UserControl item = uc;
AllControls.Add(item);
//Here I am trying to add control to a current Tab
foreach (var currentTab in _allTabs)
{
currentTab.DocumentWindow.Dispatcher.BeginInvoke(new Action(() =>
{
if (tab.DocumentWindow.IsSelected)
{
tempTab = tab;
tempControl = item;
finish = true;
}
}));
}
This is my finish property
bool finish
{
get { return _finish; }
set
{
_finish = value;
if (_finish)
{
tempTab.AnimatedCanvas.Dispatcher.BeginInvoke(new Action(() => tempTab.AnimatedCanvas.Children.Add(tempControl)));
}
} // Here I get error - The calling thread cannot access this object because a different thread owns it
}
How can I avoid this error and why this error happend?
as the error says, you can't access the this object because a different thread owns it, so you can invoke that thread using Invoke(Delegate Method)
you can check if invoke required using tempTab.InvokeRequired
This error is coming because u must be doing the different tasks on the same thread like U cannot make a thread go async and update the UI using the same thread.It will cause conflict.because UI thread is the main thread.
You can use BAckground Worker Thread and subsribe its two eventHandlers to your events which you want to work on.. for eg-
BackgroundWorker Worker=new BackgroundWorker();
worker.DoWork+=Yorevent which will do the timeTaking Task();
Worker.RunWorkerCompleted+=YOurEvent which will Update your UI after the work is done();
worker.RunWorkerAsync();
RunWorkerAsync() will make your thread go async and work on background
this way it will not cause any thread Error too..
The whole point of the backgroundWorker is to update the UI after a time-consuming task. The component works as advertised in my WPF app.
However in my test, the callback is not invoked on the calling thread.
[Test]
public void TestCallbackIsInvokedOnClientThread()
{
var clientId = Thread.CurrentThread.ManagedThreadId;
int callbackThreadId = -1;
var manualEvent = new ManualResetEventSlim(false);
var someUIControl = new TextBox();
var bw = new BackgroundWorker();
bw.DoWork += (s,e) => e.Result = 5 ; // worker thread
bw.RunWorkerCompleted += (s, e) =>
{
try
{
callbackThreadId = Thread.CurrentThread.ManagedThreadId;
//someUIControl.Text = callbackThreadId.ToString();
manualEvent.Set();
}
catch (System.Exception ex)
{
Console.Out.WriteLine(ex.ToString());
}
};
bw.RunWorkerAsync();
if (!manualEvent.Wait(5000))
Assert.Fail("no callback");
Assert.AreEqual(clientId, callbackThreadId);
}
Result Message: Assert.AreEqual failed. Expected:<15>. Actual:<10>. callback not invoked on client Thread
What am I missing ?
In the Unit Test I see behavior like
------ Run test started ------
MainThread Id =21
Worker Thread Id =9
Callback Thread Id =9
In the Wpf App, this would be
MainThread Id =1
Worker Thread Id =14
Callback Thread Id =1
Update:
With Justin's answer, made the following changes and now the test passes
Before creating the BackgroundWorker
SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext(control.Dispatcher));
Instead of using a event for signalling between the threads, simulate a message pump
.
for (int i = 0; i < 3; i++)
{
control.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
Thread.Sleep(50);
}
The behavior is different dues to the different contexts that you are running under.
When you call bw.RunWorkerAsync(), the SynchronizationContext is captured. This is used to dispatch out the RunWorkerCompleted call.
Under WPF it will use DispatcherSynchronizationContext which will marshall the completed call back to the UI thread. Under the test, this marshalling is unnecessary so it remains on the background worker thread.
I belive that the calling thread must support messagepumping (mean, being STA apartment and having an associated Dispatcher) so the background worker can post the callback. If it does not, the background worker has no option but execute the callback in its own thread. If you want to test it, see this link.
I ran into a problem in my code where the user closing a window caused a save, that in turn used a BackgroundWorker to update the home window and it did not run the RunWorkerCompleted because the thread that started the BackgroundWorker had terminated when the window closed.
I had to change the closing window's save run in the home window's context so that after the BackgroundWorker completed, it had a thread to return to.
In my case I am using Windows Forms and controls don't have a Dispatcher property (see the answer in no definition for dispatcher).
Gishu's solution works as well if we use Dispatcher.CurrentDispatcher instead of the one in the control.
On test initialisation:
// I am using a field Dispatcher _dispatcher
_dispatcher = Dispatcher.CurrentDispatcher;
And then when waiting for the background task to be completed:
_dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
Thread.Sleep(50);
I have an application where I am uploading a file in blocks. My front end is WPF and I have a progress bar to show file upload progress (upload is done by separate thread, and the progress bar is in a separate form invoked by the child thread when uploading starts).
I found the total number of blocks in the file to set the maximum property of the progress bar.
Now for each block uploaded I increment the value of progress bar by 1.
But to my surprise, the progress bar starts to increment but never completes ( it stops showing progress after few blocks ).
Here is code for the thread responsible for uploading files:
System.Threading.Thread thread = new Thread(
new ThreadStart(
delegate()
{
// show progress bar - Progress is the name of window containing progress bar
Progress win = new Progress();
win.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
win.Show();
// find number of blocks
long BlockSize = 4096;
FileInfo fileInf = new FileInfo(filename);
long FileSize = fileInf.Length;
long NumBlocks = FileSize / BlockSize;
//set the min and max for progress bar
win.Dispatcher.Invoke(
new Action(
delegate()
{
win.progressBar1.Minimum = 0;
win.progressBar1.Maximum = NumBlocks;
}
), System.Windows.Threading.DispatcherPriority.Render);
//upload file
while (true)
{
// code to upload the file
win.Dispatcher.Invoke(
new Action(
delegate()
{
win.progressBar1.Value += 1;
}
), System.Windows.Threading.DispatcherPriority.Render);
}
}
Can someone help me analyze why is this happening.
Thanks.
Here's the issue:
upload is done by separate thread, and
the progress bar is in a separate form
invoked by the child thread when
uploading starts
If that means your child thread created the form, then that's the problem. Your child thread might be updating the progress bar values, but this will just invalidate the display, and not necessarily refresh the display. When a control's display is invalidated, it simply records that it must redraw it's display the next time it gets a chance. A refresh is when the control actually gets to render to the screen.
A better approach is to create the progress bar form in the main thread.
Your worker thread can then update the status, and your main thread will refresh the display.
One thing to remember: if you're updating a control that was created in a different thread, you must do so via the control's dispatcher.
var dispatcher = progressBar.Dispatcher;
dispatcher.BeginInvoke(new Action( () => { progressBar.Value = currentProgress }));
Edit after seeing the code
All you need to do is move the creation of the progress variable so that it is instantiated by the main thread before the worker thread is created.
Progress win = new Progress();
win.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen;
win.Show();
System.Threading.Thread thread = new Thread(
new ThreadStart(
delegate()
{
// ...