Minimized WinForms app restoring to Normal on second launch - c#

First of all, I want to explain the "second launch": I use the SingleInstanceController approach to make it possible to call my app's EXE file, and accept arguments.
That way other apps, or users can tell the application to take a specific action.
The app is set to start with a WindowState of Minimized, and only if the user clicks the tray icon it restores to Normal.
But what I'm seeing is, that the first time I launch the application it stays minimized. Then when I call the EXE file for the second time, it restores to a normal window state.
I have no code that alters the window state.
I suspect this is because something else is triggering the restore.
The code of my SingleInstanceController looks like this:
public class SingleInstanceController : WindowsFormsApplicationBase
{
public SingleInstanceController()
{
IsSingleInstance = true;
StartupNextInstance += this_StartupNextInstance;
}
void this_StartupNextInstance(object sender, StartupNextInstanceEventArgs e)
{
Form1 form = MainForm as Form1;
string command = e.CommandLine[1];
switch (command.ToLowerInvariant())
{
case "makecall":
string phoneNumber = e.CommandLine[2];
PhoneAppHelper.MakePhoneCall(phoneNumber);
break;
default:
System.Windows.Forms.MessageBox.Show("Argument not supported");
break;
}
}
protected override void OnCreateMainForm()
{
MainForm = new Form1();
}
}
On my form, I have a listbox to show connected devices (USB), and a multiline textbox to show some activity, most for debug/information purposes.
Could the interaction with the controls on the form cause the restore?

Yes, this is the default behavior for WindowsFormsApplicationBase.OnStartupNextInstance(). You can simply fix that by overriding the method instead of using the event. Do note that you probably still want this to happen when you have a message to display. So make it look similar to this:
protected override void OnStartupNextInstance(StartupNextInstanceEventArgs e) {
//...
switch (command.ToLowerInvariant()) {
// etc..
default:
base.OnStartupNextInstance(e); // Brings it to the front
System.Windows.Forms.MessageBox.Show("Argument not supported");
break;
}
}

Related

Display my forms in another class

I recently developped an application for a student project, and everything works fine. However, if I'm asking something here, you can certainly assume that the whole thing isn't so functional...and you'd be right hohoho. Let's get started. Basically the functional version of my project uses an UI console.
It runs, but from an user point of view, working with something like this isn't the most amazing stuff in the world. So I decided to replace my old console by some Windows Forms.
Project global render with console
Project global render with form
This is where things become wrong. I have "3" classes :
Program.cs (the main program with all the serious stuff)
formOne.cs (the first form with a button for each options)
form/Two to Five/.cs (each options open the corresponding form)
At some point, Program.cs will launch formOne.cs, and from there the user should be able to navigate between the various options and so the various forms...but nope. formOne.cs opens, and then we can't click on anything (well we can but nothing happens). I did a mistake somewhere, I would like to know where and how to fix it. Here's how I proceeded :
(this is the basic algorithm, not the whole code)
Program.cs :
class Program {
formOne winRecep = new formOne();
formTwo winCrea = new formTwo();
formThree winSearch = new formThree();
formFour winDel = new formFour();
formFive winView = new formFive();
winRecep.ShowDialog();
string userChoice = winRecep.getUserChoice();
switch(userChoice){
case "create new task" :
winCrea.ShowDialog();
break;
case "search a task" :
winSearch.ShowDialog();
break;
case "delete a task" :
winDel.ShowDialog();
break;
case "view my tasks" :
winView.ShowDialog();
break;
}
}
formOne.cs :
class formOne {
string userChoice;
public formOne()
{
InitializeComponent();
}
public string getUserChoice()
{
return userChoice;
}
private void formOne_Load(object sender, EventArgs e)
{
//blabla update current date, current hour...
}
private void buttonOptionOne_Click(object sender, EventArgs e)
{
userChoice = "create new task";
}
private void buttonOptionTwo_Click(object sender, EventArgs e)
{
userChoice = "search a task";
}
private void buttonOptionThree_Click(object sender, EventArgs e)
{
userChoice = "delete a task";
}
private void buttonOptionFour_Click(object sender, EventArgs e)
{
userChoice = "view my tasks";
}
}
It seems pretty clear to me, but I did a mistake somewhere. I would like to work in Program.cs instead of formOne.cs because, well, everything is in Program.cs so the most logical way to proceed is certainly to work here instead of bring informations and variables everywhere accross the classes.
Could someone help me and explain why it's wrong ?
EDIT : Program.cs using Application doesn't solve the thing :
class Program {
var winRecep = new formOne();
var createtask = new formTwo();
var viewTask = new formThree();
var searchTask = new formFour();
var deleteTask = new formFive();
Application.Run(winRecep);
string userChoice = winRecep.getUserChoice();
switch(userChoice){
case "create new task" :
Application.Run(createtask);
break;
case "search a task" :
Application.Run(searchTask);
break;
case "delete a task" :
Application.Run(deleteTask);
break;
case "view my tasks" :
Application.Run(viewTask);
break;
}
}
You need to start your main form like this:
var mainForm = new formOne();
Application.Run(mainForm);
This starts the windows message loop. Without a message loop, your application can't respond to any events (such as mouse clicks or keypresses).
The first thing you should do, is opening the main form using Application.Run, as already suggested. This starts the message loop which is essential for the rest of the execution:
var mainForm = new formOne();
Application.Run(mainForm);
Then use that form to open all others. Closing the form started with Application.Run will close the application since the message loop will end too. So on any action, like a button click or a command written, open the form you need. You can call Show to open the other form and make it possible to still access the main form, or ShowDialog which will block further actions until the child form was closed.

ManagementObjectSearcher causes re-entrancy issues for onclick handler

I am having an odd problem with protecting a section of code. My application is a tray app. I create a NotifyIcon inside my class (ApplicationContext). I have assigned a balloon click handler and a double click handler to the NotifyIcon object. there is also a context menu but I am not showing all code. Only important pieces.
public class SysTrayApplicationContext: ApplicationContext
{
private NotifyIcon notifyIcon;
private MainForm afDashBoardForm;
public SysTrayApplicationContext()
{
this.notifyIcon = new NotifyIcon();
this.notifyIcon.BalloonTipClicked += notifyIcon_BalloonTipClicked;
this.notifyIcon.MouseDoubleClick += notifyIcon_MouseDoubleClick;
// ... more code
}
Both handlers launch or create/show my form:
private void notifyIcon_MouseDoubleClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
openDashboard();
}
}
private void notifyIcon_BalloonTipClicked(object sender, EventArgs e)
{
openDashboard();
}
private void openDashboard()
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
There is a problem with the above code. Maybe more than 1. Issue: it is possible to display 2 dashboard forms which is not what I want. If user double clicks on tray icon while balloon message is displaying causes a race condition in openDashboard. I can reproduce this easily. So I added a lock around the code in openDashboard code and, to my surprise, that did NOT prevent 2 dashboard forms from displaying. I should not be able to create 2 MainForms. Where am I going wrong here?
here is the updated code with lock statement:
private void openDashboard()
{
lock (dashBoardFormlocker)
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
else
{
log.Debug("Dashboard form does not exist, create it");
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
}
Note: lock object was added to the class and initialized in constructor.
private object dashBoardFormlocker;
UPDATE: Showing more code. this is how code gets started :
static void Main()
{
if (SingleInstance.Start())
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
XmlConfigurator.Configure();
// For a system tray application we don't want to create
// a form, we instead create a new ApplicationContext. The Run method takes
Application.Run(new SysTrayApplicationContext());
SingleInstance.Stop();
SingleInstance.Dispose();
}
}
}
UPDATE 2: Provide more code for clarity
public partial class MainForm : Form
{
public MainForm()
{
log.Trace("MainForm constructor...");
InitializeComponent();
// ... code not shown
this.label_OSVersion.Text = getOSFriendlyName();
// .. more code
}
private string getOSFriendlyName()
{
try
{
string result = string.Empty;
var mgmtObj = (from x in new ManagementObjectSearcher("SELECT Caption FROM Win32_OperatingSystem").Get().OfType<ManagementObject>()
select x.GetPropertyValue("Caption")).FirstOrDefault();
result = mgmtObj != null ? mgmtObj.ToString() : string.Empty;
OperatingSystem os = Environment.OSVersion;
String sp = os.ServicePack ?? string.Empty;
return !string.IsNullOrWhiteSpace(result) ? result + sp : "Unknown";
}
catch (System.Exception ex)
{
log.Error("Error trying to get the OS version", ex);
return "Unknown";
}
}
}
The main UI thread must always pump a message loop to support communication from COM components.
So when you do a blocking operation from the UI thread like locking or joining a thread, (EDIT: edited based on Peter Duniho's fix) the UI thread will enter an 'alertable' state, allowing COM to dispatch certain type of messages, which in turn can cause re-entrancy issues like in your scenario.
Look at the answer to this question (Why did entering a lock on a UI thread trigger an OnPaint event?) for a much more accurate explanation.
Looking at the source code of ManagementObjectSearcher.Get there is a lock (inside Initialize), and since you call it from the constructor of your form, it may lead to the second event triggering while the form's constructor has not finished. The assignment to the dashBoardFormlocker variable only happens after the constructor finishes, so that would explain why it was null on the second entry.
The moral of the story is never do blocking operations on the UI thread.
Without a good, minimal, complete code example that reliably reproduces the problem, it's impossible to know for sure what the problem is. But the guess by answerer tzachs seems reasonable. If so, you can fix your problem by changing your method to look like this:
private bool _dashboardOpen;
private void openDashboard()
{
if (_dashboardOpen)
{
if (dashBoardForm != null)
{
log.Debug("Dashboard form created already, so Activate it");
dashBoardForm.Activate();
}
}
else
{
log.Debug("Dashboard form does not exist, create it");
_dashboardOpen = true;
dashBoardForm = new MainForm();
dashBoardForm.Show();
}
}
In that way, any re-entrant attempt to open the window will be detected. Note that you still need the check for null before actually activating; you can't activate a window that hasn't actually finished being created yet. The subsequent call to Show() will take care of activation anyway, so ignoring the activation in the re-entrant case shouldn't matter.

How to move wpf application into minimize tray at Window Start-up C#?

I have created setup of my application using Windows Installer.
Now I want to Start application at Windows Start-Up and move it system minimize tray as i don't want to display GUI(View) at Windows Start-Up.
I have searched in Google and i found to use Registry key But that is not enough for me as i also want to move to system minimize tray and application run.
My purpose to do it is, user do not feels annoying when application starts every time when he/she starts system.
Can anyone have answer?
Thanks..
In your application, add an event handler for the FrameworkElement.Loaded event. In that handler, add the following code:
WindowState = WindowState.Minimized;
This will minimise the application when it starts.
To start the application when the computer starts, you'll need to add your program into Windows Scheduler and set it to run at startup. You can find out more on the Schedule a task page at MSDN.
You also have to set this property to remove it from the taskbar
ShowInTaskbar= false;
Maybe this answer is late, but I still want to write it down to help those who haven't found solutions yet.
Firstly you need to add a function to minimize your app to tray when it autostarts as system startup.
In your App.xaml file, change the original StartupUri=... to Startup="App_Startup" as below. App_Startup is your function name and can be changed.
<Application x:Class="Yours.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="App_Startup">
In your App.xaml.cs file. Add the function below:
public partial class App : Application
{
private void App_Startup(object sender, StartupEventArgs e)
{
// Process command line args
var isAutoStart = false;
for (int i = 0; i != e.Args.Length; ++i)
{
if (e.Args[i] == "/AutoStart")
{
isAutoStart = true;
}
}
// Create main application window, starting minimized if specified
MainWindow mainWindow = new MainWindow();
if (isAutoStart)
{
mainWindow.WindowState = WindowState.Minimized;
}
mainWindow.OnAutoStart();
}
}
In your MainWindow.xaml.cs, add a function as below:
public void OnAutoStart()
{
if (WindowState == WindowState.Minimized)
{
//Must have this line to prevent the window start locatioon not being in center.
WindowState = WindowState.Normal;
Hide();
//Show your tray icon code below
}
else
{
Show();
}
}
Then you should set you app utostart as system start.
Now if you have a switch to decide whether you app to autostart as system start, you can just add the function below as your switch status changed event function.
private void SwitchAutoStart_OnToggled(object sender, RoutedEventArgs e)
{
const string path = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Run";
var key = Registry.CurrentUser.OpenSubKey(path, true);
if (key == null) return;
if (SwitchAutoStart.IsOn)
{
key.SetValue("Your app name", System.Reflection.Assembly.GetExecutingAssembly().Location + " /AutoStart");
}
else
{
key.DeleteValue("Your app name", false);
}
}
If you want to automatically start the application for all users on Windows startup, just replace the forth line with
RegistryKey key = Registry.LocalMachine.OpenSubKey(path, true);
^_^

Hide form instead of closing when close button clicked

When a user clicks the X button on a form, how can I hide it instead of closing it?
I have tried this.hide() in FormClosing but it still closes the form.
Like so:
private void MyForm_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
}
}
(via Tim Huffman)
I've commented in a previous answer but thought I'd provide my own. Based on your question this code is similar to the top answer but adds the feature another mentions:
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
}
}
If the user is simply hitting the X in the window, the form hides; if anything else such as Task Manager, Application.Exit(), or Windows shutdown, the form is properly closed, since the return statement would be executed.
From MSDN:
To cancel the closure of a form, set the Cancel property of the FormClosingEventArgs passed to your event handler to true.
So cancel then hide.
Based on other response, you can put it in your form code :
protected override void OnFormClosing(FormClosingEventArgs e)
{
base.OnFormClosing(e);
if (e.CloseReason == CloseReason.UserClosing)
{
e.Cancel = true;
Hide();
}
}
According MSDN, the override is preferred:
The OnFormClosing method also allows derived classes to handle the
event without attaching a delegate. This is the preferred technique
for handling the event in a derived class.
If you want to use the show/hide method I've actually done this myself for a menu structure a game I've recently done... This is how I did it:
Create yourself a button and for what you'd like to do, for example a 'Next' button and match the following code to your program. For a next button in this example the code would be:
btnNext.Enabled = true; //This enabled the button obviously
this.Hide(); //Here is where the hiding of the form will happen when the button is clicked
Form newForm = new newForm(); //This creates a new instance object for the new form
CurrentForm.Hide(); //This hides the current form where you placed the button.
Here is a snippet of the code I used in my game to help you understand what I'm trying to explain:
private void btnInst_Click(object sender, EventArgs e)
{
btnInst.Enabled = true; //Enables the button to work
this.Hide(); // Hides the current form
Form Instructions = new Instructions(); //Instantiates a new instance form object
Instructions.Show(); //Shows the instance form created above
}
So there is a show/hide method few lines of code, rather than doing a massive piece of code for such a simple task.
I hope this helps to solve your problem.
Note that when doing this (several answers have been posted) that you also need to find a way to ALLOW the user to close the form when they really want to. This really becomes a problem if the user tries to shut down the machine when the application is running, because (at least on some OS) this will stop the OS from shutting down properly or efficiently.
The way I solved this was to check the stack trace - there are differences between when the user tries to click the X vs when the system tries to end the application in preparation for shutdown.
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
StackTrace trace = new StackTrace();
StackFrame frame;
bool bFoundExitCommand = false;
for (int i = 0; i < trace.FrameCount; i++)
{
frame = trace.GetFrame(i);
string methodName = frame.GetMethod().Name;
if (methodName == "miExit_Click")
{
bFoundExitCommand = true;
Log("FormClosing: Found Exit Command ({0}) - will allow exit", LogUtilityLevel.Debug3, methodName);
}
if (methodName == "PeekMessage")
{
bFoundExitCommand = true;
Log("FormClosing: Found System Shutdown ({0}) - will allow exit", LogUtilityLevel.Debug3, methodName);
}
Log("FormClosing: frame.GetMethod().Name = {0}", LogUtilityLevel.Debug4, methodName);
}
if (!bFoundExitCommand)
{
e.Cancel = true;
this.Visible = false;
}
else
{
this.Visible = false;
}
}
This is the behavior of Modal forms. When you use form.ShowDialog() you are asking for this behavior. The reason for this is that form.ShowDialog doesn't return until the form is hidden or destroyed. So when the form is hidden, the pump inside form.ShowDialog destroys it so that it can return.
If you want to show and hide a form, then you should be using the Modeless dialog model
http://msdn.microsoft.com/en-us/library/39wcs2dh(VS.80).aspx
form.Show() returns immediately, you can show and hide this window all you want and it will not be destroyed until you explicitly destroy it.
When you use modeless forms that are not children of a modal form, then you also need to run a message pump using Application.Run or Application.DoEvents in a loop. If the thread that creates a form exits, then the form will be destroyed. If that thread doesn't run a pump then the forms it owns will be unresponsive.
Edit: this sounds like the sort of thing that the ApplicationContext is designed to solve. http://msdn.microsoft.com/en-us/library/system.windows.forms.applicationcontext.aspx
Basically, you derive a class from ApplicationContext, pass an instance of your ApplicationContext as an argument to Application.Run()
// Create the MyApplicationContext, that derives from ApplicationContext,
// that manages when the application should exit.
MyApplicationContext context = new MyApplicationContext();
// Run the application with the specific context.
Application.Run(context);
Your application context will need to know when it's ok to exit the application and when having the form(s) hidden should not exit the application. When it's time for the app to exit. Your application context or form can call the application context's ExitThread() method to terminate the message loop. At that point Application.Run() will return.
Without knowing more about the heirarchy of your forms and your rules for deciding when to hide forms and when to exit, it's impossible to be more specific.

Any way to create a hidden main window in C#?

I just want a c# application with a hidden main window that will process and respond to window messages.
I can create a form without showing it, and can then call Application.Run() without passing in a form, but how can I hook the created form into the message loop?
Is there another way to go about this?
Thanks in advance for any tips!
Excellent! That link pointed me in the right direction. This seems to work:
Form f = new Form1();
f.FormBorderStyle = FormBorderStyle.FixedToolWindow;
f.ShowInTaskbar = false;
f.StartPosition = FormStartPosition.Manual;
f.Location = new System.Drawing.Point(-2000, -2000);
f.Size = new System.Drawing.Size(1, 1);
Application.Run(f);
To keep it from showing up in Alt-Tab, you need it to be a tool window. Unfortunately, this prevents it from starting minimized. But setting the start position to Manual and positioning it offscreen does the trick!
In the process of re-writing a VC++ TaskTray App, in C# .NET, I found the following method truly workable to achieve the following.
No initial form dislayed at startup
Running Message Loop that can be used with Invoke/BeginInvoke as needed as IsWindowHandle is true
The steps I followed:
Used an ApplicationContext in Application.Run() Instead of a form. See http://www.codeproject.com/Articles/18683/Creating-a-Tasktray-Application for the example I used.
Set the Form's ShowInTaskbar property to true within the GUI Designer. (This seems counter productive but it works)
Override the OnLoad() method in your Form Class setting Visible and ShowInTaskbar to false as shown below.
protected override void OnLoad(EventArgs e)
{
Visible = false;
ShowInTaskbar = false;
base.OnLoad(e);
}
I know this is old question, but it ranks well in google, so I will provide my solution anyway.
I do two things:
private void Form_Load(object sender, EventArgs e)
{
Opacity = 0;
}
private void Form_Shown(object sender, EventArgs e)
{
Visible = false;
Opacity = 100;
}
The best way is to use the following 1-2 lines in the constuctor:
this.WindowState = FormWindowState.Minimized;
this.ShowInTaskbar = false; // This is optional
You can even set the Minimized property in the VS Property window.
You can create a class that inherits from System.Windows.Forms.NativeWindow (which provides basic message loop capability) and reference the Handle property in its constructor to create its handle and hook it into the message loop. Once you call Application.Run, you will be able to process messages from it.
I solved the problem like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Main main = new Main();
Application.Run();
//Application.Run(new Main());
}
This code resides in the Program.cs file, and you can see the original Application.Run method call commented out. I just create a Main class object (my main form class is named Main) and start application message loop w/o any parameters. This starts the application, initializes any form components but doesn't show the form.
Note: you have to have some method to get your window showing (like system tray icon, hotkey or timer or anything you might like).
public partial class Form1 : Form
{
private bool _isApplicationRun;
public Form1(bool applicationRun)
{
InitializeComponent();
_isApplicationRun = applicationRun;
}
protected override void SetVisibleCore(bool value)
{
if (_isApplicationRun)
{
_isApplicationRun = false;
base.SetVisibleCore(false);
return;
}
base.SetVisibleCore(value);
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.Run(new Form1(true));
}
}
Why can't you just pass the form when you call Application.Run? Given that it's clearly a blocking call, on what event do you want to show the form? Just calling form.Show() should be enough.
Using Kami's answer as an inspiration, I created a more complete concept. If you use this solution, don't ever show the hidden window. If you do, the user might close it and then you've lost the ability to control the application exit in an orderly way. This approach can be used to manage a Timer, NotifyIcon, or any other component that is happy living on an invisible form.
using System;
using System.Windows.Forms;
namespace SimpleHiddenWinform
{
internal class HiddenForm : Form
{
private Timer _timer;
private ApplicationContext _ctx;
public HiddenForm(ApplicationContext ctx)
{
_ctx = ctx;
_timer = new Timer()
{
Interval = 5000, //5 second delay
Enabled = true
};
_timer.Tick += new EventHandler(_timer_Tick);
}
void _timer_Tick(object sender, EventArgs e)
{
//tell the main message loop to quit
_ctx.ExitThread();
}
}
static class Program
{
[STAThread]
static void Main()
{
var ctx = new ApplicationContext();
var frmHidden = new HiddenForm(ctx);
//pass the application context, not the form
Application.Run(ctx);
}
}
}
Form1 f1=new Form1();
f1.WindowState = FormWindowState.Minimized;
f1.ShowInTaskbar = false;
in the Form1 code file add this.Visible = false; to the constructor.
This will hide the window but it will flash for a sec before it is hidden. Alternatively you can write your own Application.Run command.
for more info http://social.msdn.microsoft.com/forums/en-US/winforms/thread/dece45c8-9076-497e-9414-8cd9b34f572f/
also you may want to set the this.ShowInTaskbar to false.
You should look at creating a 'service' as this is an application without a form.
See http://support.microsoft.com/kb/816169

Categories

Resources