WPF open new window on another thread - c#

In the code below, I call method that opens a custom new window. However when application is doing some long running task I wish to still be able to activate the window. Is it possible to do it on another thread or by using the Task class?
public static class CustomW
{
static Custom_Window_Chrome_Demo.ThemedWindow MsgBox(string Msgbx_TTL, string Msgbx_Contnt)
{
var w_mbx = new Custom_Window_Chrome_Demo.ThemedWindow();
w_mbx.Width = 950; w_mbx.Height = 159;
w_mbx.Title = Msgbx_TTL;
Grid g = new Grid();
StackPanel spM = new StackPanel();
TextBlock TblckErrMsg = new TextBlock();
//more settings......
}
}
This is how I tried to invoke it,
public void newMsgBoxShow(string Msgbx_TTL, string Msgbx_Contnt)
{
System.Threading.Thread s = new System.Threading.Thread(
()=>
CustomW.MsgBox(Msgbx_TTL, Msgbx_Contnt).Show()
);
}
but when I am using the new thread I am getting the following error.
The calling thread must be STA, because many UI components require this
What is the correct way to achieve the required result ?

Use this:
Task.Factory.StartNew(new Action(() =>
{
//your window code
}), CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
When new thread is created with Current Synchronization context it will be able to update the UI.(when current thread is UI thread)
You can also use dispatcher to execute your code.
System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(()=>
{
//your window code
}));

Take a look at Dispatcher
https://msdn.microsoft.com/cs-cz/library/system.windows.threading.dispatcher%28v=vs.110%29.aspx
Dispatcher.CurrentDispatcher.Invoke(delegate { /* CODE */ }, DispatcherPriority.Normal);

Related

Async / await, create a new UI element error [duplicate]

This question already has answers here:
The calling thread must be STA, because many UI components require this error In WPF. On form.show()
(3 answers)
Closed 6 years ago.
I have the following issue with my simplified code (WPF) below:
System.InvalidOperationException' in PresentationCore.dll
The calling thread must be STA, because many UI components require this
Would be so kind to help me to correct my code.
void CrearBtnNews()
{
KinectTileButton botontest = new KinectTileButton
{
Style = FindResource("KinectTileButtonStyle1") as Style,
Content = "WeB",
Height = 265,
Width = 450,
Background = null,
BorderBrush = null
};
botontest.Click +=
async (o, args) =>
{
await Task.Run(()=> BrowserAsync());
};
}
private void BrowserAsync()
{
Grid gridx = new Grid();//////// ERROR in this line ///////////////
System.Threading.Thread.Sleep(8000);
MessageBox.Show("working 8 seg");
}
All UI-related things must be done in the main UI thread. You are trying to create an UI element in a background thread, which is a no-go.
If you want to do some long calculations etc. in the background, you should do only that, and then return the data to the main thread and create the UI controls there.
Something like this: (ResultStruct is madeup)
button.Click += async(o,args) =>
{
ResultStruct data = await Task.Run(() => Browser());
Grid gridx = new Grid();
// set the data to the grid
};
private ResultStruct Browser()
{
// calculations, working ...
return data;
}
Also, method BrowserAsync is not actually async, you are just calling it in an async task, so I renamed it to just Browser.
There's no need to wrap the entire method in Task.Run. Instead, you should only be wrapping 'the work' in Task.Run, and handle the creation of UI components on the UI thread. Though, if the creation of components is a lot, you can also wrap that in an async called (as shown in the example).
Note: The reason why you are getting the error is because you are trying to create a UI component (the Grid) on a separate thread. All UI specific stuff must be created on a Dispatcher thread.
void CrearBtnNews()
{
KinectTileButton botontest = new KinectTileButton
{
Style = FindResource("KinectTileButtonStyle1") as Style,
Content = "WeB",
Height = 265,
Width = 450,
Background = null,
BorderBrush = null
};
botontest.Click += async (o, args) =>
{
Grid gridx = new Grid();
await BrowserAsync();
MessageBox.Show("working 8 seg");
};
}
private async Task BrowserAsync()
{
// Do work here
//
await Task.Run(() =>
{
System.Threading.Thread.Sleep(8000);
});
}

Change the cursor when UI is busy

I have this class:
public class CursorWait : IDisposable
{
private readonly CancellationTokenSource _tokenSource;
public CursorWait(int showAfter)
{
_tokenSource = new CancellationTokenSource();
Task.Delay(showAfter, _tokenSource.Token).ContinueWith(delegate(Task task)
{
if (!task.IsCanceled)
Mouse.SetCursor(Cursors.Wait);
});
}
public void Dispose()
{
_tokenSource.Cancel();
Mouse.SetCursor(Cursors.Arrow);
}
}
To use it like this :
using (new CursorWait(showAfter: 500))
{
DoSomethingMayBeHeavyOrNotInUI();
}
However is not working since the Mouse.SetCursor relies in the UI thread to change it, and since it is busy, it will never change, so how can I change the cursor ?
Note: I know I should not be blocking the UI thread and instead just changing the property IsHitTestVisible of the window. but I'm new at this project and my team made the things this way, and they won't let me since the project is almost finished
Try adding this line After setting the mouse cursor.
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background, new Action(delegate { }));
Application.Current.Dispatcher.Invoke(new Action(()=>
{
// your code
}));
or
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
new ThreadStart(delegate
{
// your code
}));
More information at: MSDN - Dispatcher.Invoke Method

The calling thread cannot access this object error

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..

C# calling form.show() from another thread

if I call form.show() on a WinForms object from another thread, the form will throw an exception. Is where any way I can add a new, visible form to the main app thread? Otherwise, how can I open the form without stopping my currently executing thread?
Here is my sample code. I am attempting to start a thread and then execute some work within that thread. As the work progresses, I will show the form.
public void Main()
{
new Thread(new ThreadStart(showForm)).Start();
// Rest of main thread goes here...
}
public void showForm()
{
// Do some work here.
myForm form = new myForm();
form.Text = "my text";
form.Show();
// Do some more work here
}
Try using an invoke call:
public static Form globalForm;
void Main()
{
globalForm = new Form();
globalForm.Show();
globalForm.Hide();
// Spawn threads here
}
void ThreadProc()
{
myForm form = new myForm();
globalForm.Invoke((MethodInvoker)delegate() {
form.Text = "my text";
form.Show();
});
}
The "invoke" call tells the form "Please execute this code in your thread rather than mine." You can then make changes to the WinForms UI from within the delegate.
More documentation about Invoke is here: http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx
EDIT: You will need to use a WinForms object that already exists in order to call invoke. I've shown here how you can create a global object; otherwise, if you have any other windows objects, those will work as well.
You should call Application.Run() after you call form.Show(). For example:
public void showForm()
{
// Do some work here.
myForm form = new myForm();
form.Text = "my text";
form.Show();
Application.Run();
// Do some more work here
}
As for the details behind why, this msdn post may help.
The best way by my experience:
var ac = (ReportPre)Application.OpenForms["ReportPre"];
Thread shower = new Thread(new ThreadStart(() =>
{
if (ac == null)
{
this.Invoke((MethodInvoker)delegate () {
ac = new ReportPre();
ac.Show();
});
}
else
{
this.Invoke((MethodInvoker)delegate
{
pictureBox1.Visible = true;
});
if (ac.InvokeRequired)
{
ac.Invoke(new MethodInvoker(delegate {
ac.Hide();
ac.Show();
}));
}
}
}));
shower.Start();
If your use case is to display a GUI while the Main GUI Thread is busy (like loading bar) you can do the following:
private void MethodWithLoadingBar()
{
Thread t1 = new Thread(ShowLoading);
t1.Start();
// Do the Main GUI Work Here
t1.Abort();
}
private void ShowLoading()
{
Thread.Sleep(1000); //Uncomment this if you want to delay the display
//(So Loading Bar only shows if the Method takes longer than 1 Second)
loadingGUI = new LoadingGUI();
loadingGUI.label1.Text = "Try to Connect...";
loadingGUI.ShowDialog();
}
My LoadingGUI is a simple Form with a public Label and a ProgressBar with Style set to Marquee
After searching the web and not finding a good solution, I came up with my own:
public async Task FunctionWhereIStartForm( Func<Delegate,object>invoke )
{
//this is on another thread
invoke( new MethodInvoker( ( ) =>
{
new formFoo().Show( );
} ) );
}
Then call like this:
await FunctionWhereIStartForm(Invoke);

c# thread and a waiting form

I connect to a webserive. While the webservice is connected i want to have a waiting form with an animated gif inside of it. The waiting form is correctly displayed but the animated give is not animated it is fixed.
Can anybody help me. I have already tried : DoEvents but the gif is still not animated.
// Create the new thread object
Thread NewThread = new Thread(new ThreadStart(RunThread));
// Start the new thread.
NewThread.Start();
// Inform everybody that the main thread is waiting
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
waitingDialog.Activate();
Application.DoEvents();
// Wait for NewThread to terminate.
NewThread.Join();
// And it's done.
waitingDialog.Close();
MessageBox.Show("Upload erfolgreich erledigt.", "Upload Erfolgreich",
MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
}
public void RunThread()
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
}
Don't call Join on a thread from within the UI thread. Instead, disable any controls you don't want to act on until the task has completed (e.g. buttons) and then call back into the UI thread when the operation has completed - so move the "And it's done" code into a new method which is invoked at the end of the operation. If you're using .NET 4, I'd suggest using the TPL for this, as it makes it easier to represent "a task which is in progress" and to add a continuation to it. (It's also a good start for what will become the idiomatic way of doing async operations in .NET 4.5.)
The problem is coming from your join. join is synchronous, so basically you are making your UI wait till the thread finishes its work.
You want to use a callback function to come back to your UI.
Edit : ive been skeetified
You problem is here:
NewThread.Join();
This blocks the UI thread until NewThread ends.
Here's one way to do it:
private myDelegate;
// ...
myDelegate = new Action(RunThread);
myDelegate.BeginInvoke(new AsyncCallback(MyCallback),null);
// You RunThread method is now running on a separate thread
// Open your wait form here
// ...
// This callback function will be called when you delegate ends
private void MyCallback(IAsyncResult ar)
{
myDelegate.EndInvoke(ar);
// Note this is still not the UI thread, so if you want to do something with the UI you'll need to do it on the UI thread.
// using either Control.Invoke (for WinForms) or Dispatcher.Invoke (for WPF)
}
Thread.Join is a blocking call that does not pump messages so that is your problem. It is typically advised to avoid calling any kind of synchronization mechanism that causes the UI thread to block.
Here is a solution using the Task class and the Invoke marshaling technique.
private void async InitiateWebService_Click(object sender, EventArgs args)
{
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
Task.Factory.StartNew(
() =>
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
waitingDialog.Invoke(
(Action)(() =>
{
waitingDialog.Close();
}));
});
}
Here is a solution using a raw Thread.
private void async InitiateWebService_Click(object sender, EventArgs args)
{
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
var thread = new Thread(
() =>
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
waitingDialog.Invoke(
(Action)(() =>
{
waitingDialog.Close();
}));
});
thread.Start();
}
C# 5.0 makes this kind of pattern even easier with its new async and await keywords1.
private void async InitiateWebService_Click(object sender, EventArgs args)
{
FRM_Wait waitingDialog = new FRM_Wait();
waitingDialog.Show();
await Task.Run(
() =>
{
mfsportservicedev.ServiceSoapClient servicedev = new mfsportservicedev.ServiceSoapClient();
int status = servicedev.addEvent(videosNames, videos);
});
waitingDialog.Close();
}
1Not yet released.

Categories

Resources