CM bootstraper showing a window before the root view - c#

I have a Caliburn.Micro bootstrapper where I use OnStartup() to check a view things and call DisplayRootViewFor<IShell> later. In some scenarios I must have the user to make an important decision or show some warning before the root view launches. I can do this by using WindowManager.ShowDialog().
But here is the problem: When I have no window shown before the root view, all works like expected.
When I show a window before the root view the DisplayRootViewFor() call is made but the application terminate immediately.
I guess this is because when I use the window manager to show a window before it gets the root view and closing it makes the WPF application thinking its main window is closed and it terminates automatically.
So how can I show a (modal) window before the root view?
I found one possible solution is:
Set Application.ShutdownMode to OnExplicitShutdown. Then I have to track when really shutting down the application like when the shell is closed I have to call System.Windows.Application.Current.Shutdown(); explicitly.
I am curious if there is also another way but I guess when the modal window is closed the for a moment the number of windows in the WPF application goes down to 0 the shutdown is triggered regardless if a new main window is established shortly after.

you can put your shutdown on your shell view on close as the shell view is the main window if the user close it means they want to close the exe.
private void ShellView_OnClosed(object sender, EventArgs e)
{
Application.Current.Shutdown();
}

Well, if someone else is having this problem, I solved it overriding the OnActivate method like this:
protected override void OnActivate()
{
_windowManager.ShowDialog(YOUR_WINDOW);
base.OnActivate();
}
Thus you don't have to set Application.ShutdownMode to OnExplicitShutdown and your application works normally.

Related

Form gets disposed on Hide()

I have a C# WinForms application running on .NET Framework 4.0.
When the user is inactive for a certain period of time, I want it to hide all the displayed forms and show an icon in the notification area. When the user clicks that icon, a login form appears and if the credentials are valid, it opens the exact forms that were open before.
To do this, I store the list of open forms in a List of Form objects and hide them, like this. This method is called by a Timer:
private void LogOut()
{
foreach (Form form in Application.OpenForms)
if (form.Visible)
{
GlobalVariables.formList.Add(form);
form.Hide();
}
}
When the credentials are validated, I try to make the forms visible again, like this:
//Show the previous forms.
foreach (Form form in GlobalVariables.formList)
form.Visible = true;
//Clear the forms list.
GlobalVariables.formList.Clear();
If I only have the MainForm open when I hide the forms, it shows it back fine when logging back in. If I have any other forms open (which are opened using ShowDialog() from the MainForm), the program will crash on form.Visible = true; and give me the following error message:
ObjectDisposedException was unhandled
Cannot access a disposed object
How can I fix this problem? An alternative way of doing what I'm trying to achieve would also be great.
Please note that using a try - catch block to determine if the form has been disposed and just relaunch the form is not an option as the user may have unsaved input in the hidden forms.
I couldn't manage to find anything related online in over 3 hours of search so any help would be much appreciated!
EDIT: After trying various things, I have noted that the problem only occurs on forms I have opened forms using ShowDialog(). If I only have forms opened using Show(), everything works fine.
However in my case, using Show() is not an option because I cannot have the user click on things in the parent form. Hiding the parent form is not an option either as he needs to see information in the parent form.
Clearly hiding a form is more impactful than you counted on. Your code was involved in a security review that Microsoft conducted on Winforms. Very thorough, not often visible in the way it behaves but very visible in the source code. One rule is imposes is that a user should never lose control over the application.
A dialog is very troublesome that way. The core problem is that ShowDialog() creates a modal window that disables all the other windows. That creates an opportunity for malware, very easy to take advantage of, all it has to do is hide a dialog and you snookered the user. There isn't any way that the user can gain control of the app again. The one window that was enabled is hidden with no way for the user to re-activate it again. All the other windows are disabled so trying to click on them, or their taskbar button, will not have any effect. All that's left is for the user to use Task Manager to kill the app. And if the user account is locked down then that's not an option either.
I can hear you sputter by now: "But, but, it is my code that hides the dialog, not malware!" That's not the way it works in Windows, there's no way to tell that it actually was your code that did it. Not only because it could be injected code, it doesn't even have to be code that runs in your process. Any code can do it, it is part of the winapi.
So there's a specific counter-measure against this built into Winforms, it will automatically close a form if it is hidden while operating in dialog mode. Which of course has a big impact, code that was written after the ShowDialog() call will now run. Anything is possible, but a sure-fire mishap in your case is that this disposes another window and an attempt to revive it will die.
The rough guidance here is that you are doing it wrong. You are trying to build a security system on top of one that's already highly secure and heavily tested. And it is very risky, handling passwords yourself is a very good way to make the overall system much less secure. The average user will of course favor picking the same password as he used to login to Windows. Makes it much easier for an attacker to harvest that password.
Call LockWorkStation() instead.
After many hours of trial and error, I found out what the problem was.
My understanding of modal forms was that code would continue executing in the parent form only after the modal form was closed. In fact, the specification found on MSDN states:
A modal form or dialog box must be closed or hidden before you can continue working with the rest of the application.
This introduced a subtle bug in the way I handled the forms. This is the code I used to display the forms:
using (var theForm = new CreateInvoice())
{
theForm.ShowDialog();
if (theForm.Updated)
{
GetInvoiceStatus();
}
}
The using statement disposes of theForm as soon as the statement exits. Normally, this works perfectly fine as it would be called only when the user closes theForm. However, because ShowDialog() permits the parent form to continue its work when it is hidden, this meant that the code actually exited the using statement, which effectively disposed of theForm, resulting in my error.
Testing, it seems that Hide()ing a modal dialog - closes it. It actually triggers the FormClosing event.
Tested like this: (Also, see this answer.)
private void button1_Click(object sender, EventArgs e)
{
Form1 f1 = new Form1();
f1.ShowDialog();
}
private void button2_Click(object sender, EventArgs e)
{
Hide();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
MessageBox.Show("Closing");
}
EDIT
I noticed this actually doesn't solve the mystery, just adds more information. Here's another piece of information: When you're setting Visible to true - you're not showing it modal again. This time it's equivalent to Show().
One suggestion to the design: instead of saving forms(views), you should save the data that the form holds(Model) and destroy the form. When you need the form again, create it back with the data(model). First, this can resolve this mysterious dispose problem, second, each form will need GDI resource, which are limited, if two many forms, you'll encounter the memory and GDI problems.
As how to do this, please refer the MVC or MVP design pattern.
BTW my guess on this problem: when you make the form visible, it will try find its parent, but its parent may be already disposed. I encountered this problem once, it throws the object disposed exception.

How to delete file, then user closes program in WPF?

I know full path of file var temp_file = Path.Combine(_directoryName1, "temp.ini"); which need to be deleted in the end of program working. How could I do this ? As I know it is possible to realize via OncloseEvent(). In addition, I dont know exatly how user will close application via alt+f4 or via buttons.
So far, I have tried to use this code below from almost the same question How to override onclose event on WPF?
protected override void OnClosing(System.ComponentModel.CancelEventArgs e)
{
//do my stuff before closing
base.OnClosing(e);
}
And I have added it in App.xaml.cs but it doesn't work. VS2013 says that he don't know such method base.OnClosing(e);
Is there any mistake or another way out?
Window.Closing is for a specific Window. That's probably not what you want in any case, because the Closing event can be cancelled. Window.Closed is likely a better choice.
To run something when the program is closed -- not tying yourself to a window -- you should subscribe to the Application.Exit event instead.

Windows Form only refreshes(rerenders) button.visible when any other button is pressed.[c#]

I have a main form called "Main" which dynamically resizes itself depending on what buttons are enabled. Inside of Main I also have a function called Updater. Updater reloads all the information from a file into memory, does a tiny bit of processing, and then 'should' refresh the window. This is the refreshing part:
Application.DoEvents();
this.Refresh();
Then I have another form that is a configuration page. Simply opens up the same data file, lets the user change things, and saves it back. Which then closes itself and runs Updater which should simply refresh the Main form with the new information. Here is the calling code:
Main main = new Main();
main.UpdaterCaller();
Now understand the new information is enabling and disabling buttons and positioning them. I know this all works as when I ran it, i put a snippet of console code that lets me know it has run through it. But when I close the configuration window, the console displays the message that is written within Updater(), but it doesn't show the changes on Main. Only when I click any other message does is instantly refresh with teh new buttons.
Sorry if I wasn't specific enough or didn't use correct termonology. Thanks for the help!
You're problem is that the configuration page is creating a new instance of Main and updating that. This instance is actually invisible since it has never been shown.
I'd simply display the configuration page via ShowDialog() and then have Main update itself once it's closed:
// ... this code is running in form Main ...
frmConfiguration config = new frmConfiguration();
config.ShowDialog(); // code here STOPS until "config" is closed
this.UpdaterCaller();

How to close winform with TopMost=true, when another application opens on top of my app

I have a C# .Net 3.5, winform (displaying graph) that needs to remain open as a front screen. I refresh the graph during the datagridview RowEnter event of the calling window. I open graph winform as show(), and use TopMost = true. Everything works fine till I open another application like Word; Graph window still remains in the front of Word.
Is there an application event gets triggered when another application opens on top of my application, when I can close the open graph screen. Or, please let me know if you have a suggestion about the different approach.
Look at Form.Deactivate Event. It is raised when the form loses focus and is no longer the active form. You can use this event to Close the form
Sorry but I can't get the sense of your question. You set TopMost=true and the next moment you want it to hide behind another application... ????
This solution method is 100% working, considering that the login form name is loginForm.
Simply create the following method:
private void hideLogin()
{
if (System.Windows.Forms.Application.OpenForms["loginForm"] != null)
{
System.Windows.Forms.Application.OpenForms["loginForm"].Hide();
}
}
Call this method through task:
Task HideLoginTask = new Task(hideLogin);
HideLoginTask.Start();

PostMessage to hidden form doesn't work the first time

I have a C# WinForms application that appears in the system tray. The application is hiding the main form on load:
private void MainForm_Load(object sender, System.EventArgs e)
{
Hide();
}
For the same main form, I've overriden the WndProc to catch a custom window message (that message is registered to Windows with the RegisterWindowMessage Win32 API call).
protected override void WndProc(ref Message m)
{
if (m.Msg == WM_MYCUSTOM_MESSAGE)
{
// Handle custom message
}
}
From an external C++ application I'm broadcasting the same custom window message with PostMessage.
UINT msg = RegisterWindowMessage(L"WM_MYCUSTOM_MESSAGE");
PostMessage(HWND_BROADCAST, msg, NULL, NULL);
When I execute the above code after the C# application was first started, it doesn't get into the WndProc. After the main form is shown (by a double click on the system tray icon, which in essence does a Show()), catching the broadcast message works and keeps working after hiding the form with Hide().
Can anyone explain me why it doesn't work after the first Hide() from the MainForm_Load event handler? Am I hiding the form too early?
EDIT1: It seems like it has nothing to do with the Hide on load. Even without the initial Hide, my main form WndProc will only start accepting broadcast posts after it is hidden and re-shown...
After creating a small test application, I have found out that PostMessage() to HWND_BROADCAST doesn't arrive in Form.WndProc if Form.ShowInTaskbar is set to false while SendMessage() to HWND_BROADCAST does.
Even though the MSDN note about sending or posting messages to HWND_BROADCAST is exactly the same.
So it had nothing to do with the hiding or showing of the form and this seems like another undocumented feature of the Windows API.
There's something else going on, calling Hide() in the form's Load event handler doesn't actually hide the window. Try it with a little test Winforms app to see this.
If you don't actually see the window then the simple explanation is that the window just wasn't created. Which entirely explains why the message isn't being received. Override OnHandleCreated() and set a breakpoint on it to double-check this theory. The Show() method must be called to create the native window, apparently your NotifyIcon event handler is the first one to do so. Which happens when you call Application.Run() but don't pass a form instance.
Check this answer for a way to ensure that that a form is created but not made visible.
With Broadcast, the message is posted to all top-level windows in the system, including disabled or invisible unowned windows, overlapped windows, and pop-up windows. The message is not posted to child windows (according to MSDN). Use an application, like WinSpy to make sure your windows is a top level one right after the initial startup and hiding. Also this http://blog.paulbetts.org/index.php/2010/07/20/the-case-of-the-disappearing-onload-exception-user-mode-callback-exceptions-in-x64/ might (or might not) give some insights on how the OnLoad works... Seems it's a little bit more complicated. Try to hide the MainForm somewhere else, not on the OnLoad.

Categories