How to fix message boxes appearing behind splash screen? - c#

My WinForms app's main window is slow to load (up to 20 seconds, depending on arguments), so it needs a splash screen.
The main window constructor is slow because it exercises thousands of lines of code (some of it beyond my influence). Sometimes this code pops up message boxes.
I've tried two splash screen designs, they each have problems. Any better ideas?
Splash screen with BackgroundWorker
static void Main(string[] args)
{
var splash = !args.Contains("--no-splash");
if (splash)
{
var bw = new BackgroundWorker();
bw.DoWork += (sender, eventArgs) => ShowSplash();
bw.RunWorkerAsync();
}
var app = new FormMain(args); // slow. sometimes opens blocking message boxes.
Application.Run(app);
}
private static void ShowSplash()
{
using (var splash = new FormSplash())
{
splash.Show();
splash.Refresh();
Thread.Sleep(TimeSpan.FromSeconds(2));
}
}
Problems:
Splash screen sometimes expires before main window open (user thinks app has crashed)
Main window sometimes minimises when splash closes.
Splash screen with WindowsFormsApplicationBase
sealed class App : WindowsFormsApplicationBase
{
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new FormSplash();
}
protected override void OnCreateMainForm()
{
// slow. sometimes opens blocking message boxes.
this.MainForm = new FormMain(this.CommandLineArgs);
}
}
Problems:
Any MessageBoxes opened appear behind splash screen, silently. User won't notice it and thinks app is stuck.
If splash screen is 'always on top', the message box is inaccessible and unclickable.

I agree with Hans Passant in that the code needs to be re-evaluated as the design seems incorrect.
As for the problem at hand, you should be able to resolve this by creating your own instance of a messageBox.
I tested using this code;
public DialogResult TopMostMessageBox(string message, string title, MessageBoxButtons button, MessageBoxIcon icon)
{
return DisplayMessageBox(message, title, button, icon);
}
public DialogResult DisplayMessageBox(string message, string title, MessageBoxButtons buttons, MessageBoxIcon icon)
{
DialogResult result;
using (var topmostForm = new Form {Size = new System.Drawing.Size(1, 1), StartPosition = FormStartPosition.Manual})
{
var rect = SystemInformation.VirtualScreen;
topmostForm.Location = new System.Drawing.Point(rect.Bottom + 10, rect.Right + 10);
topmostForm.Show();
topmostForm.Focus();
topmostForm.BringToFront();
topmostForm.TopMost = true;
result = MessageBox.Show(topmostForm, message, title, buttons, icon);
topmostForm.Dispose();
}
//You might not need all these properties...
return result;
}
//Usage
TopMostMessageBox("Message","Title" MessageBoxButtons.YesNo, MessageBoxIcon.Question)
Again, I need to stress that I agree that the original code needs to be re-factored and am only providing a possible solution to the question.
Hope this helps?

You can implement our own message box and use TopMost property, with TopMost you will get message in-front of splash screen loader.
More about topmost: http://msdn.microsoft.com/en-us/library/system.windows.forms.form.topmost.aspx

In the end, moved the slow code from the constructor to a handler for the OnShown event.
Used WindowsFormsApplicationBase for splash screen as Hans Passant suggested, carefully checked the remaining constructor code to make sure it'll never open an message boxes.

Related

Check if window is minimized in UWP

I am developing a UWP application for Windows 10. I want to show a message dialog box when user reduces the size of the window. As of now, I have tried to implement the feature using this link. My purpose is being fulfilled here except the message dialog appears when user either minimizes the window or changes its size. I have also attached the code snippet below. Thank you.
private async void CoreWindow_SizeChanged(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.WindowSizeChangedEventArgs args)
{
var appView = ApplicationView.GetForCurrentView();
MessageDialog messageDialog = new MessageDialog("");
if (!appView.IsFullScreen)
{
if (!msgboxshown)
{
messageDialog = new MessageDialog("To run the application smoothly, please maximize the window.");
msgboxshown = true;
await messageDialog.ShowAsync();
msgboxshown = false;
}
}
args.Handled = true;
}
My purpose is being fulfilled here except the message dialog appears when user either minimizes the window or changes its size.
When app minimizes, it will invoke EnteredBackground event, you could detect it and show your message dialog.
public App()
{
this.InitializeComponent();
this.Suspending += OnSuspending;
this.EnteredBackground += App_EnteredBackground;
}
private async void App_EnteredBackground(object sender, EnteredBackgroundEventArgs e)
{
var messageDialog = new MessageDialog("To run the application smoothly, please maximize the window.");
await messageDialog.ShowAsync();
}
Update
As #Raymond Chen said, it looks os bug. So please use the above methods with caution.

How to properly display a splash screen

I want to display a splash screen in my app since I have to read some data on disk and customize the interface accordingly. If I didn't the effect would be that the interface is loaded and then customized, and the effect is clearly visible. So my idea is define a globla splash screen window and:
In the constructor.
WindowState = WindowState.Minimized; // <---- for the mainWindow
splashScreen.Show();
in the WindowViewBase_Loaded event
SetInterfaceElements(); // <-------interface customization (1)
splashScreen.Close();
WindowState = WindowState.Maximized; // (2)
Activate(); // <------------------------to put focus on
In the end the effect is always the same so a gap between (1) and (2).
So I thought about a refresh problem. I tried to force it with UpdateLayout but no luck. So from here another solution but always the same. Am I missing something??
What you need to do is to create a splash screen class and encapsulate all of its functions. Furthermore, you need to activate the splash screen through a thread, like this:
public static class SplashScreenView
{
public static Show()
{
Thread thread = new Thread(() =>
{
splashScreenView = new SplashScreenView();
....
}
// you code
thread.Start();
}
public static Close()
{
// close splash screen code
}
}
After that your code suppose to be like that:
SplashScreenView.Show();
// all your code
SplashScreenView.Close();
This way you don't need to maximize and minimize your window.
In the mainView constructor
public MainView()
{
SplashScreen splashScreen = new SplashScreen();
splashScreen.Show();
...
}
Then
Action emptyDelegate = delegate { };
bool IsContentRendered = false;
private void WindowViewBase_Loaded(object sender, RoutedEventArgs e)
{
SetInterfaceElements();
Dispatcher.Invoke(emptyDelegate, DispatcherPriority.Render);<---to refresh
IsContentRendered = true;
}
finally
private void WindowViewBase_ContentRendered(object sender, EventArgs e)
{
if (IsContentRendered)
{
if (splashScreen != null)
splashScreen.Close();
WindowState = WindowState.Maximized;
Activate();
}
}
Personally i would go with setting the Splash as the MainWindow on application initialization, doing the required loading in the loaded callback of the splash window and then opening + changing the actual MainWindow. That way you don't have to bother with threads/ui freezes.

Splash screen to show up in the middle of application

I have a list of report names displayed as Tree hierarchy in ReportViewer control. When user clicks on a report name, an input form loads, user enters some values and presses OK. At this point, Splash screen should load while the backend process is happening (connecting to DB, retrieving values etc). Once the report is loaded in Reportviewer editor, the splashscreen should close.
So far, I am able to display the splash screen however it gets stuck at that point, the actual report does not load and the splash screen stays on forever.
Is it possible to use splashscreen in the middle of application, not at app launch? If so, how do I continue with loading report?
static class Program
{
[STAThread]
static void Main(string[] args)
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new SnapPlusReports());
//new SplashScreenApp().Run(args);
}
}
public class SplashScreenApp : WindowsFormsApplicationBase
{
private static SplashScreenApp _application;
public static void Run(Form form)
{
_application = new SplashScreenApp { MainForm = form };
_application.Run(Environment.GetCommandLineArgs());
}
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new ShowProgress();
base.OnCreateSplashScreen();
}
}
I have done this before by making a new form at run time dynamically with code. Make sure you set all the options up, especially FormBorderStyle to none, or something like that so the user can't close it. Then simply manipulate labels that appear on that form, and eventually close it once your process is complete.
This way you don't have to worry about threading and a nice side effect is that the initial form won't be clickable.
For example I have an about form that pops up during run time (granted I don't change anything on it but the idea is there:
AboutForm aboutForm = new AboutForm();
aboutForm.StartPosition = FormStartPosition.CenterParent;
Label lblAbout = new Label();
Version applicationVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version;
lblAbout.Text = applicationVersion.ToString();
lblAbout.Location = new Point(145,104);
aboutForm.Controls.Add(lblAbout);
aboutForm.ShowDialog();
This shows the current programs version number, etc. There are other labels that already exist on the form (I created it visually first and then called an instance of it).
Hope this helps!
...Catch other instances and gracefully exit if you need only one copy of your app in memory at a given time
static void Main()
{
Application.EnableVisualStyles();
bool exclusive;
System.Threading.Mutex appMutex = new System.Threading.Mutex(true, "MY_APP", out exclusive);
if (!exclusive)
{
MessageBox.Show("Another instance of xxxx xxxBuilder is already running.","MY_APP",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation );
return;
}
Application.SetCompatibleTextRenderingDefault(false);
xxxWindowsApplication.InitializeApplication();
Application.Run(new frmMenuBuilderMain());
GC.KeepAlive(appMutex);
}
In the main form load you could do something like:
private void frmMenuBuilderMain_Load(object sender, EventArgs e)
{
//Show Splash with timeout Here--
if(!SystemLogin.PerformLogin())
{
this.Close();
return;
}
tmrLoad.Enabled = true;
}

System tray icon with c# Console Application won't show menu

I've got a small C# (.NET 4.0) Console Application that I'd like the user to be able to interact by showing a menu when they right-click the System Tray icon. I can add an icon to the Tray with no problems, but I just cannot get the menu to appear. I'm using the following code:
NotifyIcon trayIcon = new NotifyIcon();
trayIcon.Text = "TestApp";
trayIcon.Icon = new Icon(SystemIcons.Application, 40, 40);
ContextMenu trayMenu = new ContextMenu();
trayMenu.MenuItems.Add("Blah", item1_Click);
trayMenu.MenuItems.Add("Blah2", item1_Click);
trayMenu.MenuItems.Add("Blah3", item1_Click);
trayIcon.ContextMenu = trayMenu;
trayIcon.Visible = true;
... which puts the icon in the tray. However, right-clicking the icon does nothing. I've tried various permutations of MenuItems.Add, but nothing will make the menu appear. I'm sure I'm missing something simple - any ideas what?
Try adding this after you create the icon:
Application.Run()
Note that this method will not return, so you can't do anything after calling it. This means that you'll have to do all your other work in a separate thread.
What happens is that the OS sends your application a message telling it that the tray icon has been right-clicked, but the tray icon code never sees it (because these messages are processed by Application.Run) and so can't respond by opening the menu.
Concerning Application.Run(), this is an alternative to placing all the other code in another thread would be to create the NotifyIcon, menu, events, etc on a thread other than the main thread.
This should include Application.Run() as this allows the standard application message loop to work on the current thread. Then since the events were created on the same thread, the Application.Exit() can be used to close out the notification messaging but still allow the main thread to continue. Here's a small example for a console app...
class Program
{
public static ContextMenu menu;
public static MenuItem mnuExit;
public static NotifyIcon notificationIcon;
static void Main(string[] args)
{
Thread notifyThread = new Thread(
delegate()
{
menu = new ContextMenu();
mnuExit = new MenuItem("Exit");
menu.MenuItems.Add(0, mnuExit);
notificationIcon = new NotifyIcon()
{
Icon = Properties.Resources.Services,
ContextMenu = menu,
Text = "Main"
};
mnuExit.Click += new EventHandler(mnuExit_Click);
notificationIcon.Visible = true;
Application.Run();
}
);
notifyThread.Start();
Console.ReadLine();
}
static void mnuExit_Click(object sender, EventArgs e)
{
notificationIcon.Dispose();
Application.Exit();
}
}
Here is the solution:
You have to use Application.Run() because events of gui in console mode not working.
But you can use this solution:
var task = System.Threading.Tasks.Task.Factory.StartNew(() => ShowTrayIcon());
void ShowTrayIcon()
{
some code with tray icon ...
}
This will start your setup of try icon in new thread ...
Did you add the event-handler for tray Icon mouse click?
trayIcon .MouseDown += new MouseEventHandler(trayIcon_MouseDown);
create context menu and do as following inside the trayIcon_MouseDown function
private void trayIcon_MouseDown (object sender,MouseEventArgs e)
{
//add you menu items to context menu
contextMenu.Items.Add(item);
contextMenu.IsOpen = true;
}
Try this. Think this will help you.

Delaying Window for Splash Screen

I am developing a project and I want to add to it a splash screen. I have checked questions here on Stackoverflow and other blogs and MSDN etc. but could not find the solution I am looking for.
I want my SplashScreen,
1- appear and stay on the screen 3-4 seconds but at the same time I want my Main Window NOT TO appear. When my splash screen fades out completely then Main Window should appear. Many of the examples I have checked out do not implement this. Even though I set SplashScreen.Close.(TimeSpan.FromMiliseconds(4000)) MainWindow still apeear immediately front or back of SplashScreen. They say "add an image to your project, make it's Build Action SplashScreen or Resource, if you want to handle fade out time go App.xaml.cs file and implement your own Main() method and put your logic." I know that already. It does not work.
2- If possible I want my splashscreen NOT TO fade out slowly. I want it to disappear suddenly.(if this is not possible or really hard for a intermediate developer to do it, it is ok. you may disregard.)
And please I want C# code not Xaml. My project is based on WPF adn .NET 4.0 client profile.
Thank you.
Why don't you make your splash screen a fully qualified XAML <window> and in your App.xaml set it up as your StartupUri:
<Application x:Class="MyApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="SplashWindow.xaml">
Then in your splash window's Load Event you initialize the main window (preferably somewhere else so the instance sticks around when you close the splash). From here you can also specify a timer for x-seconds to go off and show the main window / hide the splash window.
using System.Threading;
/// -------------------------------
private Timer t;
private void Window_Loaded(object sender, RoutedEventArgs e)
{
App.MainWindow = new MainWindow(); // Creates but wont show
t = new Timer(new TimerCallback(CloseSplash), null, new TimeSpan(0,0,10), new TimeSpan(0,0,0,0,-1));
// Do Other load stuff here?
}
private void CloseSplash(object info)
{
// Dispatch to UI Thread
Dispatcher.Invoke(DispatcherPriority.Normal, x => CloseSplashMain());
}
private void CloseSplashMain()
{
App.MainWindow.Show()
this.Close();
}
You'll have to change your app's main window behaviour though, otherwise closing the splash window will cause the app to close.
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
App.Current.ShutdownMode = ShutdownMode.OnLastWindowClose;
}
}
Also don't forget to dispose your timer when you're done. It's an IDisposable and will keep firing that method unless it's stopped.
There are answers but I find a an easier one. Just use the Thread.Sleep(int miliSeconds) method in the main window's constructor. This will delay your app in order to open specified miliseconds later.
In the constructor of App.xaml.cs open your splash screen, wait for a few seconds, then close before proceeding with rest of the app. I am using Unity, so I close the splash screen somewhere after the Boostrapper has initialized some services.
public partial class App : Application
{
private static SplashScreen _splashScreen;
public App()
{
OpenSplashScreen();
new Bootstrapper().Run();
}
private void OpenSplashScreen()
{
_splashScreen = new SplashScreen("SplashScreen/splash.jpg");
_splashScreen.Show(false);
}
internal static void CloseSplashScreen(double time)
{
_splashScreen.Close(TimeSpan.FromSeconds(0));
_splashScreen = null;
}
}
where Bootstrapper.cs is listed below:
public class Bootstrapper : UnityBootstrapper
{
protected override void ConfigureContainer()
{
base.ConfigureContainer();
var section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
section.Configure(Container);
// initialize some services before
App.CloseSplashScreen(0);
}
protected override IModuleEnumerator GetModuleEnumerator()
{
return new ExtendedConfigurationModuleEnumerator();
}
protected override DependencyObject CreateShell()
{
MainWindow shell = new MainWindow(Container);
shell.Show();
return shell;
}
}
The best way and using the API is
SplashScreen splash = new SplashScreen("splashscreen.jpg");
splash.Show(false);
splash.Close(TimeSpan.FromMilliseconds(2));
InitializeComponent();

Categories

Resources