I began using C# and WPF a few months ago and now I thought I'd try to learn some new techniques like using threading. So I have an app that I want to run all the time (using an infinity while loop) but show the dialog (main window) every minute. So I am doing this by using Threading and here is how i am doing this:
public MainWindow()
{
InitializeComponent();
while (true)
{
callmyfunction()
system.Threading.Thread.Sleep(1000);
}
}
In my callmyfunction(), I am calling the dialog (which is the main WPF application) so it will show. Here's how i am doing it:
public void callmyfunction()
{
this.ShowDialog();
}
I have a regular button and when you click on it, it should hide the main window. So my button function is like this:
private void Button_Click2(object sender, RoutedEventArgs e)
{
this.Hide();
}
So what I am doing is, I am loading the main window normally and it has a button, when I click on that button, it should hide the main window and the window should sleep as per the milli-seconds I specified in thread.sleep and then it will wake up, then again the dialog will appearand it will show the button and so on and so forth. This loop is working fine with me, but the issue I am having is that after the first dialog appears and I click on the button to hide the main window, the second time the main window appears, the button would appear as a "pressed" button, not as a new button. I think it's because I "pressed" on it the first time the main window appeared. And it stays like that until I stop the program and run it again.
So my question is, any idea how I can "reset" the button control? Or do I need to reset the mouse click event? Any pointers on this would be helpful.
You should run the loop on a background thread. If you run it on the UI thread, the application won't be able to respond to user input.
The easiest and recommended way to run some code on a background thread is to start a new Task.
Also note that you cannot access the window from a background thread so you need to use the dispatcher to marshal the call back to the UI thread.
The following sample code should give you the idea.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Task.Factory.StartNew(() =>
{
while (true)
{
Dispatcher.Invoke(() => callmyfunction());
System.Threading.Thread.Sleep(5000);
}
}, TaskCreationOptions.LongRunning);
}
public void callmyfunction()
{
WindowState = WindowState.Normal;
}
private void Button_Click2(object sender, RoutedEventArgs e)
{
WindowState = WindowState.Minimized;
}
}
Related
I'm currently working on a snip tool that captures an image of the screen. When I click "New Snip" the program needs to create a screen capture after the tool window has minimized. It currently works like this.
//Minimize Tool Window
WindowState = WindowState.Minimized;
//Set delay to allow window to finish its minimizing animation
Thread.Sleep(200);
//Show screen capture with snipping
SnippingWindow sw = new SnippingWindow();
sw.ShowDialog();
//Close minimized window
Close();
Is there a way to wait for the WindowState change animation to finish before calling the SnippingWindow? I would prefer not to make a Thread.Sleep call, but it's the only thing that seems to be working.
I could close the window prior to calling the Snip, but that doesn't seem to "get it out of the way" in time either as it's executed on the same thread.
I eventually figured out a workaround (although not very clean, but it avoids having to using Sleep).
I bound the Window's Opacity to a value that can be changed with a BackgroundWorker. When the BackgroundWorker starts it sets the opacity to 0. Once the worker is complete the RunWorkerCompleted event handles the new snip screen shot.
This ensures the tool window is out of the way before the screen shot is taken.
I tried to simply Close the window from the background worker first, but it didn't execute the close operation correctly.
private void Bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Close();
SnippingWindow sw = new SnippingWindow();
sw.ShowDialog();
}
private void Bw_DoWork(object sender, DoWorkEventArgs e)
{
//Windows Opacity is bound to this value.
//I had to implement iNotifyPropertyChanged on this property.
OpacityValue = 0;
}
After clicking on a button, I show a progress form and start a task. I want to disable the button or make the progress form a modal so nothing should clickable on the main form, till the progress is completed.
I Tried passing owner reference as an argument when calling the progress bar form's ShowDialog:
m_oProgressBarForm = new ProgressBarForm();
m_oProgressBarForm.ShowDialog(this);
Please help how can I prevent users to click on button again by graying out the main window or making the buttons not clickable or making the progress bar window as the modal?
Currently the user can click the button again and another instance starts which makes the application not usable, and have to manually kill the application using task manager.
If you show a form using ShowDialog() the code following it is not executed until after the dialog box is closed. But you probably need to show progress-bar form and then perform some operations and then close the form, You can not show your progress form as modal.
Instead you can disable your main form and the show your progress form by setting the main form as its parent, then perform the time-consuming task and at last, close the progress form and enable the main form:
private async void Button1_Click(object sender, EventArgs e)
{
var f = new Form(); //Your progress form
f.Show(this);
this.Enabled = false;
try
{
//For example a time-consuming task
await Task.Delay(5000);
}
catch (Exception ex)
{
//Handle probable exceltions
}
f.Close();
this.Enabled = true;
this.BringToFront();
}
I'm trying to use a window as splash screen. I have this:
{
InitializeComponent();
new splash().ShowDialog();
}
in my main window to start up with and it works but on the window that I'm using as splash when I press start it stays blank. This is the code I'm using for the splash window:
public partial class splash : Window
{
public splash()
{
InitializeComponent();
}
private void Window_Loaded_1(object sender, RoutedEventArgs e)
{
System.Threading.Thread.Sleep(3000);
Close();
}
As it is it just loads the window blank and after 3secs it moves on to the main window.
I want the splash window to load with a label and an image.
Any ideas?
Thanks
So, as I already mentioned it in my comment, here is an excellent guide on how to implement a splash screen for WPF applications. Also Microsoft offers an easier way if your splash screen is only an image (see here).
But the main problem with your code is the Sleep(3000) call, as it blocks the UI thread. Use a Timer instead, which you can start in the window loaded event handler, and close the window in the Timer's Elapsed event handler.
Hope this helps.
(Sorry for almost duplicating my comment, but at the third edit I realized it actually should be an answer :))
I have a WPF application. The main window of this application has a button. I am opening a WinForms modal dialog in a separate thread when this button is clicked. The trouble I am having is that the dialog does not behave like a modal i.e it is still possible to switch focus to the main window, whereas, I require to allow focus on the newly opened dialog and it should not be possible to select the main window.
Note: I cannot move the modalDialog.ShowDialog(); outside of the delegate because the dialog form creates controls dynamically and this means that these controls must remain on the thread that it was created. To be more clear, if I move the modalDialog.ShowDialog(); outside I will get an exception like so:
Cross-thread operation not vaild: Control 'DynamicList' accessed from a thread other than the one it was created on.
Any ideas as to how I might make the form behave as a modal?
Here is the code:
private void button1_Click(object sender, RoutedEventArgs e)
{
DoSomeAsyncWork();
}
private void DoSomeAsyncWork()
{
var modalDialog = new TestForm();
var backgroundThread = new Thread((
delegate()
{
// Call intensive method that creates dynamic controls
modalDialog.DoSomeLongWaitingCall();
modalDialog.ShowDialog();
}
));
backgroundThread.Start();
}
You should always create controls on the UI thread. If you do that, calling ShowDialog() through Dispatcher should work.
I have a form in a Windows form application that I want to display on top of a main form, close it, and then immediately show a dialog box using MessageBox.Show(). But the first form is still showing when the message box is shown, and it does not disappear until I click OK on the message box. I tried waiting to show the message box in an event handler for the form's VisibleChanged event and even calling Refresh() on both the form and the main form. Is there a way I can determine when the first form has fully disappeared before displaying the message box?
Edit:
Here is some code that demonstrates how the forms are being shown.
static class Program
{
// The main form is shown like this:
static void Main()
{
Application.Run(new MainForm());
}
}
public class Class1
{
// _modalForm is the first form that is displayed that won't fully go away
// when it is closed.
ModalForm _modalForm;
BackgroundWorker _worker;
public Class1()
{
_modalForm = new ModalForm();
_worker = new BackGroundWorker();
_worker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted
}
public void Method1()
{
_worker.RunWorkerAsync();
// The first form is shown.
_modalForm.ShowDialog();
}
// This code runs in the UI thread.
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
_modalForm.VisibleChanged += new EventHandler(_modalForm_visibleChanged);
_modalForm.Close();
}
void _modalForm_visibleChanged(object sender, EventArgs e)
{
// When the message box is shown, the other form is still visible
// and remains so until I click OK.
MessageBox.Show("The other form was just closed.");
// Note: I originally tried to use the FormClosed event instead of
// VisibleChanged. Then I tried Deactivate, in attempt to use an event
// that occurred later thinking that might do the trick. VisibleChanged
// is the latest event that I found.
//
}
I'll guess that you are running your code on Windows XP or Vista/Win7 with Aero turned off. Closing a form does not make the pixels on the screen disappear instantly. The Windows window manager sees that the window for the form got destroyed and that this revealed parts of other windows underneath it. It will deliver a WM_PAINT message to let them know that they need to repaint the parts of the window that got revealed.
This will not work properly if one or more of those windows isn't actively pumping a message loop. They can't see the WM_PAINT message. They won't repaint themselves, the pixels of the closed form will remain on the screen.
Find out why these windows are not responding. Hopefully it is your window and the debugger can show you what the UI thread is doing. Make sure it isn't blocking on something or stuck in a loop.
After seeing the edit: there's indeed blocking going on, of a different kind. The MessageBox.Show() call is modal, it prevents the VisibleChanged event from completing. That delays the closing of the form.
Use System.Diagnostics.Debug.WriteLine() or Console.WriteLine() to get diagnostics in a Window Forms app. You'll see it in the Output window. Or simply use a debugger breakpoint.
The Form.FormClosed event is raised when the form completes closing. At this point, all Form.FormClosing event handlers have been run, and none of them canceled the close.
Form.FormClosed replaced Form.Closed (which is deprecated) in the .NET 2.0 framework.
Form.Closed Event
http://msdn.microsoft.com/en-us/library/system.windows.forms.form.closed(VS.71).aspx