I want to be able to prevent the screen saver from triggering while my app is in use by using the DisplayRequest class, but I only want to do this while it's the active app. If the user switches to another window/app then I want to act like a good citizen and allow the screensaver again.
I can't see an obvious way to detect when a UWP app gets/loses focus (or is activated/deactivated) and a quick search around doesn't offer any insights. Can anyone point me in the right direction?
It's actually quite simple:
Window.Current.Activated += Current_Activated;
private void Current_Activated(object sender, Windows.UI.Core.WindowActivatedEventArgs e)
{
if (e.WindowActivationState == CoreWindowActivationState.Deactivated)
{
// do stuff
}
else
{
// do different stuff
}
}
We can use CoreWindow.Activated event to detect when a UWP app is activated or deactivated. This method is fired when the window completes activation or deactivation. And for a UWP app, we usually only have one window. So we can add some code like following in Application.OnLaunched method to detect:
Window.Current.CoreWindow.Activated += (sender, args) =>
{
if (args.WindowActivationState == Windows.UI.Core.CoreWindowActivationState.Deactivated)
{
System.Diagnostics.Debug.WriteLine("Deactivated " + DateTime.Now);
}
else
{
System.Diagnostics.Debug.WriteLine("Activated " + DateTime.Now);
}
};
Besides this, you can also use CoreApplication.GetCurrentView method or CoreWindow.GetForCurrentThread method to get the CoreWindow.
You should not handle the changes of app's activation state.
Please, refer to How to prevent screen locks in your UWP apps.
Automatic request handling
Without any additional coding, though, your RequestActive() call will also be deactivated when your app no longer has the focus—in other words, when your app goes into a suspended state. On Windows Phone mobile and on a Windows 10 PC or Surface Pro in Tablet mode, “your app no longer having focus” means whenever your app is no longer in the foreground (snapped apps still count as being in the foreground, by the way). On a Windows 10 PC in desktop mode, however, it will mean whenever your app is minimized. In desktop mode, note that even when another app window covers up your app window, your app is still considered by the UWP app lifecycle to be running in the foreground.
The really cool thing is that when your app comes back into the foreground or gets de-minimized, the RequestActive() call will automatically get reactivated. You don’t have to do a thing!
This also means that if another app happens to make a request to keep the display active, it cannot hijack the behavior of every other app on the same device. The request is only good as long as the user is working with that app. Once the app is dismissed or minimized, Windows 10 goes back to its regular power conservation habits, even if the app forgets to call RequestRelease().
Finally, when your app is terminated, all remaining display active requests are automatically cleaned up for you.
It's all made for your needs.
I use the visibility changed event on the home window. The event is not fired when opening or closing a new window within the app.
Window.Current.VisibilityChanged += OnVisibilityChanged;
/// <summary>
/// When the window visibility changes, the stuff happens
/// </summary>
/// <param name="sender">object sender</param>
/// <param name="e">VisibilityChangedEventArgs e</param>
private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs e)
{
if (!e.Visible)
{
// do stuff
}
else
{
// do other stuff
}
}
Related
I'm having troubles understanding how can I execute code when doing fast app switching (i.e. pressing the Windows/Start button to show Start screen on the Phone emulator, and then pressing the Back button to go back into the app).
To simplify the issue, I started a new Windows Universal App that uses the "Visual C# Hub App (Universal Apps)" template as base code (since it includes the SuspensionManager and the NavigationHelper). Since I'm not interested in the Hub itself, I removed all the Grid content from the HubPage.xaml and simply added a TextBox called TimeTextBox:
...
<Grid x:Name="LayoutRoot">
<TextBox Name="TimeTextBox"/>
</Grid>
</Page>
Then, in the HubPage.xaml.cs, I added the following simple line to the method NavigationHelper_LoadState:
private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
var sampleDataGroups = await SampleDataSource.GetGroupsAsync();
this.DefaultViewModel["Groups"] = sampleDataGroups;
TimeTextBox.Text = DateTime.Now.TimeOfDay.ToString();
}
If I execute the app on the Phone emulator after applying those simple changes, the app will show the time of the day when loading the page, for example: 16:08:53.4390827.
What I want is that time to be updated every time I navigate to that page. But I if use the Lifecycle Events from Visual Studio to simulate a Suspend, when I send the Resume event the time is still the same: 16:08:53.4390827, and a breakpoint in that line will confirm that the NavigationHelper_LoadState method doesn't get executed when resuming.
The explanation for this is that the App.xaml.cs, as it is in the template, doesn't provide any listener for the Resume event, so nothing gets executed. Adding the next few lines fixes that:
public App()
{
this.InitializeComponent();
this.Suspending += this.OnSuspending;
this.Resuming += App_Resuming;
}
async void App_Resuming(object sender, object e)
{
await SuspensionManager.RestoreAsync();
}
So if I run again the app on the Phone emulator, now I get the actual time after resuming. Great! The problem is that these Suspend/Resume events don't get triggered when I simply tap on the Windows button of the Phone and then tap on the back button.
Actually, I haven't been able to identify one single method that gets executed when performing that kind of fast app switch. And that's the scenario I'm actually interested for my Universal App.
Is there a way of catching when the navigation brings us back into the app from the Start screen through the Back button? Am I missing any code to handle this scenario?
Thanks!
There's nothing wrong here. It's the default behavior. When you debug a Windows Phone 8.1 app from Visual Studio, the only way to trigger Suspend/Resume event is using the Lifecycle Events in VS. But when you run the app without VS, those methods will trigger as expected. :)
I am currently writing a running tracker, and I want it to be possible for the user to have my application running in the background.
Everything is fine when running it in the background, but whenever I re-open the app, it puts me back at the main menu.
In the end, I want access to the RootFrame.BackStack, so that the user can pick up where they left off.
I tried the following code in my App.xaml.cs but it threw a "InvalidOperationException" at the attempt to access RootFrame.BackStack.GetEnumerator().Current.
Note: I checked, and all values before Current are non-null.
private void Application_Activated(object sender, ActivatedEventArgs e)
{
JournalEntry j;
if (RootFrame.BackStack.GetEnumerator().Current != null)
j = RootFrame.BackStack.GetEnumerator().Current;
RunningInBackground = false;
}
What you're looking for is called Fast App Resume:
Windows Phone 8 introduces the ability for apps to request that user
actions that would typically relaunch the app, such as tapping the
app’s Start Tile, instead resume the suspended instance of the
suspended app instance, if one exists. This feature is called Fast
Resume.
To enable Fast Resume for your app, add the ActivationPolicy attribute to the DefaultTask element in WMAppManifest.xml and set the value to “Resume”.
<DefaultTask Name="_default" NavigationPage="MainPage.xaml" ActivationPolicy="Resume"/>
try this sample link. I hope this is what you are looking for. http://code.msdn.microsoft.com/wpapps/Fast-app-resume-backstack-f16baaa6
I have an app that doesn't show any forms most of the time, and I needed it to close by itself when a user was shutting down the system.
So I created a question, and the answer there, SystemEvents.SessionEnding, seemed to work.
At first.
Here is my Program.Main() method:
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
#region Pre-launch stuff
// Are we upgrading from an older version? We need to grab our old settings!
if (Properties.Settings.Default.UpgradeSettings)
{
Properties.Settings.Default.Upgrade();
Properties.Settings.Default.UpgradeSettings = false;
Properties.Settings.Default.Save();
}
// Visual styles
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
#if !DEBUG
// Add the event handler for handling UI thread exceptions to the event.
Application.ThreadException += new ThreadExceptionEventHandler(ErrorHandling.Application_ThreadException);
// Set the unhandled exception mode to force all Windows Forms errors to go through our handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
// Add the event handler for handling non-UI thread exceptions to the event.
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(ErrorHandling.CurrentDomain_UnhandledException);
#endif
// Since we have no forms open we need to watch for the shutdown event; otherwise we're blocking it
SystemEvents.SessionEnding += new SessionEndingEventHandler(SystemEvents_SessionEnding);
// ...
// ...
// ...
Application.Run();
}
The method SystemEvents_SessionEnding runs a method calling Exit(), which in turn runs:
public static void Exit()
{
MessageBox.Show("test");
try
{
IconHandler.SuperNotifyIcon.Dispose();
}
finally
{
Application.Exit();
}
}
And yet, my app blocks shutdown at times. And the message box that I added there doesn't show up.
I can't figure out how such a simple, simple path of execution could fail. But it does, and it's a major irritant for both me and my app's users.
Any ideas? Thoughts to throw around?
EDIT:
Per corvuscorax's answer, I've tried adding a form, but it's acted weird - at first, I had this in the form's code:
ShowInTaskbar = false;
WindowState = FormWindowState.Minimized;
Shown += (sender, e) => Hide();
And modified the Program.Exit() method to close that form, which would run the disposal code as well in the FormClosing event.
It turned out disabling ShowInTaskbar stopped the form from receiving a HWND_BROADCAST message. I've commented out that line now, but even so, I found that shutdown blocking was occurring. Once I clicked a button I made to show the form and tried to shut down again, it completed smoothly.
I would recommend against not having any forms at all.
Just do a normal WinForms application and set the main Form to be hidden and you get all the message processing working as always.
Addendum
As an added bonus of doing it this way, you also get a console for free while developing - by not hiding the main form, you can have an easy output view, simulate input, trigger actions with buttons, all sorts of useful stuff.
Addendum 2
Here's how I normally create invisible main forms. In the forms constructor, add this:
if (!Debugger.IsAttached)
{
// Prevent the window from showing up in the task bar AND when Alt-tabbing
ShowInTaskbar = false;
FormBorderStyle = FormBorderStyle.FixedToolWindow;
// Move it off-screen
StartPosition = FormStartPosition.Manual;
Location = new Point(SystemInformation.VirtualScreen.Right+10, SystemInformation.VirtualScreen.Bottom+10);
Size = new System.Drawing.Size(1, 1);
}
This will hide the window when not running from within the debugger.
Does your code have a message pump?
You need a message pump inside each user interface thread of your application so that any windows messages aimed at your application are dispatched and processed. Even if you never show a Form on the screen if the thread is a user interface thread when it will still be sent other system messages that would need to be dispatched and processed.
In a typical WinForms applications the message pump is inside this call...
Application.Run(new MyForm());
...which only exits once the MyForm instance is closed. In your case it seems you never make a call to the Application.Run method or anything similar. In which case your not handling windows messages. So what is your processing loop when you are not showing a Form?
My program puts an icon in the system tray because the user may minimize to it. However, if the application crashes, or I stop the app from running in VS it leaves the icon in it until I hover over it with the mouse. Sometimes I'll look down there and there will be 10 or so icons.
I can I make sure the icon goes away?
There is no way to do this. Windows does not poll your program to see if it's still alive. Therefore, your icon will stay in the system tray until you either tell it to leave (normal exit), or the user does something that initiates a call to your program (such as mouseover). Only then does the shell know that your program has died and it needs to clean up your icon.
You have several options:
1) Write a root structured exception handler that ensures the destruction of the icon before exit. This one takes some effort - but you can basically add your own unhandled exception handler to Windows which will be called in the case of many forms of "boom" which will then allow you some last-ditch clean up opportunity.
2) Write a monitor application that sleeps on your main processes handle, and kills the icon when it wakes up (the main application has died). To make this latter scenario work well, you'll likely want the main process to NOT kill the icon itself - or you could create a way to tell the monitor app that it should exit w/o destroying the icon (have the monitor app sleep on both the process handle and a named event - and if woken by the event then it simply dies w/o cleaning up after the main app).
Before you exit, set the NotifyIcon Visible property to false.
You need to call Dispose on the NotifyIcon for it to leave the tray.
EDIT: There's an event you can hook into in your Program.cs. For all Windows apps, you'll have something like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
The Application class has a few events you can take advantage of to know when your app dies:
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
I don't know what happens when one kills the process, but if it crashes due to an exception, of course one can handle it. The way to do it best, depends on the type of application: Console, Forms, a service, ...
But in all cases, you should be able to use a try / finally structure in your Program.cs, and in the 'finally' section Dispose() the TrayIcon.
For example, in a Forms application, make your NotifyIcon (called TrayIcon in my example below) in your Form class public, and change the "Application.Run(new Form1())" line in Program.cs as follows:
Form form = new Form1();
try { Application.Run(form); }
finally { form.TrayIcon.Dispose(); }
We can hide trayIcon before form closing.
Add FormClosing Event
NotifyIcon ni;
...
...
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
ni.Visible = false;
}
I'm new here and have a really mysterious problem to start off. I'm a software developer in the UK and have over 15 years of experience but have only been developing in .Net for 18 months. My NotifyIcon mouse events are not firing!
I am using C# to write an application that starts as a NotifyIcon ('main app icon') and displays a ContextMenu on mouse right-click. This works fine: ContextMenu, forms launching and mouse click events firing.
A tiny bit of background: the application is supposed to sense the insertion of a usb 'device' (it does), interrogate it and create another NotifyIcon ('device icon') to allow the user to interact with that device. The 'main app icon', mentioned in my previous paragraph, allows the user to interact with the database and to configure the software.
To encapsulate these device interaction functions, I have constructed a 'device class' that
contains the device NotifyIcon, ContextMenu, forms, mouse click events etc which allow interaction with the device.
The Problem
The problem starts when I instantiate my 'device class' from the ManagementEventWatcher EventArrived event. If I instantiate the 'device class' in my program Main then the events fire correctly when I click the notifyicon.
So, please, can someone help me?
Cheers, in advance
Matthew
IIRC, the event usage (rather than WaitForNextEvent) works async. I would be interested to know what thread the event is being raised on. I wonder if there is no message pump servicing messages for your icon.
Do you have a form anywhere? Or something else with a message loop? I would be tempted to call into the form (using Control.Invoke), and ask the form to show the icon - since then it should have an active message pump.
Sorry for the delay; reading your comments, it sounds like you've broadly got a workaround. The only gotcha is cross-threaded issues; ideally, you would ask the UI to make such a change on the UI thread; for example, if you have a Form kicking around (owning the icon) - add to your form class:
// not a property, as there is no need to add a complex x-thread "get"
public void SetIconVisible(bool isVisible) {
if(this.InvokeRequired) {
this.Invoke((MethodInvoker) delegate {
myIcon.Visible = isVisible;
});
} else {
myIcon.Visible = isVisible;
}
}
This does a thread-switch (if needed) to the UI thread. Any use?
So the answer is:
The events will only work if when you make the NotifyIcon visible, you do it in the main thread. So the code given by Marc Gravell is the solution.
Mark, just to let you know -
I worked out that I could create the class instances that have the NotifyIcon as members in the main thread and then make the NotifyIcon(s) visible when the USB device(s) are connected.
It needed a bit of adjustment though because the NotifyIcon is created when it's first made visible, so I had to make sure that (in the main thread) I set Visible to true then to false for each - giving rise to the need for limiting the number of instances.
The ManagementEventWatcher thread could then set the Visible property to true when the device gets connected.
A workaround.
(see replies to your comments)