You Can Take It With You, Part 1
| |
This article is about about mobility in the .NET Framework, specifically power awareness. |
|
Arian Kulp
Difficulty: Easy
Time Required: 1-3 hours
Cost: Free
Hardware:
|
In this column, the first of two, we're going to talk about mobility
in the .NET Framework. What is mobility? Good question! Mobility means
different things to different people. But key to mobility is that it
allows you to respond to various changes in a mobile-enabled
environment. For instance, if you are running on a laptop, you need to
consider whether the system is on battery or plugged in. If it's on
battery, how charged is the battery? If it's on AC, is the battery at
full capacity or still charging? You also need to consider the network.
As you move around with a laptop, your connection may come and go.
Being aware of your network and power status are two important aspects
of mobility. This column focuses on power awareness, while the next
column will discuss network considerations.
Adding mobility support to an application makes it easier for you to
play with available resources. If you know your network speed, you can
tailor your data throughput accordingly. If you know that a network
connection is unavailable, you might avoid even attempting a
connection, and thus reduce the errors that users see. Finally, once
you are aware of your power status, you can be more energy efficient
when on battery power, especially when it gets down to a low level.
The application showcased in this article is available in both C#
and Visual Basic. The links are at the top of the page. Use the code to
jumpstart your own projects, or just to learn. To get started with this
column, you'll need a copy of Microsoft Visual Basic Express Edition or
Visual C# Express Edition, both free to download until November of this
year. You can download either of these (and other Visual Studio Express
editions) from http://msdn.microsoft.com/vstudio/express.
This application shows off the most important features when running on
a laptop or tablet computer but is fully usable on a desktop computer
as well (it will simply report that no battery is present).
Working with Power
Up until .NET Framework 2.0, working with power information required
calling into native Win32 libraries using the P/Invoke feature. This
was okay if you had good code to copy and paste (or felt comfortable
calling out to unmanaged code), but it never felt right. Writing all of
your code using managed objects makes it both easier to write and
easier to maintain.
.NET Framework 1.1 included support to determine one of several power states through an event. SystemEvents.PowerModeChanged event indicates one of three modes:
- Resume — A system is resuming.
- StatusChange — Power is changing between AC and battery, or other state.
- Suspend — A system is suspending.
To my mind, the StatusChange mode is the strange one. To
quote MSDN, "This might indicate a weak or charging battery, a
transition from AC power from battery, or other change in the status of
the system power supply." It just isn't very useful by itself. Knowing
that you are switching between power sources ("or other change")
doesn't make much of a difference if you are unable to read more
information. This is where unmanaged code comes in. At this point, you
would have been forced to call into the Win32 GetSystemPowerStatus function and then work with a structure containing the system power information. Not very elegant.
With 2.0, Microsoft added an additional property to the SystemInformation class: PowerStatus.
This property returns an object of the same type, containing the same
information. While it's true that the same unmanaged call is most
likely being made behind the scenes, this property makes it possible
for you to avoid importing the kernel32.dll and allows you to leverage
your .NET skills. As an aside, let me add that SystemInformation
class is a cornucopia of useful information that helps you to determine
your number of monitors, resolution, keyboard/mouse preferences,
computer name, and much, much more.
Obtaining battery information is now as easy as:
Visual Basic
Dim ps As PowerStatus = SystemInformation.PowerStatus
' .1 - 0 for current battery charge
Console.WriteLine("Level: {0}:", (ps.BatteryLifePercent * 100) & "%")
Visual C#
PowerStatus ps = SystemInformation.PowerStatus;
// .1 - 0 for current battery charge
Console.WriteLine("Level: {0}:", (ps.BatteryLifePercent * 100) + "%")
When the event fires, you can check to see if it was a power-related event, if the Mode property of the event is StatusChange, and then read the PowerStatus information.
This is the approach taken in today's application. The following screen
shot demonstrates the application when power is on AC and an Internet
connection is available.

Figure 1. Running on AC
For the moment, let's ignore the Network region and focus on the Battery Info area. All five fields displayed are simply the properties of the PowerStatus object. The BatteryChargeStatus property can be any of the following:
- Charging
- Critical
- High
- Low
- NoSystemBattery
- Unknown
The NoSystemBattery value is set for—you guessed it—no
battery on the system. This is why you can implement the power features
in your application, and why it works fine on a desktop or laptop
computer. The PowerLineStatus can be used to indicate if the system is on battery or AC. Its value can be one of the following:
- Online (AC)
- Offline (Battery)
- Unknown (Hmm…)
The BatteryFullLifetime and BatteryLifeRemaining
properties return the number of seconds representing the amount of time
when the battery is full or at present level, respectively. The results
can vary based on the specific battery and system. On my system, as you
can see, my batteries don't report BatteryFullLifetime. This is indicated by a value of zero. For display in an application, create a TimeSpan object and format it like this:
Visual Basic
' How long is battery life based on current charge
Dim ts As New TimeSpan(0, 0, ps.BatteryLifeRemaining)
If ps.BatteryLifeRemaining > 0 Then
BatteryLifeRemainingTextBox.Text = ts.ToString()
Else
BatteryLifeRemainingTextBox.Text = "Unknown"
End If
Visual C#
TimeSpan ts = new TimeSpan(0, 0, ps.BatteryLifeRemaining);
if (ps.BatteryLifeRemaining > 0)
BatteryLifeRemainingTextBox.Text = ts.ToString();
else
BatteryLifeRemainingTextBox.Text = "Unknown";
Finally, the BatteryLifePercent property returns a number
between 0 (fully discharged) and 1 (fully charged). Simply multiply by
100 to obtain a number to display. I've found that, at least on my
system, it rarely wants to report 100%. But it is generally consistent
with what I find in the system Power Properties control panel applet.

Figure 2. Running on battery
The complete method for determining power simply queries the properties of PowerStatus and formats the data. Note that this can be called at any time, not just based on the PowerModeChanged event.
In this application, the method is called once during application
initialization (in the form constructor), and then every time the event
fires.
Visual Basic
Private Sub UpdatePowerInfo()
Dim ps As PowerStatus = SystemInformation.PowerStatus
' Charging, discharging, full, etc.
BatteryChargeStatusTextBox.Text = ps.BatteryChargeStatus.ToString()
' .1 - 0 for current battery charge
BatteryLifePercentTextBox.Text = (ps.BatteryLifePercent * 100) & "%"
PowerLineStatusTextBox.Text = ps.PowerLineStatus.ToString()
' How long is battery life based on current charge
Dim ts As New TimeSpan(0, 0, ps.BatteryLifeRemaining)
If ps.BatteryLifeRemaining > 0 Then
BatteryLifeRemainingTextBox.Text = ts.ToString()
Else
BatteryLifeRemainingTextBox.Text = "Unknown"
End If
' How long is battery life if fully charged (seconds)
ts = New TimeSpan(0, 0, ps.BatteryFullLifetime)
If ps.BatteryFullLifetime > 0 Then
BatteryFullLifetimeTextBox.Text = ts.ToString()
Else
BatteryFullLifetimeTextBox.Text = "Unknown"
End If
' PowerLineStatus is Online (AC), Offline (Battery), or Unknown
PowerModeToolStripStatusLabel.Text = _
IIf(ps.PowerLineStatus = PowerLineStatus.Online, "AC", "Battery")
End Sub
Visual C#
private void UpdatePowerInfo()
{
PowerStatus ps = SystemInformation.PowerStatus;
// Charging, discharging, full, etc.
BatteryChargeStatusTextBox.Text
= ps.BatteryChargeStatus.ToString();
// .1 - 0 for current battery charge
BatteryLifePercentTextBox.Text
= (ps.BatteryLifePercent * 100) + "%";
PowerLineStatusTextBox.Text = ps.PowerLineStatus.ToString();
// How long is battery life based on current charge
TimeSpan ts = new TimeSpan(0, 0, ps.BatteryLifeRemaining);
if (ps.BatteryLifeRemaining > 0)
BatteryLifeRemainingTextBox.Text = ts.ToString();
else
BatteryLifeRemainingTextBox.Text = "Unknown";
// How long is battery life if fully charged (seconds)
ts = new TimeSpan(0, 0, ps.BatteryFullLifetime);
if (ps.BatteryFullLifetime > 0)
BatteryFullLifetimeTextBox.Text = ts.ToString();
else
BatteryFullLifetimeTextBox.Text = "Unknown";
// PowerLineStatus is Online (AC), Offline (Battery), or Unknown
PowerModeToolStripStatusLabel.Text =
((ps.PowerLineStatus == PowerLineStatus.Online)
? "AC" : "Battery");
}
Next Steps
Integrating this with your application is easy. Best yet, your
application will look more polished and complete when such information
is reported in the status bar. You can even add settings to allow the
user to modify actions based on battery level or AC/battery status. For
instance, a game could automatically reduce its level of detail, or a
dynamic screensaver its complexity, in order to reduce battery drain.
I've touched upon but not really discussed the Resume and Suspend modes of the PowerModeChanged event. Use these to flush unsaved data to disk before the system suspends, or to halt and resume file transfers.
Conclusion
Adding power awareness may sound like a complicated proposition, but
the new support in .NET 2.0 makes it simple. Any application can take
advantage of these features. Using available system information makes a
good application even better, and can make it stand out from other
choices.
Download the sample code at the top of the page, download a copy of Visual Basic or C# Express at http://msdn.microsoft.com/vstudio/express,
and make your code better than ever. Adding power awareness will ensure
that people can take your applications with them, wherever they go.