|
Hi, OG, I think, but am not sure, the OP is looking to show another Form before the Main Form ... perhaps a log-in Form, or a splash-screen ... without activating the Main Form. That can be done using a custom ApplicationContext.
Hopefully, the OP will clarify what he's doing
cheers, Bill
«While I complain of being able to see only a shadow of the past, I may be insensitive to reality as it is now, since I'm not at a stage of development where I'm capable of seeing it. A few hundred years later another traveler despairing as myself, may mourn the disappearance of what I may have seen, but failed to see.» Claude Levi-Strauss (Tristes Tropiques, 1955)
|
|
|
|
|
Could be - difficult to tell with some of these.
How are you doing? Had your eyes fixed yet?
Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
AntiTwitter: @DalekDave is now a follower!
|
|
|
|
|
What I'm trying to do: I start my app from another app (XnView). My app gets a list of files from XnView. I want my app to start minimized and to not steal the focus from XnView.
|
|
|
|
|
I did that too. The form starts minimized but it still steals the focus from the current app.
|
|
|
|
|
|
I've seen that. Actually the code that I posted is from that site. Anyone could make a working test solution with just an empty form that is not activated when it's launched and send it to me?
|
|
|
|
|
The "override ShowWithoutActivation"-method works for me - on Windows 7.
For testing I made two projects, one Forms-Project, one Console-Project. The Console-Project is basically mimicking XnView starting your app.
Essentials of the Forms-Project:
static class Program
{
[STAThread]
static void Main(string[] args)
{
bool withoutActivation = false;
if (args.Length > 0 && !String.IsNullOrWhiteSpace(args[0]))
withoutActivation = args[0] == "1";
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1(withoutActivation));
}
}
public partial class Form1 : Form
{
private bool WithoutActivation;
public Form1(bool withoutActivation)
{
WithoutActivation = withoutActivation;
InitializeComponent();
label1.Text = WithoutActivation ? "without activation" : "with activation";
}
protected override bool ShowWithoutActivation
{
get
{
return WithoutActivation;
}
}
}
Essentials of the Console-Project:
class Program
{
static void Main(string[] args)
{
bool withoutActivation = false;
while (!Console.KeyAvailable)
{
Thread.Sleep(3000);
Process.Start("[path to forms-app.exe goes here]", withoutActivation ? "1" : "0");
withoutActivation = !withoutActivation;
}
}
}
Every second "invocation" of the forms-app steals the focus from the currently active app, every other one does not.
Edit: Just realized that I didn't do the "start minimized" part (it's late here..). When I do show it minimized (by inserting WindowState = FormWindowState.Minimized; after InitializeComponent(); ) then neither "invocation" steals the focus, regardless whether WithoutActivation is true or false. So either Windows 10 behaves differently there or you're doing something not quite right.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Thanks for help. The test is working. But I'm a beginner and I have troubles implementing this on my actual project. I'm using SingleInstanceController at Program.cs in order to prevent opening the same app twice (forced single instance). Because of that, the simple ShowWithoutActivation doesn't work.
This app works in 2 main stages:
1. The app starts minimized, then it reads the arguments (file paths), then it displays those arguments in a listbox, then it waits.
2. If it's started again, it gets other arguments (actually just one), rename the files from stage 1, then it quits.
Basically it's a file (picture) arranger: the user selects the files to be renamed (stage 1) and the target file after those files will be placed through auto-renaming (stage 2). Like this: FileArranger - Streamable[^] Each stage means a launch of this app.
I want this app to stay minimized and without focus during its whole "lifetime". Of course, except when the user activates it by clicking its taskbar button. I uploaded the whole project here: MEGA[^] This app has around 300 LOC (MainForm.cs + Program.cs). Please, could you modify it in order to start without focus? That would be very helpful.
modified 21-Oct-17 21:37pm.
|
|
|
|
|
You using WindowsFormsApplicationBase and its event StartupNextInstance to implement a "SingleInstanceController" is the important piece of information here.
The code in WindowsFormsApplicationBase that is calling your event handler ThisStartupNextInstance (which I would suggest renaming to something like "StartedAgain") is bringing your MainForm to the foreground. Unless you explicitly tell it not to by inserting this line into the event handler method:
e.BringToForeground = false; (Take a look at the tooltip of BringToForeground .)
I assume the line form.Hide(); there was an attempt to solve your problem; you probably want to remove it.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
modified 22-Oct-17 3:11am.
|
|
|
|
|
I tried that but the form still steals the focus. This is how Program.cs looks like now:
using System;
using System.Windows.Forms;
using System.Linq;
using Microsoft.VisualBasic.ApplicationServices;
namespace FileArranger
{
internal sealed class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string[] args = Environment.GetCommandLineArgs();
var controller = new SingleInstanceController();
controller.Run(args);
}
}
public class SingleInstanceController : WindowsFormsApplicationBase
{
public SingleInstanceController()
{
IsSingleInstance = true;
StartupNextInstance += StartedAgainInstance;
}
void StartedAgainInstance(object sender, StartupNextInstanceEventArgs e)
{
var form = MainForm as MainForm;
e.BringToForeground = false;
form.ReadArguments(e.CommandLine.ToArray());
}
protected override void OnCreateMainForm()
{
MainForm = new MainForm();
}
}
}
What should I modify next?
|
|
|
|
|
You also still have this bit in your Form, right?
protected override bool ShowWithoutActivation
{
get { return true; }
}
Please test the following: Start your app directly, without doing anything with XnView. For me, it's showing minimized in the taskbar and doesn't take focus. Start it again. For me, it remains minimized in the taskbar without focus. Does it behave the same for you this far?
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
"You also still have this bit in your Form, right?"
Yes.
"Please test the following: Start your app directly, without doing anything with XnView. For me, it's showing minimized in the taskbar and doesn't take focus. Start it again. For me, it remains minimized in the taskbar without focus. Does it behave the same for you this far?"
I did that. The app still gets the focus. If I press Enter after the app is launched it restores the form, hence it still has focus. This is by far the most stubborn app I did. Please, could you send me the project modified by you?
|
|
|
|
|
Well, this is awkward. For testing, I commented the ShowWithoutActivation -override. As expected, it did activate then. Then I removed the comments and it still activates. I don't know what's happening there.
However, I have another solution for you:
1) Go to this article and download the code: All Three Features of Single-Instance Applications at One Shot, .NET[^]
2) Copy the files IRemoteFileLoader.cs , Server.cs and SingleInstanceManager.cs to your project folder.
3) Include them into your project (Project > Add existing item)
4) Add a reference to the System.Runtime.Remoting -assembly to your project (Project > Add reference).
5) Replace the code of your Program.cs by this:
using System;
using System.Windows.Forms;
using SA.Universal.SingleInstance;
namespace FileArranger
{
internal sealed class Program
{
[STAThread]
static void Main(string[] commandLine)
{
if (SingleInstanceManager.IsSecondInstance)
{
SingleInstanceManager.HandleRemoteCommandLine(commandLine);
SingleInstanceManager.ActivateFirstInstance();
return;
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainForm());
}
}
} 6) Add a using-Directive to your MainForm.cs :
using SA.Universal.SingleInstance; 7) Add this to the constructor of MainForm :
SingleInstanceManager.FilesLoading += (sender, eventArgs) => {
Invoke(new System.Action(() => ReadArguments(eventArgs.CommandLine)));
};
That should do it.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
modified 22-Oct-17 9:04am.
|
|
|
|
|
Finally, the form starts without focus. But it doesn't get the second round of parameters. As I said, this app should get the parameters twice: once when it's started, and once when it's started again after the user selects the target file and pass that as a parameter. Basically the second instance should pass the parameters to the first one.
I suspect the issue is here:
if (SingleInstanceManager.IsSecondInstance)
{
SingleInstanceManager.HandleRemoteCommandLine(commandLine);
SingleInstanceManager.ActivateFirstInstance();
return;
}
The second instance should pass the command line arguments to the first instance. But how to do that?
modified 22-Oct-17 9:31am.
|
|
|
|
|
It does for me. Do you know how to use the debugger? Place a breakpoint on the opening brace of ReadArguments(..) (F9). Start your app in debugging mode (F5). Then do that XnView thing. The debugger should stop the execution of your app at the beginning of ReadArguments(..) . Then step through the execution line by line (F10 step over, F11 step into) and inspect the values of the variables and what your code is doing - and how that differs from your expectation of what it should be doing.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
alin1 wrote: I suspect the issue is here:
if (SingleInstanceManager.IsSecondInstance)
{
SingleInstanceManager.HandleRemoteCommandLine(commandLine);
SingleInstanceManager.ActivateFirstInstance();
return;
}
The second instance should pass the command line arguments to the first instance. But how to do that?
Nope, that's being taken care of by the code in the files you included into your project. That's all fine. See my other reply.
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Now the issue was in the ReadArguments function: previously args[0] was the app's path (got through Environment.GetCommandLineArgs()), but now it's not. I modified ReadArguments a little and the app finally works as intended. Thanks a lot for help!
|
|
|
|
|
You're welcome
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
Be sure to leave an upvote on the article where you downloaded the code from, too
If the brain were so simple we could understand it, we would be so simple we couldn't. — Lyall Watson
|
|
|
|
|
|
Member 12536513 wrote: Any ideas how to make it work? It shouldn't work.
If you are launching an application that does not put up a UI (ie, needs no immediate user-interaction), then you do not need to display a form. Display something in the System-Tray, and show a form only once you're ready to interact with the user.
Bastard Programmer from Hell
If you can't read my code, try converting it here[^]
|
|
|
|
|
This is a particular case where I want to have a form in background. The app has just a listbox with the files selected by the user and s/he could see the list just in case. Also this app works in 2 stages: when it's started it waits in background and when it's started again it does the processing and it closes automatically. So, it's important to have that form starting in background.
|
|
|
|
|
That doesn't make any sense.
What do you mean by "has just a listbox with the files selected by the user"? How is this functionality supposed to work? Where does this list of files come from and how does the listbox get populated? How is this app supposed to know how and when "files are selected by the user"?
How does the "when it's started again it does the processing" supposed to work?
This is not intuitive and goes against probably half of the UI and app design guidelines.
|
|
|
|
|
"Where does this list of files come from and how does the listbox get populated?"
The list is populated with the file names selected by the user in another app (XnView). These file names (complete paths) are passed as arguments.
"How is this app supposed to know how and when "files are selected by the user"?"
When it's launched it read the parameters. If there are parameters it means those are the files selected by the user.
"What do you mean by "has just a listbox with the files selected by the user"?"
The purpose of this app is renaming files in 2 stages: first the source (which files to rename) then the destination (after which file). Because it does its job in background in 2 stages, the user needs a visual prompt to know that he selected the files (that he finished the stage 1). Also if he forgets which files he selected in the first place, he could read the list. That's why I didn't hide the MainForm.
|
|
|
|
|