I use a control that is a popup window. I have a methodes that show and hide it and I need to implement a functionality that will prevent show and hide my popup in different threads. Can I show my popup in not UI thread?
Update
The main goal of my question is:
It's not important from what thread the method Show starts, the Method Hide should be in the same thread. How to implement this?
public void Show()
{
IsShown = true;
if (this.ChildWindowPopup == null)
{
this.ChildWindowPopup = new Popup();
try
{
this.ChildWindowPopup.Child = this;
}
catch (ArgumentException)
{
throw new InvalidOperationException("The control is already shown.");
}
}
if (this.ChildWindowPopup != null && Application.Current.RootVisual != null)
{
// Configure accordingly to the type
InitializeProgressType();
// Show popup
this.ChildWindowPopup.IsOpen = true;
}
}
public void Hide()
{
IsShown = false;
// Restore system tray
SystemTray.IsVisible = currentSystemTrayState;
this.progressBar.IsIndeterminate = false;
this.ChildWindowPopup.IsOpen = false;
}
Generally working on UI components can only be done in UI thread. If you want to block your specific popup from being started from UI threads you would need to provide an interface to launch it that internally checks the thread it has been executed from - perhaps using SynchronizationContext or Thread classes, but in the end - it will need to show the Popup by invoking a method on UI thread.
Related
I have a issue with thread, I've searched for a few days but still cannot solve it..
Due to some reason, I customize a progress form and use it in threads.
I tried to write all functions inside the progress form so that they are wrapped by Invoke and delegate. Unfortunately, this code is not working properly since this.InvokeRequired is returning false when I expected it to return true.
The problem is, when I execute the program, sometimes it throw an exception:
Cross-thread operation not valid: Control 'FormProgress' accessed from a thread other than the thread it was create on.
Here's the code of progress form.
I've wrapped all functions with Invoke and delegate.
public partial class FormProgress : Form
{
public FormProgress()
{
InitializeComponent();
}
public void SetStatusLabelText(string text)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker) delegate
{
label1.Text = text;
});
}
else
{
// exception thrown here
label1.Text = text;
}
}
public void SetDialogResult(DialogResult dialogResult)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
if (DialogResult == DialogResult.None)
this.DialogResult = dialogResult;
});
}
else
{
if (DialogResult == DialogResult.None)
this.DialogResult = dialogResult;
}
}
}
Here's the code of thread, the exception throws when I click button1
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
for (int i=0; i<100; i++)
ProgressTest();
}
private void ProgressTest()
{
FormProgress dialog = new FormProgress();
{
Thread threadTest = new Thread(delegate ()
{
dialog.SetStatusLabelText("initial....(1)");
Thread.Sleep(50);
dialog.SetStatusLabelText("initial....(2)");
Thread.Sleep(50);
dialog.SetStatusLabelText("initial....(3)");
Thread.Sleep(50);
dialog.SetDialogResult(DialogResult.OK);
});
threadTest.Name = "ThreadTest";
threadTest.Start();
if (dialog.ShowDialog() == DialogResult.Cancel)
{
if (threadTest.IsAlive)
threadTest.Abort();
}
threadTest.Join();
}
}
}
As per the docs:
If the control's handle does not yet exist, InvokeRequired searches up
the control's parent chain until it finds a control or form that does
have a window handle. If no appropriate handle can be found, the
InvokeRequired method returns false.
This means that InvokeRequired can return false if Invoke is not
required (the call occurs on the same thread), or if the control was
created on a different thread but the control's handle has not yet
been created.
In the case where the control's handle has not yet been created, you
should not simply call properties, methods, or events on the control.
This might cause the control's handle to be created on the background
thread, isolating the control on a thread without a message pump and
making the application unstable.
You can protect against this case by also checking the value of
IsHandleCreated when InvokeRequired returns false on a background
thread. If the control handle has not yet been created, you must wait
until it has been created before calling Invoke or BeginInvoke.
Typically, this happens only if a background thread is created in the
constructor of the primary form for the application (as in
Application.Run(new MainForm()), before the form has been shown or
Application.Run has been called.
The issue you have is that some of your InvokeRequired calls may be occurring before the form has been shown. This is because you are starting your new thread before calling dialog.ShowDialog(). Note, as is common with race conditions, the problem won't always occur - just sometimes.
As per above, you may want to consider checking IsHandleCreated before executing the logic in your else blocks (after checking InvokeRequired) to protect against this possibility. Alternatively, rethink your entire strategy around the progress form.
do changes with controls inside if (this.InvokeRequired) block
remove the else block staying after if (this.InvokeRequired)
public void SetStatusLabelText(string text)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker) delegate
{
label1.Text = text;
});
}
}
public void SetDialogResult(DialogResult dialogResult)
{
if (this.InvokeRequired)
{
this.Invoke((MethodInvoker)delegate
{
if (DialogResult == DialogResult.None)
this.DialogResult = dialogResult;
});
}
}
let's consider method ProgressTest(), what happaning:
after threadTest.Start() has been called , the threadTest method starts execution of his work item in a new thread
after dialog.ShowDialog() the GUI thread become blocked , it makes this.InvokeRequired = false
at the same time threadTest keep working and when threadTest try to execute
else
{
label1.Text = text;
}
label1.Text setter is called from NONE GUI thread (it is called from "ThreadTest" thread), that's why you get exception
It should be noted that dialog.SetStatusLabelText("initial....") which supposed to be called 300 times , actually will be called less then 300
I can't understand how can I implement sync MessageBox/QueryBox/QueryStringBox calls in my Monogame Windows Store application.
In realizations of our engine on other platforms (Android, WP7/8, IOS) we call MessageBox/QueryBox/QueryStringBox wrapper in sync way:
if (SysDlg.QRESULT_YES == SysDlg.showQueryBox(APP_NAME, "Save data?", S_YES, S_NO))
{
//Save data
}
else
{
//do something else
}
It's the most convenient way, as gui code becomes linear. Such calls are made inside a background render thread, so it's ok if we wait inside SysDlg.showQueryBox. Pseudocode looks like this:
int standardOpenDlg(int dlgType, <some arguments>)
{
msDlgActive = true;
< async construct and show dlg on main UI thread with handler like below >
dlg.onButtonPress += () =>
{
dlg.close();
msDlgResult = 1;
msDlgActive = false;
}
while (msDlgActive)
EngineSystem.Sleep(100);
//when user presses button msDlgActive becomes false and we quit endless loop and return msDlgResult
return msDlgResult;
}
In my current case I have such MessageBox code:
var messageDialog = new MessageDialog(msDlgText, msDlgTitle);
for (int i = 0; i < cnt; i++)
messageDialog.Commands.Add(new UICommand(buttonsTexts[i],
new UICommandInvokedHandler(MsgBoxInputFinishedCallBack), (int)i));
messageDialog.DefaultCommandIndex = 0;
messageDialog.ShowAsync();
And buttons' callback:
private static void MsgBoxInputFinishedCallBack(IUICommand command)
{
// get the result
if (command != null && command.Id != null)
{
int result = (int)command.Id;
msDlgResult = (int)result;
}
msDlgActive = false;
}
I've tried to wrap messagebox creation and call in
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {...} );
But if I block my background draw thread after that I never see the message box. It looks like MainGame.Update (it executes exactly in this bg thread) should finish and return control to the system so that my code on main thread can be executed.
If I call (even in my thread) and then continue execution (but also leave caller context without msDlgResult ) I messagebox appears as expected.
I also tried
messageDialog.ShowAsync();
await messageDialog.ShowAsync();
Task t = messageDialog.ShowAsync().AsTask();
t.Wait();
but nothing helps.
In Windows Phone 8 I just called Guide.BeginShowMessageBox with callback and everything worked fine.
I suppose I don't quite understand this new Metro and async/await (although I know what is it).
Please tell how can I wait in bg thread until messagebox closed?
Maybe I should use some other winRT UI for that?
Thanks!
------Update
If anybody cares - finally I had to create/show dialogbox and handle results in two different places.
I need to be able to start up a window on a second UI thread and shut it down again at will.
This is my current code:
/// <summary>Show or hide the simulation status window on its own thread.</summary>
private void toggleSimulationStatusWindow(bool show)
{
if (show)
{
if (statusMonitorThread != null) return;
statusMonitorThread = new System.Threading.Thread(delegate()
{
Application.Run(new AnalysisStatusWindow(ExcelApi.analyisStatusMonitor));
});
statusMonitorThread.Start();
}
else
{
if (statusMonitorThread != null)
statusMonitorThread.Abort();
statusMonitorThread = null;
}
}
AnalysisStatusWindow is a fairly basic System.Windows.Forms.Form
The above code is successfully creating the new UI thread, but my request to Abort the thread is ignored. The result is that toggling the above function multiple times simply results in new windows opening up - all of which are on their own thread and fully functional.
Is there any way I can pass a message to this thread to shut down nicely? Failing that, is there any way to make sure Abort() really kills my second UI thread?
I've tried using new Form().Show() and .ShowDialog() instead of Application.Run(new Form()), but they aren't any easier to shut down.
If anyone is questioning the need for a separate UI thread, this code exists in an Excel Add-in, and I cannot control the fact that the Excel UI blocks while calculations for a given cell are underway. For that reason, when a long running custom formula executes, I require this second UI thread to display progress updates.
Thanks to Hans for his comment. I solved my problem using the following code:
/// <summary>Show or hide the simulation status window on its own thread.</summary>
private void toggleSimulationStatusWindow(bool show)
{
if (show)
{
if (statusMonitorThread != null) return;
statusMonitorWindow = new AnalysisStatusWindow(ExcelApi.analyisStatusMonitor);
statusMonitorThread = new System.Threading.Thread(delegate()
{
Application.Run(statusMonitorWindow);
});
statusMonitorThread.Start();
}
else if (statusMonitorThread != null)
{
statusMonitorWindow.BeginInvoke((MethodInvoker)delegate { statusMonitorWindow.Close(); });
statusMonitorThread.Join();
statusMonitorThread = null;
statusMonitorWindow = null;
}
}
When my application is loading. I display a progress bar using the code below. The problem is if someone clicks on the toolbar context menu (the way to exit) it will be blocked until this the progress bar is closed. Does anyone know a better way of achieving this?
The reason I'm using ShowDialog is that when I used Show the progress bar wouldn't animate - I'm using the MarqueeStyle.
Thanks
public partial class PopUpProgessBar : Form
{
public PopUpProgessBar()
{
InitializeComponent();
}
Thread t;
private void StartAnmiation()
{
this.Update();
this.ShowDialog();
}
public void Stop()
{
if (t != null)
{
t.Abort();
t.Join();
}
}
public void Start()
{
if (t == null)
{
t = new Thread(new ThreadStart(this.StartAnmiation));
t.Start();
}
}
This code doesn't look quite right. Are you sure it doesn't throw cross-thread violations? In general, your whole metaphor here is wrong. You need to keep the GUI on the GUI thread. Load your application on the background thread and have it send progress updates to the GUI thread.
Your PopupProgressBar form shouldn't be responsible for loading itself in a new thread, that should be done in presumably your main window.
I would get rid of all the thread stuff in PopupProgressBar and make it simply start updating it's marquee. Then, in your main window (OnLoad) you tell it to do it's thing:
bool done = false;
PopupProgressBar splashForm = null;
ThreadPool.QueueUserWorkItem((x) =>
{
using (splashForm = new PopupProgressBar ())
{
splashForm.Show();
while (!done)
Application.DoEvents();
splashForm.Close();
}
});
// do all your initialization work here
// also, during each step of your initialization you could send call a function
// in splashForm to update
done = true;
In my application I have a Button. If the button is clicked as select against a database is executed and the result is shown in a ListView.
As the select is quite complex, it takes some time to retrieve the data.
When I click the Button, the Application-Window should be disabled until the data is loaded.
But when I set the IsEnabled-Property of the Window to false, the window gets disabled after the data is loaded.
I tried to disable the Window in an other thread with a BackgroundWorker. But then I get an exception that the window is already in use by another thread.
How can I disable the Window bevore it retrieves the data?
You did the wrong thing in a background thread. You have to affect the UI from the UI thread, and your data loading should occur in a background thread.
The simplest approach is to use a BackgroundWorker to load your data, store that data in a class-level variable, and when your background work is complete, the UI re-enables and loads the data from the class-level variable.
I'd think you'd move the database activity to the background thread to leave your UI responsive (even if it's only to disable it) rather than the other way around.
try this:
BackgroundWorkerHelper.DoWork<Type of object you want to retrieve>(
() =>
{
//Load your data here
//Like
using (MarketingService svc = new MarketingService())
{
return svc.GetEmployeeLookupTable();
}
},
(args) =>
{
this.IsEnable = true;
if (args.Error == null)
{
Your Target Datasource= args.Result;
}
});
this.IsEnable = false;
I will suggest "BusyDialog" window in addition to background thread.
Yous busy dialog can be an animation displaying it is doing something, and modally blocking any user input as well.
public partial class BusyDialog : Window
{
public BusyDialog()
{
InitializeComponent();
}
public static T Execute<T>(DependencyObject parent, Func<T> action)
{
Window parentWindow = null;
if (parent is Window)
{
parentWindow = parent as Window;
}
else
{
parentWindow = Window.GetWindow(parent);
}
T val = default(T);
Exception le = null;
BusyDialog bd = new BusyDialog();
bd.Owner = parentWindow;
ThreadPool.QueueUserWorkItem((o) =>
{
try
{
val = action();
}
catch (Exception ex)
{
le = ex;
}
bd.EndDialog();
});
bd.ShowDialog();
if (le != null)
{
Trace.WriteLine(le.ToString());
throw new Exception("Execute Exception", le);
}
return val;
}
private void EndDialog()
{
Dispatcher.Invoke((Action)delegate() {
this.DialogResult = true;
});
}
}
Now you can use following way to call your method asynchronously,
List<Result> results = BusyDialog.Execute( this ,
()=>{
return MyLongDatabaseLoadingCall();
});
This is what happens,
BusyDialog is displayed modally, blocking any user input as well as displaying busy animation
A call to your method MyLongDatabaseLoadingCall is executed in ThreadPool.QueueUserItem, which asynchronously calls your method in different thread (Same as background threading functionality suggested by others here).
Animation continues till the call is executing
When your method ends, BusyDialog is ended and everything is back to how it was.