I've created new WPF project, in main window I'm doing:
public MainWindow()
{
InitializeComponent();
Thread Worker = new Thread(delegate(){
this.Dispatcher.BeginInvoke(DispatcherPriority.SystemIdle, new Action(delegate
{
while (true)
{
System.Windows.MessageBox.Show("asd");
Thread.Sleep(5000);
}
}));
});
Worker.Start();
}
the problem is between those messages MainWindow hangs. How do I get it work asynchronously?
Because you are telling the UI thread to sleep and you are not letting the dispatcher return to processing its main message loop.
try something more like
Thread CurrentLogWorker = new Thread(delegate(){
while (true) {
this.Dispatcher.Invoke(
DispatcherPriority.SystemIdle,
new Action(()=>System.Windows.MessageBox.Show("asd")));
Thread.Sleep(5000);
}
});
What do you try to archive?
Your while-Loop and the Thread.Sleep() are executed on the UI-Thread, so no wonder the MainWindow-hangs.
You should put these two outside the BeginInvoke-call and only the MessageBox.Show inside the Action.
the delegate code you sent to Dispather.BeginInvoke is executed in main thread.
you should not sleep or do other long time job in the delegate of BeginInvoke method.
you should do long time job before BeginInovke method like this.
Thread CurrentLogWorker = new Thread(delegate(){
while (true)
{
this.Dispatcher.Invoke(DispatcherPriority.SystemIdle, new Action(delegate
{
System.Windows.MessageBox.Show("asd");
}));
Thread.Sleep(5000);
}
});
CurrentLogWorker.Start();
Related
I have app with 2 windows.
1st LoginWindow used to authentificate user and launch main app. I use thread and run dispatcher for that:
private bool EndTrigger = false;
/.../
Thread thread = new Thread(() =>
{
MainWindow T_window = new MainWindow(t_data);
T_window.WindowState = WindowState.Maximized;
T_window.Show();
EndTrigger = true;
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
After that LoginWindow is closed. I used function that checks if MainWindow is ready and Timer like this:
Timer LoginWinClose = new Timer(new TimerCallback(IfLoginWinCanBeClosed), null, 2000, 1000);
and
public void IfLoginWinCanBeClosed(Object stateInfo)
{
if (EndTrigger)
{
this.Dispatcher.Invoke(new Action(delegate
{
this.Close();
}));
}
}
It works as it should: LoginWindow disapper, MainWidow appear and everything works.
But when I tryed to create one more window in MainWindow I get Exception that tells me: Application is shutting down.
It looks like closing LoginWindow leads to attemp of closing application, but if I close any other window (for example MainWindow), I still can create one more from LoginWondow without any error.
Currently I solve this by by changing
this.Close();
to
this.Visibility = Visibility.Collapsed;
It means that LoginWindow will continue to run all the time. If there any another solution?
Thanks to #Sham I understand where is the mistake!
Code, where new window is created located in separate thread (this is because login check operations run in the separated thread to avoid hanging LoginWindow), so I Create and run new window with separate dispatcher in that Thread, instead of main UI thread.
So, the solution is quite easy. Just need to make a little modification:
Thread thread = new Thread(() =>
{
this.Dispatcher.Invoke(new Action(delegate
{
AdminWindow T_window = new AdminWindow(t_data);
T_window.WindowState = WindowState.Maximized;
T_window.Show();
t_data.Link_auth_win.EndTrigger = true;
System.Windows.Threading.Dispatcher.Run();
}));
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
I've create a WPF application with NotifyIcon, and I use this.Hide() to make it minimized, but now I hope it could be minimized once it had been executed, so I invoke this.Hide() in the method MainWindow_Loaded, but once I start the app, the content of the window turn to be all dark.
Then I try to invoke this.Hide() in another thread, and this is what my code looks like...
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
...
Thread thread = new Thread(DoWork);
thread.Start();
Console.WriteLine("thread start");
while (!thread.IsAlive) ;
Thread.Sleep(500);
thread.Join();
Console.WriteLine("thread has terminated.");
...
}
public void DoWork()
{
this.Hide();
}
then I encounter the problem, when DoWork() is invoked, it showed Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on. What should I do to avoid this? Thanks!
You need to use Dispatcher object.
Dispatcher.BeginInvoke((Action) (() =>
{
// your code
}));
I know there are a few answers on this topic on SO, but I can not get any of the solutions working for me. I am trying to open a new window, from an ICommand fired from within a datatemplate. Both of the following give the aforementioned error when the new window is instantiated (within "new MessageWindowP"):
Using TPL/FromCurrentSynchronizationContext Update: works
public class ChatUserCommand : ICommand
{
public void Execute(object sender)
{
if (sender is UserC)
{
var user = (UserC)sender;
var scheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task.Factory.StartNew(new Action<object>(CreateMessageWindow), user,CancellationToken.None, TaskCreationOptions.None,scheduler);
}
}
private void CreateMessageWindow(object o)
{
var user = (UserC)o;
var messageP = new MessageWindowP();
messageP.ViewModel.Participants.Add(user);
messageP.View.Show();
}
}
Using ThreadStart: Update: not recommended, see Jon's answer
public class ChatUserCommand : ICommand
{
public void Execute(object sender)
{
if (sender is UserC)
{
var user = (UserC)sender;
var t = new ParameterizedThreadStart(CreateMessageWindow);
var thread = new Thread(t);
thread.SetApartmentState(ApartmentState.STA);
thread.Start(sender);
}
}
private void CreateMessageWindow(object o)
{
var user = (UserC)o;
var messageP = new MessageWindowP();
messageP.ViewModel.Participants.Add(user);
messageP.View.Show();
}
}
Thanks
EDIT. Based on the responses so far, I'd like to point out that I have also tried BeginInvoke on the current dispatcher, as well as executing the code in the original method (that's how the code started). See below:
BeginInvoke Update: not recommended see Jon's answer
public class ChatUserCommand : ICommand
{
public void Execute(object sender)
{
if (sender is UserC)
{
var user = (UserC)sender;
Dispatcher.CurrentDispatcher.BeginInvoke(new Action<object>(CreateMessageWindow), sender);
}
}
private void CreateMessageWindow(object o)
{
var user = (UserC)o;
var messageP = new MessageWindowP();
messageP.ViewModel.Participants.Add(user);
messageP.View.Show();
}
}
In same thread Update: works if you are on UI thread already
public class ChatUserCommand : ICommand
{
public void Execute(object sender)
{
if (sender is UserC)
{
var user = (UserC)sender;
var messageP = new MessageWindowP();
messageP.ViewModel.Participants.Add(user);
messageP.View.Show();
}
}
}
BeginInvoke, using reference to dispatcher of first/main window Update: works
public void Execute(object sender)
{
if (sender is UserC)
{
var user = (UserC)sender;
GeneralManager.MainDispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => this.CreateMessageWindow(user)));
}
}
where GeneralManager.MainDispatcher is a reference to the Dispatcher of the first window I create:
[somewhere far far away]
mainP = new MainP();
MainDispatcher = mainP.View.Dispatcher;
I'm at a loss.
The calling thread must not only be STA, but it must also have a message loop. There's only one thread in your application that already has a message loop, and that's your main thread. So you should use Dispatcher.BeginInvoke to open your window from your main thread.
E.g. if you have a reference to your main application window (MainWindow), you can do
MainWindow.BeginInvoke(
DispatcherPriority.Normal,
new Action(() => this.CreateMessageWindow(user)));
Update: Be careful: you cannot blindly call Dispatcher.CurrentDispatcher because it doesn't do what you think it does. The documentation says that CurrentDispatcher:
Gets the Dispatcher for the thread currently executing and creates a
new Dispatcher if one is not already associated with the thread.
That's why you must use the Dispatcher associated with an already-existing UI control (like your main window as in the example above).
With TPL you can use the StaTaskScheduler from the TPL Extras
It will run tasks on STA threads.
Only used it for COM. Never tried to run several UI threads.
You are trying to create a window from a background thread. You cannot do it due to a variety of reasons. Typically you need to create the window in the main application thread.
For your case, a simple idea would be just do it immediately (just call CreateMessageWindow inside Execute) instead of allocating a Task, because your command will definitely fire from the main (UI) thread. If you are unsure about the thread where your Execute runs, you can marshal it to the UI thread using Dispatcher.BeginInvoke().
There are really few cases when you would want your new window to run in a non-main thread. If this is really really needed in your case, you should add Dispatcher.Run(); after messageP.View.Show(); (using the second variant of the code). This starts the message loop in the new thread.
You shouldn't try to run window in TPL's thread, because these threads are typically threadpool threads, and therefore out of your control. For example, you cannot ensure they are STA (typically, they are MTA).
Edit:
from the error in your updated code, it's clear that the Execute runs in some non-UI thread. Try using Application.Current.Dispatcher instead of Dispatcher.CurrentDispatcher. (CurrentDispatcher means the dispatcher of the current thread, which may be wrong if the current thread is not the main one.)
Recently I needed to implement please wait dialog in wpf application. i found below code. it's really good but it always open an window in saprate thread and hold the position. is there any other alter for below code. while my request of code is non threaded.
private void NewWindowThread<T,P>(Func<P, T> constructor, P param) where T : Window
{
Thread thread = new Thread(() =>
{
T w = constructor(param);
w.Show();
w.Closed += (sender, e) => w.Dispatcher.InvokeShutdown();
System.Windows.Threading.Dispatcher.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
}
to call above method use below lines. where loading window is you window which you want to show at dialog (please wait. windows)
string t = "Please Wait…";
NewWindowThread<LoadingWindow, string>(c => new LoadingWindow(c), t);
Blocking the ui thread was never a good idea, but it is increasingly more a bad idea.
Windows will tell the user that your app stopped responding. This may incite them to force your appliccations. If you render progress bars, they will lose the animation effects, and they may render incorrect. In WPF the gui animations will stop.
Use background threads for the heavy processing, and if you need to write data back in the objects used by your main thread, marshall them back to the gui thread. BackgroundWorker can be useful there.
this might help you out.
public partial class Splash : Window
{
private static Splash splash = new Splash();
// To refresh the UI immediately
private delegate void RefreshDelegate();
private static void Refresh(DependencyObject obj)
{
obj.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Render,
(RefreshDelegate)delegate { });
}
public Splash()
{
InitializeComponent();
}
public static void BeginDisplay()
{
splash.Show();
}
public static void EndDisplay()
{
splash.Close();
}
public static void Loading(string test)
{
splash.statuslbl.Content = test;
Refresh(splash.statuslbl);
}
}
using above code
Splash.BeginDisplay();
// Setting the status to show the application is still loading data
Splash.Loading("Connecting...");
// Set to sleep to simulate long running process
Thread.Sleep(1500);
Splash.Loading("Retrieving....");
Thread.Sleep(1500);
Splash.Loading("Success....");
Thread.Sleep(1500);
Splash.EndDisplay();
I try to delay close a window in my App.xaml.ca :
Window splash = new Window();
splash.Show();
Timer timer = new Timer(callback, null, 2000, Timeout.Infinite);
private void callback(object stateInfo)
{
splash.Close();
}
It works fine, but the whole App is shutdowning. What am doing wrong here ?
Be sure to check that you timer callback is coming back on the main dispatcher thread. If not then you will likely be getting an exception when you try to close your window from a different thread.
Use splash.Dispatcher.CheckAccess() to make sure you are on the right thread and if not then use splash.Dispatcher.BeginInvoke((Action) () => splash.Close() to dispatch the call onto the main thread.
Check out this page for more
Here is my solution to this exact same problem:
private async void CloseWindow()
{
await ClosingTasks();
}
private async Task ClosingTasks()
{
await Task.Delay(2000);
this.Close();
}
Where you simply call CloseWindow() when you want to close the current window after the given delay of 2000 mS.
There are different shutdown modes, if that window is closed and it is the last then the application will shut down by default. So you can either see to it that there is still some window around or you can change the shutdown behaviour by setting the ShutdownMode to something that suits your needs.
e.g.
Application.Current.ShutdownMode = System.Windows.ShutdownMode.OnExplicitShutdown;