Mobility

Make Your WPF Apps Power-Aware

Andre Michaud

This article discusses:

  • Creating power-aware applications
  • Power support on Windows XP
  • Power support on Windows Vista
  • Custom WPF dependency properties and routed events
This article uses the following technologies:
.NET Framework 3.0, Windows Vista, Windows XP

Code download available at: Power Aware 2007_07.exe(360 KB)

Contents

Existing Work
Overview of the Code
Notification of System Power Changes
PowerModeChanged Event and Handler
Trapping the WM_POWERBROADCAST Window Message
Registering for Notifications on Windows Vista
Detecting the Operating System
Custom Power Dependency Properties
Custom Power Events
Windows XP Power Structs
Initializing the Dependency Properties on Windows XP
Windows Vista Power Structs
How the Message Is Handled
Using the Dependency Properties and Events
Controlling an Animation
Conclusion

As mobile computers become increasingly prevalent, battery life has become more important. Hardware manufacturers are aware of this and design mobile devices with battery consumption in mind. But can the same be said for your software? If not, maybe you should begin to think about coding software that is power-aware. Both Windows® XP and Windows Vista™ provide system information to enable your applications to do just that. In this article, I provide a starting point from which developers can understand power awareness in their Windows Presentation Foundation applications.

First, you must understand that Windows Presentation Foundation itself does not inherently make power notifications available to a would-be power-aware application. You’ll see that for this functionality, my sample relies on the operating system—either Windows XP or Windows Vista (the operating systems on which Windows Presentation Foundation is designed to run).

Before I show you how to get this information into a Windows Presentation Foundation application, I should note that the power notification system on Windows Vista is more evolved than that on Windows XP. Windows Vista contains all the functionality of the Windows XP power notification system and then some, which can present quite a challenge. What are the notification systems, how do they differ, and how do you manage these differences to design a successful power-aware application? These are especially important questions in light of the fact that Windows Presentation Foundation applications are meant to run on both Windows XP and Windows Vista.

Existing Work

There are two excellent hands-on labs designed for those who are looking for sample code and hands-on experience with the Windows XP and Windows Vista power notification systems. These labs can be found at (for Windows XP) and (for Windows Vista). The labs cover the power notification systems as they would be used in managed code; however, they don’t represent how the power notification systems would be used specifically in Windows Presentation Foundation. Still, these labs provide an excellent starting point and cover the notification systems as a whole in greater detail than I can do here. I recommend you use them in conjunction with this article.

Another important point is that every application will have its own unique concerns in regard to power consumption. Not every application will perform 3D animation or write data to disk when the system is about to enter a suspended state. So, a single sample or hands-on lab cannot realistically address all the power needs of all applications. I would thus encourage developers to review the details of each notification system, as well as the measures taken in this article, within the context of his or her application’s needs.

Overview of the Code

In this article, I attempt to reconcile the differences between the Windows XP and Windows Vista power notification systems for use in a Windows Presentation Foundation application. The bulk of the work is performed inside a custom class: PowerAwareWindow. This class derives from the Window class (the element in which many applications live) and exposes much of the power-related system information and notifications in a fashion typical of the Windows Presentation Foundation—using custom dependency properties and events. The definition of this custom class can be found in the PowerAwareWindow.cs file (all of the code for this article is available for download from the MSDN® Magazine Web site).

The functions, variables, and associated structures that make power notifications from the operating system possible exist in native code. In order to make the work that is done in the PowerAwareWindow class more readable, these native elements are placed in a separate code file, NativeMethods.cs.

The sample application, which uses PowerAwareWindow, lives within the Window1.xaml and Window1.xaml.cs files. Rather than being hosted within a standard Window, the application is hosted within a PowerAwareWindow; it hooks up to the custom events and makes use of the custom dependency properties declared within the PowerAwareWindow to receive and respond to changes in the system’s power status.

Note that the sample code in the download differs slightly from the code presented here. Many of the native elements of the sample reside within the NativeMethods.cs file. Therefore, in the sample code, when native elements are referenced they are prefixed with NativeMethods. For example, the downloadable code refers to the operating system version as:

NativeMethods.VISTA_OS_VERSION

In this article, however, for clarity I simply refer to it as:

VISTA_OS_VERSION

In addition, the formatting differs slightly for better readability, the comments differ, and the code in the download includes some Debug.Assert calls. These calls are included as an extra precaution—some sections of the code assume that the application is running on Windows Vista and others assume it to be running on Windows XP.

Notification of System Power Changes

The first step is to get notified of power-related system changes. As I mentioned earlier, this article does not provide a complete overview of the Windows XP and Windows Vista notification systems. I present a subset of this functionality specific to its use in the Windows Presentation Foundation. (For a complete overview of the notification systems, you should check out the Mobile PC Development Guides listed in the "Additional Resources" sidebar.)

I discuss two ways in which you can acquire notifications of system power changes: with the PowerModeChanged event on the SystemEvents class in the Microsoft.Win32 namespace in System.dll, and by trapping the WM_POWERBROADCAST window message.

The PowerModeChanged event is actually built on top of the WM_POWERBROADCAST message, but was done so before new power-related features were added to Windows Vista. As such, on Windows Vista the latter approach is more powerful. For example, when using only the PowerModeChanged event, an application would know nothing about the Windows Vista user’s defined power plan (to be discussed shortly).

PowerModeChanged Event and Handler

The PowerModeChanged event passes a PowerModes enumeration to registered event handlers. This enum has three values: Resume, StatusChange, and Suspend. With this event, an application is notified when:

  • The OS is about to resume from a suspended state (Resume)
  • The power source has transitioned between AC and battery or some other change has occurred in the status of the system power supply (StatusChange)
  • The operating system is about to be suspended (Suspend)

In the PowerAwareWindow, this event would normally be hooked up in the Window.Loaded event handler and unhooked in the Window.Closed event handler (see Figure 1). This, of course, is after the Window.Loaded and Window.Closed events are hooked up. This event covers much of the power-related information that an application might be interested in, and it may be the best option for your power-aware application. If an application is interested only in this information, then this method of receiving power events is much more straightforward than the window message trapping method that I’ll discuss momentarily.

Figure 1 Hooking Up PowerModeChanged Event

protected override void OnInitialized(EventArgs e)
{
    // Hook up the Window.Loaded and Window.Closed events
    this.Loaded += new RoutedEventHandler(PowerAwareWindow_Loaded);
    this.Closed += new EventHandler(PowerAwareWindow_Closed); 
    base.OnInitialized(e);
}

void PowerAwareWindow_Loaded(object sender, RoutedEventArgs e)
{            
    Microsoft.Win32.SystemEvents.PowerModeChanged += 
        new PowerModeChangedEventHandler(
            XP_SystemEvents_PowerModeChanged);
}

void PowerAwareWindow_Closed (object sender, RoutedEventArgs e)
{            
    Microsoft.Win32.SystemEvents.PowerModeChanged -= 
        new PowerModeChangedEventHandler(
            XP_SystemEvents_PowerModeChanged);
}

The XP_SystemEvents_PowerModeChanged function is implemented in the code download and can be viewed there. It isn’t utilized in the sample as the code for hooking up the event is commented out and is included only for demonstration purposes.

Trapping the WM_POWERBROADCAST Window Message

The operating system sends out WM_POWERBROADCAST Window messages to alert listening applications of changes to power-related status or settings. However, in order to receive some of these notifications, an application must first register for them. The sample accompanying this article uses the window messages for power notifications (rather than the PowerModeChanged event) in the interest of thoroughness.

With WM_POWERBROADCAST, an application can receive the full gamut of system power change notifications. In order to receive window messages in a Windows Presentation Foundation application, an event handler must first be declared to receive window messages, like so:

void PowerAwareWindow_Loaded(object sender, RoutedEventArgs e)
{            
    WindowInteropHelper helper = new WindowInteropHelper(this);
    HwndSource source = HwndSource.FromHwnd(helper.Handle);
    source.AddHook(new HwndSourceHook(this.MessageProc));
    ...
}

This code declares the MessageProc function as the window message event handler. That function will check for the WM_POWERBROADCAST message (see Figure 2) and act accordingly.

Figure 2 WM_POWERBROADCAST Message

const int WM_POWERBROADCAST = 0x0218;

IntPtr MessageProc(
    IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        case WM_POWERBROADCAST:
            // Handle the message here...
            break;

       // Handle other window messages (if the application needs to) here
    }
}

The information about the event is contained in the wParam of the window message. The wParam can take many forms to indicate different power-related system events. For example, the following value indicates that either the power source changed or that the battery percentage remaining has changed:

const int PBT_APMPOWERSTATUSCHANGE = 0x000A;

These indicate that the system is about to enter or is in the process of leaving a suspended state:

const int PBT_APMSUSPEND = 0x0004;
const int PBT_APMRESUMESUSPEND = 0x0007;

Some values are not available on all platforms. For example, the value shown below is available starting with Windows Vista and indicates that one of the following has changed: the power source, the power plan, the battery percentage remaining, or the monitor state.

const int PBT_POWERSETTINGCHANGE = 0x8013;

A complete list of such events can be found in the Power Management documentation. For comparison, the PBT_APMPOWERSTATUSCHANGE, PBT_APMSUSPEND, and PBT_APMRESUMESUSPEND wParams are analogous to the StatusChange, Suspend, and Resume PowerModes in the PowerModeChange event, respectively.

It is important to note the difference between wParam values for PBT_APMPOWERSTATUSCHANGE and the PBT_ POWERSETTINGCHANGE. The latter can be understood as a Windows Vista-only superset of PBT_APMPOWERSTATUSCHANGE. It indicates all the power-related changes that PBT_APMPOWERSTATUSCHANGE indicates as well as other information specific only to Windows Vista, such as power plans and monitor state. Both of these wParams exist and are passed along with the WM_POWERBROADCAST window message on Windows Vista, but processing both would result in redundancy in this sample when running on Windows Vista.

The PBT_POWERSETTINGCHANGE wParam, however, will not be sent to a Windows Vista application until the application registers for power setting notifications (PBT_APMPOWERSTATUSCHANGE is received without registration). This is a very important step for receiving power notifications specific to Windows Vista.

Registering for Notifications on Windows Vista

To register for power setting notifications on Windows Vista, the first step is to make the RegisterPowerSettingNotification function available for use in the application. You can do that with the following P/Invoke function definition:

[DllImport(@”User32.dll”, SetLastError=true,
    CallingConvention=CallingConvention.StdCall)]
static extern IntPtr RegisterPowerSettingNotification(
    IntPtr hRecipient, ref Guid PowerSettingGuid, Int32 Flags);

This function returns a handle that will be used to unregister for the events when the application is shutting down.

The Flags passed into this function indicate to the operating system how the application would like to receive notifications. For this sample, I declare this as:

const int DEVICE_NOTIFY_WINDOW_HANDLE = 0x00000000;

This indicates to the registration function that notifications are to be sent to the application using the WM_POWERBROADCAST window message with a wParam of PBT_POWERSETTINGCHANGE. The other option for Flags is relevant for OS services and is explained in the RegisterPowerSettingNotification documentation mentioned earlier.

The PowerSettingGuid value passed to the registration function indicates to the operating system the notifications in which the application is interested. Figure 3 shows the four PowerSettingGuid values you’re probably most interested in.

Figure 3 Four Types of PowerSettingGuid

// notify application when percentage of battery remaining changes
static Guid GUID_BATTERY_PERCENTAGE_REMAINING = 
    new Guid(“A7AD8041-B45A-4CAE-87A3-EECBB468A9E1”);

// notify application when the state of the monitor changes
static Guid GUID_MONITOR_POWER_ON = 
    new Guid(“02731015-4510-4526-99E6-E5A17EBD1AEA”);

// notify application when the system power source changes
static Guid GUID_ACDC_POWER_SOURCE = 
    new Guid(“5D3E9A59-E9D5-4B00-A6BD-FF34FF516548”);

// notify application when the user-defined power plan changes
static Guid GUID_POWERSCHEME_PERSONALITY = 
    new Guid(“245D8541-3943-4422-B025-13A784F679B7”);

Other notification GUIDs are available and can be used to get notified for events such as when the system is entering or exiting an away mode or when the system will be moving into an idle state in the near future and the current time is a good time to perform background or idle tasks.

In the sample, I have grouped all the Windows Vista power registration into one function (see Figure 4). Similarly, I have also grouped all the unregistration into one function. Registration occurs when the PowerAwareWindow loads (the Window.Loaded event is handled by PowerAwareWindow_Loaded function) and unregistration occurs when it closes (Window.Closed handled by PowerAwareWindow_Closed).

Figure 4 Windows Vista Power Registration

IntPtr _hBattCapacity;
IntPtr _hMonitorOn;
IntPtr _hPowerScheme;
IntPtr _hPowerSrc;

void RegisterForVistaPowerNotifications(IntPtr hWnd)
{
    _hPowerSrc = RegisterPowerSettingNotification(
        hWnd, ref GUID_ACDC_POWER_SOURCE, DEVICE_NOTIFY_WINDOW_HANDLE);
    _hBattCapacity = RegisterPowerSettingNotification(
        hWnd, ref GUID_BATTERY_PERCENTAGE_REMAINING, 
        DEVICE_NOTIFY_WINDOW_HANDLE);
    _hMonitorOn = RegisterPowerSettingNotification(
        hWnd, ref GUID_MONITOR_POWER_ON, DEVICE_NOTIFY_WINDOW_HANDLE);
    _hPowerScheme = RegisterPowerSettingNotification(
        hWnd, ref GUID_POWERSCHEME_PERSONALITY, 
        DEVICE_NOTIFY_WINDOW_HANDLE);
}

void UnregisterForVistaPowerNotifications()
{
    UnregisterPowerSettingNotification(_hBattCapacity);
    UnregisterPowerSettingNotification(_hMonitorOn);
    UnregisterPowerSettingNotification(_hPowerScheme);
    UnregisterPowerSettingNotification(_hPowerSrc);
}

Registration introduces one of the main issues that I address in this sample—to create a consistent approach to power-awareness in one Windows Presentation Foundation application that accounts for the differences regarding power notifications on Windows XP and Windows Vista. The RegisterPowerSettingNotification and UnregisterPowerSettingNotification functions exported from User32.dll are not available on operating systems prior to Windows Vista; as a result, any code that tries to use these functions should only do so after first ensuring the functions are available.

Detecting the Operating System

It is important for your application to detect the operating system and act accordingly. To this end, my sample has a private variable marked readonly for storing this information:

readonly int _osVersion = Environment.OSVersion.Version.Major;

The operating system major versions for Windows Vista and Windows XP are:

const int VISTA_OS_VERSION = 6;
const int XP_OS_VERSION = 5;

At this point, the PowerAwareWindow_Loaded and PowerAwareWindow_Closed functions should look something like that shown in Figure 5. At this point, the application is receiving power notifications via window messages in the MessageProc function. Once the power notifications are received, the application needs to respond appropriately.

Figure 5 PowerAwareWindow_Loaded and_Closed Functions

void PowerAwareWindow_Loaded(object sender, RoutedEventArgs e)
{
    // Optional: If the PowerModeChanged event contains the desired power-
    // notification functionality for an application, no OS detection is
    // necessary as PowerModeChanged will work with both
    SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(
        XP_SystemEvents_PowerModeChanged);

    // Use the helper to hook up our Windows Presentation Foundation window 
    // to receive window messages.
    WindowInteropHelper helper = new WindowInteropHelper(this);
    HwndSource source = HwndSource.FromHwnd(helper.Handle);
    source.AddHook(new HwndSourceHook(this.MessageProc));

    if (_osVersion >= VISTA_OS_VERSION)
    {
        RegisterForVistaPowerNotifications(source.Handle);

        // There’s no need to initialize the dependency properties on
        // Windows Vista because this application will get notified of the 
        // power status when it registers for the events.
    }
    else
    {
        // Application power initialization should be done here (otherwise 
        // the application is left waiting for a WM_POWERBROADCAST window 
        // message that might not come right away).
    }
}

void PowerAwareWindow_Closed(object sender, EventArgs e)
{
    // Optional: Unregister for PowerModeChanged if registration for it
    // was done in PowerAwareWindow_Loaded
    SystemEvents.PowerModeChanged -= new PowerModeChangedEventHandler(
        XP_SystemEvents_PowerModeChanged);
    
    if (_osVersion >= VISTA_OS_VERSION)
    {
        UnregisterForVistaPowerNotifications();
    }
    else
    {
        // Do Windows XP-specific application cleanup here
    }
}

Custom Power Dependency Properties

In many cases it is necessary for a power-aware application to declare and make use of custom dependency properties other than the ones declared in this sample. Note that custom dependency properties is an advanced topic. If you need more background before reading this section, check out the Custom Dependency Properties documentation.

While these custom dependency properties may seem complicated, they provide a lot of power and flexibility for an application that uses the PowerAwareWindow. The custom dependency properties declared in the sample are PowerPlan, MonitorOn, RemainingBattery, and RunningOnBattery.

PowerPlan exposes the current user-specified power plan. This piece of information is specific to Windows Vista. On Windows XP this will always be a default value (Automatic). The associated PowerPlanChanged event does not fire on Windows XP as there is no notion of power plans on that OS.

The PowerPlan enumeration is defined as:

public enum PowerPlan
{
    Automatic,
    HighPerformance,
    PowerSaver,
};

In the Windows Vista UI, these are called "Balanced," "High Performance," and "Power Saver," respectively. The dependency property registration looks like the code shown in Figure 6.

Figure 6 PowerPlan Dependency Property Registration

private static readonly DependencyPropertyKey PowerPlanPropertyKey =
    DependencyProperty.RegisterReadOnly(
        “PowerPlan”, typeof(PowerPlan), typeof(UIElement),
        new FrameworkPropertyMetadata(PowerPlan.Automatic, 
            FrameworkPropertyMetadataOptions.Inherits | 
            FrameworkPropertyMetadataOptions.Journal | 
            FrameworkPropertyMetadataOptions.
                OverridesInheritanceBehavior, 
            new PropertyChangedCallback(OnPowerPlanChanged)));

public static readonly DependencyProperty PowerPlanProperty = 
    PowerPlanPropertyKey.DependencyProperty;

private static void OnPowerPlanChanged(
    DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    PowerAwareWindow myPowerAwareWindow = d as PowerAwareWindow;
    if (myPowerAwareWindow != null &&
        myPowerAwareWindow.PowerPlanChanged != null)
    {
        myPowerAwareWindow.PowerPlanChanged(myPowerAwareWindow, e);
    }
}

public event DependencyPropertyChangedEventHandler PowerPlanChanged;

public PowerPlan PowerPlan
{
    get { return (PowerPlan)GetValue(PowerPlanProperty); }
    private set
    {
        SetValue(PowerAwareWindow.PowerPlanPropertyKey, value);
    }
}

MonitorOn exposes the current state of the monitor (display)—either on or off. This piece of information is specific to Windows Vista. On Windows XP, it will always have a default value (True). The associated MonitorOnChanged event does not fire on Windows XP as there is no notion of Monitor state in that OS.

The dependency property registration looks very similar to the one described for PowerPlan, except that the property is of type Boolean, the property is named MonitorOn, and the corresponding event is MonitorOnChanged. The full implementation for this and the rest of the dependency properties can be found in the code download.

RemainingBattery exposes the percentage of the battery currently remaining—ranging from 0 to 100 percent. For this case, the dependency property registration is very similar to that of the previous two dependency properties.

RunningOnBattery is true if the system is running on a battery and false if it is running on AC power.

Custom Power Events

As with the custom dependency properties, it is also often necessary for a power-aware application to declare and make use of custom events. (If you need more background on custom events, check out the Routed Event Overview). The custom events in my sample are PreviewSystemResuming, SystemResuming, PreviewSystemSuspending, and SystemSuspending.

PreviewSystemResuming occurs when the system is resuming from a suspended state. The Preview part of the event indicates that the event’s routing strategy is Tunnel as opposed to Direct or Bubble. The event registration looks like the code shown in Figure 7.

Figure 7 PreviewSystemResuming Event Registration

static readonly RoutedEvent PreviewSystemResumingEvent =
    EventManager.RegisterRoutedEvent(“PreviewSystemResumingEvent”,
        RoutingStrategy.Tunnel, typeof(RoutedEventHandler),
        typeof(PowerAwareWindow));

public event RoutedEventHandler PreviewSystemResuming
{
    add { AddHandler(PreviewSystemResumingEvent, value); }
    remove { RemoveHandler(PreviewSystemResumingEvent, value); }
}

//  This is called when the PowerAware window wants to raise the event 
void RaisePreviewSystemResumingEvent()
{
    RaiseEvent(new RoutedEventArgs(
        PowerAwareWindow.PreviewSystemResumingEvent, this));
}

SystemResuming occurs when the system is resuming from a suspended state. This uses the Bubble routing strategy version of the PreviewSystemResuming event. The event registration looks like the code shown in Figure 8.

Figure 8 SystemResuming Event Registration

static readonly RoutedEvent SystemResumingEvent =
    EventManager.RegisterRoutedEvent(“SystemResumingEvent”,
        RoutingStrategy.Bubble, typeof(RoutedEventHandler),
        typeof(PowerAwareWindow));

public event RoutedEventHandler SystemResuming
{
    add { AddHandler(SystemResumingEvent, value); }
    remove { RemoveHandler(SystemResumingEvent, value); }
}

//  This is called when the PowerAware window wants to raise the event 
void RaiseSystemResumingEvent()
{
    RaiseEvent(new RoutedEventArgs(
        PowerAwareWindow.SystemResumingEvent, this);
}

PreviewSystemSuspending occurs when the system is about to enter a suspended state. The Preview part of the event indicates that the event’s routing strategy is Tunnel, as it is with PreviewSystemResuming.

SystemSuspending occurs when the system is about to enter a suspended state. As with SystemResuming, it uses the Bubble routing strategy.

Windows XP Power Structs

The custom dependency properties aren’t much use unless they contain the latest system information. Similarly, the custom power events need to be raised appropriately. Let’s look at this now, as well as some of the trickier things involved in getting Windows Presentation Foundation power-awareness to function well for a single application that runs on both Windows XP and Windows Vista.

Let’s start with a look at important Windows XP power structures. First there’s the SYSTEM_POWER_STATUS (see Figure 9). As you can see, this sample uses the ACLineStatus and BatteryFlag enumerations.

Figure 9 SYSTEM_POWER_STATUS

class SYSTEM_POWER_STATUS
{
    public ACLineStatus ACLineStatus;
    public BatteryFlag BatteryFlag;

    // can be a value in the range 0 to 100, or 255 if status is unknown
    public byte BatteryLifePercent;       
    public byte Reserved1;            // reserved; must be zero

    // number of seconds of battery life remaining, 
    // or –1 if remaining seconds are unknown
    public Int32 BatteryLifeTime;         

    // number of seconds of battery life when at full charge, 
    // or –1 if full battery lifetime is unknown    
    public Int32 BatteryFullLifeTime; 
}

The SYSTEM_POWER_STATUS is returned from the GetSystemPowerStatus function. It is made available to the application with the following import:

[DllImport(“Kernel32.DLL”, CharSet=CharSet.Auto, SetLastError=true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetSystemPowerStatus(
    [In, Out] SYSTEM_POWER_STATUS SystemPowerStatus);

This function is called whenever the WM_POWERBROADCAST message is received with the PBT_APMPOWERSTATUSCHANGE wParam. This wParam indicates that something in the system power has changed and the GetSystemPowerStatus function gives the application access to that information. That information is returned inside the SYSTEM_POWER_STATUS that is passed in.

Initializing the Dependency Properties on Windows XP

As discussed in the Detecting the Operating System section, the dependency properties need to be initialized when the application is loading in order for them to be up-to-date (see Figure 5). This is not necessary in Windows Vista, as the operating system makes the application aware of the current system power status through the window message when the application registers for power notifications. This, however, does not happen on Windows XP and therefore initialization is necessary. This initialization is done in the PowerAwareWindow_Loaded function when the operating system is detected and appears as follows:

void InitializeDependencyPropertiesForXP()
{
    if (GetSystemPowerStatus(this._xpPowerStatus))
    {
        this.RunningOnBattery = 
            (ACLineStatus.Battery == _xpPowerStatus.ACLineStatus);
        this.RemainingBattery = _xpPowerStatus.BatteryLifePercent;
    }

    this.MonitorOn = true;
    this.PowerPlan = PowerPlan.Automatic;
}

At this point it is important to point out that there isn’t a Windows XP monitor or power plan notification analogous to the Windows Vista notification. Therefore, when a Windows XP application attempts to get these values, default values will be returned. And even if the application hooks up the MonitorOnChanged event or the PowerPlanChanged event, after initialization nothing will trigger the firing of the events.

Windows Vista Power Structs

Next up are the Windows Vista power structures. POWERBROADCAST_SETTING is the main Windows Vista power struct:

struct POWERBROADCAST_SETTING
{
    public Guid PowerSetting;
    public UInt32 DataLength;
}

In the official definition of the struct, there is a third member present: Data. As indicated in the struct, this can vary in size, being either a GUID or a DWORD. Thus, declared within the application is a custom variation of this struct that can hold either of these data types (note that this struct doesn’t mimic the layout of the original native structure and thus can’t be used for marshaling; it’s simply used so that the values can continue to be passed around within the application in a strongly typed fashion):

struct my_POWERBROADCAST_SETTING
{
    public IntPtr wParam;
    public Guid PowerSetting;
    public int PowerChangeData;
    public Guid PowerPlanGuid;
}

This struct also includes the wParam so that with this custom variation, all relevant data can be passed from the window message handler to the application’s Windows Vista message handler (defined later) in one struct.

So when receiving the WM_POWERBROADCAST window message on Windows Vista along with a wParam of PBT_POWERSETTINGCHANGE, the lParam associated with the message is a pointer to a POWERBROADCAST_SETTING struct that contains information about the change in the system’s power status.

One last important bit of information concerns how Windows Vista defines Power Plans. Maximum Power Savings involves very aggressive power-savings measures to help stretch battery life:

static Guid GUID_MAX_POWER_SAVINGS = 
    new Guid(“a1841308-3541-4fab-bc81-f71556f20b4a”);

No Power Savings uses almost no power savings measures:

static Guid GUID_MIN_POWER_SAVINGS = 
    new Guid(“8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c”);

Finally, the typical Power Savings power plan uses relatively aggressive power savings measures:

static Guid GUID_TYPICAL_POWER_SAVINGS = 
    new Guid(“381b4222-f694-41f0-9685-ff5bb260df2e”);

These correspond to PowerPlan.PowerSaver, PowerPlan.HighPerformance, and PowerPlan.Automatic, respectively. As mentioned earlier, they appear as Power Saver, High Performance, and Balanced in the Windows Vista UI, respectively.

How the Message Is Handled

The code in Figure 10 provides a high-level look at the MessageProc function. There are two important points to explain here. The first point is how the work is sent to each of the handlers. Second is how (and when) data is grabbed in the event of a WM_POWERBROADCAST message on Windows Vista.

Figure 10 MessageProc Function

IntPtr MessageProc(
    IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    switch (msg)
    {
        case WM_POWERBROADCAST:
            if (_osVersion >= VISTA_OS_VERSION)
            {
                // Grab the relevant power data from the message here.
 
                // Then send the data and work to the Windows Vista
                // handler here.
            }
            else
            {
                //  Send work to the Windows XP handler here.
            }
            break;

        // Other (non-power) window messages in which the
        // application is interested would be processed here
        // (if the application needs to do so).
    }

    return IntPtr.Zero;
}

Normally, sending the work to each of the handlers would be as simple as calling functions defined in the sample code. These functions would handle the message appropriately when the application is running on either Windows XP or Windows Vista. In this case, though, a bit more precaution has been taken to prevent system hangs. If, for example, the function that handles the message is complicated and processor-intensive, then control wouldn’t be returned to the window message handle immediately and this could result in noticeable system hangs. To avoid hangs like this, the work is sent to the Windows Vista and Windows XP handlers via a post to the Dispatcher thread (see Figure 11).

Figure 11 Asynchronous Invocation

delegate void VoidXPDelegate(/*power data arguments*/ );
delegate void VoidVistaDelegate(/*power data arguments*/ );

// A call to the Windows XP handler would look like this: 
this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, 
    new VoidXPDelegate(/*XP handler function here*/), 
    /*power data here*/);

// A call to the Windows Vista handler would look like this: 
this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, 
    new VoidVistaDelegate(/*Vista handler function here*/), 
    /* power data here*/);

The message will be handled asynchronously at the specified priority on the thread with which the Dispatcher is associated; control is returned immediately to the MessageProc function after the work is posted to the Dispatcher. More on the Dispatcher and Windows Presentation Foundation threading can be found in the WPF Fundamentals Threading Model documentation.

Now for the topic of grabbing data for Windows Vista power events. The wParam portion of the WM_POWERBROADCAST window message is crucial to understanding the nature of the message, as explained previously. On Windows XP, this is all that is necessary for successfully handling the power message. On Windows Vista, however, this wParam can indicate that the lParam in the message is a pointer to data necessary for determining the nature of the message. Figure 12 shows how I copy that data out of the lParam pointer. There you can see the MessageProc function with these changes.

Figure 12 Copying Data out of the lParam Pointer

// First grab the wParam, that’s important information to the 
// Windows Vista handler no matter what
my_POWERBROADCAST_SETTING myPBS = new my_POWERBROADCAST_SETTING();
myPBS.wParam = wParam;

if (wParam == (IntPtr)PBT_POWERSETTINGCHANGE)
{
    // Grab the power change information from the pointer passed
    // in the window message
    POWERBROADCAST_SETTING ps = (POWERBROADCAST_SETTING)
        Marshal.PtrToStructure(lParam, typeof(POWERBROADCAST_SETTING));

    // Copy the PowerSetting in the POWERBROADCAST_SETTING to 
    // our own structure
    myPBS.PowerSetting = ps.PowerSetting;

    // lParam points to the POWERBROADCAST_SETTING, but we need to 
    // calculate a pointer to where the data about the setting lives. 
    IntPtr pData = (IntPtr)((int)lParam + Marshal.SizeOf(ps));

    // Use the pointer calculated to grab the relevant data. Note that
    // the relevant data can be either a DWORD or a GUID, depending on
    // the datalength indicated in the POWERBROADCAST_SETTING struct.   
    if (ps.DataLength == Marshal.SizeOf(typeof(Int32)))
    {
        myPBS.PowerChangeData = (Int32)Marshal.PtrToStructure(
            pData, typeof(Int32));
    }
    else if (ps.DataLength == Marshal.SizeOf(typeof(Guid)))
    {
        myPBS.PowerPlanGuid = (Guid)Marshal.PtrToStructure(
            pData, typeof(Guid));
    }
}

// At this point ‘myPBS’ contains all the data the Windows Vista handler 
// needs for this application to process the system power change 

Once you have the relevant data for the message, you need to update the relevant properties and fire the relevant events. Considering all the work that’s been done thus far to get notifications and set up custom dependency properties, this step is actually very straightforward. All it involves is either assigning the data provided by the operating system to the appropriate application variables or raising the appropriate custom events.

Using the Dependency Properties and Events

Now let’s take a look at an application that is hosted within a PowerAwareWindow. At this level, power-awareness fits very well into the Windows Presentation Foundation model and creating a power-aware application that runs on both Windows XP and Windows Vista is quite straightforward.

The first thing to examine is the user interface, as defined in Window1.xaml. Note that the application lives within a Power- AwareWindow:

<local:PowerAwareWindow x:Class=”WPFPower_Aware.Window1”
    xmlns=”https://schemas.microsoft.com/winfx/2006/xaml/presentation”
    xmlns:x=”https://schemas.microsoft.com/winfx/2006/xaml”
    xmlns:local=”clr-namespace:WPFPower_Aware”
    x:Name=”MainWindow”   
    Title=”WPF Power Aware Application” Height=”480” Width=”550” 
    Loaded=”Window1_Loaded” 
>
    ...
</local:PowerAwareWindow>

As a result, the XAML file can make use of the custom dependency properties for data binding. My sample, for example, contains a status bar at the bottom of the window. Within the status bar, there are two pieces of power-related data—a text block indicating the current system power source and a progress bar indicating the current percentage of remaining battery power. The values assigned to these are data-bound to their corresponding dependency properties.

You’ll find that data binding is quite simple. Consider the progress bar, for example:

<ProgressBar IsIndeterminate=”False” x:Name=”myBatteryProgressBar” 
    Width=”200” Height=”20”
    Value=”{Binding ElementName=MainWindow, 
            Path=RemainingBattery, Mode=OneWay}” />

With this little bit of markup, the progress bar will always be up-to-date and no codebehind intervention is necessary. You can even data-bind to your own custom control, such as a battery meter, to display this information!

The power-source text block is somewhat more complicated as it involves converting from a Boolean value (RunningOnBattery) to a string for the text block. If you’d like to view the details of this, take a look in the sample code.

Controlling an Animation

TIf you run the sample application, you’ll see two animation elements. The one on the left is controlled in the codebehind, the other is controlled in XAML. The animation that is controlled by XAML starts and stops when the RunningOnBattery property is false and true, respectively.

The snippet in Figure 13 is for an animation that demonstrates the power of custom dependency properties via their use in XAML. With this piece of XAML, no code intervention is necessary to stop and start the animation. And this is just the beginning—there’s much more you can do with animations and triggers in XAML. For more information, see the Windows Presentation Foundation Animation Overview and the Trigger Class documentation.

Figure 13 Custom Dependency Properties in XAML

<Rectangle Name=”rect” Width=”140” Height=”90” Fill=”Blue” Stroke=”Red”>
  <Rectangle.RenderTransform>
    <ScaleTransform/>
  </Rectangle.RenderTransform>
  <Rectangle.Style>
    <Style>
      <Style.Triggers>
        <Trigger Property=”local:PowerAwareWindow.RunningOnBattery”
  Value=”False”>
          <Trigger.EnterActions>
            <BeginStoryboard Name=”myStoryboard”>
              <Storyboard>
                <DoubleAnimation Storyboard.TargetProperty=
                  “(Rectangle.RenderTransform).(ScaleTransform.ScaleY)”
                  From=”1” To=”1.5” RepeatBehavior=”Forever” 
                  AutoReverse=”True”/>
              </Storyboard>
            </BeginStoryboard>
          </Trigger.EnterActions>
          <Trigger.ExitActions>
            <StopStoryboard BeginStoryboardName=”myStoryboard”/>
          </Trigger.ExitActions>
        </Trigger>
      </Style.Triggers>
    </Style>
  </Rectangle.Style>
</Rectangle>

As I mentioned, the left element in the sample app has an animation that is controlled in the codebehind. When a relevant system power event occurs, the current power state of the system is used to determine if the animation should be paused to conserve power. First, the animation gets set up and then the custom events are hooked up. The necessary code for this is found in Window1 .xaml.cs (see Figure 14).

Figure 14 Code in Window1.xaml.cs

DoubleAnimation myDoubleAnimation;
Storyboard myStoryboard;

void Window1_Loaded(object sender, RoutedEventArgs e)
{
    // Initialize the animation
    myDoubleAnimation = new DoubleAnimation();
    myDoubleAnimation.From = 0.0;
    myDoubleAnimation.To = 360.0;
    myDoubleAnimation.Duration = new Duration(TimeSpan.FromSeconds(10));
    myDoubleAnimation.RepeatBehavior = RepeatBehavior.Forever;

    // Note that yRotate is an AxisAngleRotation3D declared
    // in the Window1.xaml file
    Storyboard.SetTargetName(myDoubleAnimation, “yRotate”);
    Storyboard.SetTargetProperty(myDoubleAnimation, 
        new PropertyPath(AxisAngleRotation3D.AngleProperty));
    myStoryboard = new Storyboard();
    myStoryboard.Children.Add(myDoubleAnimation);

    // Begin the animation if appropriate
    if (!ShouldStopAnimation()) myStoryboard.Begin(this, true);

    // Hook up the events from the PowerAwareWindow
    this.RemainingBatteryChanged += 
        new DependencyPropertyChangedEventHandler(
            Window1_RemainingBatteryChanged);
    this.RunningOnBatteryChanged += 
        new DependencyPropertyChangedEventHandler(
            Window1_RunningOnBatteryChanged);
    this.SystemSuspending += new RoutedEventHandler(
        Window1_SystemSuspending);
    this.SystemResuming += new RoutedEventHandler(
        Window1_SystemResuming);
    this.MonitorOnChanged += 
        new DependencyPropertyChangedEventHandler(
            Window1_MonitorOnChanged);
    this.PowerPlanChanged += 
        new DependencyPropertyChangedEventHandler(
            Window1_PowerPlanChanged);
}

Deciding how and when the application should take measures to conserve power depends largely on the behavior and needs of your application. In this sample, the decision is made as follows, and the power event handlers make use of this function in the appropriate event handlers:

bool ShouldStopAnimation()
{
    // If the monitor is off, or if we’re running on a battery
    // and the user hasn’t explicited requested high performance,
    // stop the animation
    return
        !MainWindow.MonitorOn ||
        (MainWindow.PowerPlan != PowerPlan.HighPerformance && 
         MainWindow.RunningOnBattery);
 }

One thing to note in this sample is that power events are reported in a list box in the UI. Here, for example, is the RunningOnBatteryChanged event handler:

void Window1_RunningOnBatteryChanged(
    object sender, DependencyPropertyChangedEventArgs e)
{
    myEventsListBox.Items.Add(
        “RunningOnBattery changed - now it’s {0}”, (bool)e.NewValue);

    if (ShouldStopAnimation()) myStoryboard.Pause(this);
    else myStoryboard.Resume(this);
}

You can learn more about other measures an application can take to conserve power by taking a look in the Mobile PC Power and Device Awareness documentation.

Conclusion

This article and the accompanying code provide a starting point from which you can learn to make your Windows Presentation Foundation applications power-aware. Note that there is a lot more to power awareness and the Windows Presentation Foundation than I’ve discussed here. Therefore, I highly recommend that you review the resources mentioned throughout this article before you design your power-aware application. You’ll discover many more capabilities and techniques that will help you tailor the best solution for your program.

Additional Resources

For general information regarding power and device management, and ideas on how you can reduce power consumption, check out these MSDN links for helpful documentation:

For an overview of the Windows Presentation Foundation features used in this article, check out these links:

And for specific definitions of certain functions, classes, and enumerations used in this article, see these links:

Andre Michaud is an SDET on the Tablet PC Platform Team at Microsoft where he works with handwriting recognition. His previous experience includes the Tablet PC components of the Windows Presentation Foundation.