Implementing a StatusBar


A status bar is something you see on almost every application. It displays a variety of information about the application as it is running. Implementing a status bar in .NET is similar to implementing a status bar in VB 6, so this section covers this topic briefly. However, you will spend a little bit of time adding and removing icons dynamically from the status bar and sending messages to the status bar using your MDI child forms, which is a little more difficult than in VB 6. Also, in .NET there is no longer a setting on the status bar for Num Lock or Caps Lock status to be displayed. In .NET, you need to do this using an owner-drawn panel. You will see one method for implementing this functionality using the same style as in VB 6.

To add the status bar to frmMain, double-click the status bar icon in the Toolbox. The status bar appears at the bottom of the form. Change the name of the status bar from StatusBar1 to sbrMain. The default property for ShowPanels is false, which is similar to the Simple Mode in VB 6. Change this property to True. Even though it looks like there is one panel on the status bar at this point, there really are not any, so let's add a couple of panels. Select the Panels collection of the sbrMain control, add the panels, and set the properties of the panels according to Table 6-4. Table 6-5 describes the purpose of each panel.

Table 6-4: The sbrMain Panels

Panel

AutoSize

Style

Minimum Width

Width

pnlStatus

Spring

Text

100

pnlUser

Contents

Text

10

10

pnlErrors

Contents

Text

10

10

pnlCapsLock

None

OwnerDraw

50

50

pnlNumLock

None

OwnerDraw

50

50

pnlDate

Contents

Text

10

10

Table 6-5: Purpose of Each Panel

Panel

Function

pnlStatus

Displays the status of application operations

pnlUser

Displays the user logged on to the system

pnlErrors

Displays an icon if there are errors in the windows event log that have not been reported to technical support

pnlCapsLock

Displays the status of the Caps Lock key

pnlNumLock

Displays the status of the Num Lock key

pnlDate

Displays the current date

The application user and the date will probably not change during the application session (or there are some overworked users), so you can set those in the constructor for the form. Add the following code at the end of the constructor for frmMain:

 pnlDate.Text = Now.ToShortDateString pnlUser.Text = Security.Principal.WindowsIdentity.GetCurrent.Name 

The first line takes the date (returned by the Now method), converts it to a short date string format, and displays it in the date panel. The second line retrieves the name of the user currently logged onto the client machine where the application is being run.

Creating the Owner-Drawn Panels

Next you will take care of the owner-drawn panels. There are a couple of things to note about the owner-drawn panels before getting into this. The StatusBar object raises an event called DrawItem. This method actually paints the graphics onto the object. In this case you will be drawing text graphics, but you can draw anything on the object. The event is raised whenever the status bar is refreshed in any manner (either through text changes to a panel, form resizing, or other such changes). The graphics you are placing on the object must be in this event and no other event. If the graphics are not drawn in this event, then whenever this event is called, the graphics there will be erased.

Note

These notes apply to all owner-drawn controls. One control you may find useful to set as owner drawn is a MenuItem control, which allows you to place graphics to the left of the MenuItem.

Now that you know these few rules, let's go over what is necessary to determine the status of the Caps Lock and Num Lock keys. If you look at the KeyUp or KeyDown events for frmMain, you will notice you can determine which key was pressed by checking the enumerated values of the KeyEventArgs object.

Note

For a full reference of this object and the enumerators, see the "Keys Enumeration" section in the MSDN documentation.

However, you cannot check to see if the Caps Lock is engaged or if the Num Lock is engaged. To do this, you need to call a Win32 API function. The specific function is the GetKeyState function. Calling a Win32 API function in .NET is fairly simple, but you must realize that the Win32 API is not managed code. Although this will not affect your application, it is something to note. In the UIUtilities code module, create a class called Win32APIcalls. Next, add the function call for the GetKeyState function. Your code will look like the following when you are done:

 Public Class Win32APIcalls     Public Declare Function GetKeyState Lib "user32" _     Alias "GetKeyState" (ByVal nVirtKey As Long) As Integer End Class 

Note

An easy way to get the list of Win32 API calls available to you is by using the Win32 API Viewer that shipped with VS 6.

You can set the nVirtKey argument by using the Keys.Capslock and Keys.Numlock enumerated values. The integer value returned by the GetKeyState function represents the key and the state of the key. The last bit of the value specifies the state of the key. Take the following example:

The GetKeyState function returns the value 65408 if the Caps Lock key is off and 65409 if the Caps Lock key is on. The number 65408 translates into the binary value of 1111111110000000. The number 65409 translates into the binary value of 1111111110000001. Notice that the only difference is the last bit.

Tip

You can check this by setting the following line of code in the frmMain.KeyDown event: MessageBox.Show(Win32APIcalls. GetKeyState(Keys.CapsLock).ToString). Make sure you set the KeyPreview property of frmMain to True.

Using this information, you can set the values in your status bar. Before you begin, you need to set the KeyPreview property of the MDI form (frmMain) to True. This allows your form to respond to specific key events. Without this, the MDI Form would never know that the Caps Lock or Num Lock key had been pressed. Then add the code from Listing 6-17 to frmMain. Afterward, you will examine the code line by line.

Listing 6-17: Drawing Owner-Drawn Panels

start example
 Private Sub sbrMain_DrawItem(ByVal sender As System.Object, _ ByVal sbdevent As System.Windows.Forms.StatusBarDrawItemEventArgs) _ Handles sbrMain.DrawItem      Dim intValue(0) As Integer      Dim ba As BitArray      Dim fnt As New Font(New FontFamily("Arial"), 10, FontStyle.Regular)      Dim strText As String      Dim clr As Color      If sbdevent.Panel Is pnlCapsLock Then           intValue(0) = Win32APIcalls.GetKeyState(Keys.CapsLock)           ba = New BitArray(intValue)           strText = "CAPS"           If ba.Item(0) = False Then                clr = Color.DarkGray           Else                clr = Color.Black           End If      End If      If sbdevent.Panel Is pnlNumLock Then           intValue(0) = Win32APIcalls.GetKeyState(Keys.NumLock)           ba = New BitArray(intValue)           strText = "NUM"           If ba.Item(0) = False Then                clr = Color.DarkGray           Else                clr = Color.Black           End If      End If      sbdevent.Graphics.DrawString(strText, fnt, New SolidBrush(clr), _      sbdevent.Graphics.VisibleClipBounds.Location.X, _      sbdevent.Graphics.VisibleClipBounds.Location.Y) End Sub 
end example

There are many things going on in this block of code, so let's examine the code. First, this code is called for every owner-drawn item on the status bar, which explains some of this code. The intValue integer array holds the return value from the GetKeyState function. The reason you are storing this value in a one-dimensional array is because of the constructor for the BitArray object. The BitArray object is used for exactly what you think it is used for—it holds an array of bits. By placing your return integer value into a BitArray, you can more easily examine the individual bits of the return value. There are other ways to examine the individual bits, such as using AND and OR bitwise operations on the return value, but this way gives you a more precise way to examine the values (it is also my preference, but how you do this does not really matter). The Font object is the font you will use to draw your text into the owner-drawn panels. The strText value holds the text string you will draw, and the clr variable holds the color of the text you will draw.

Next you check to see which panel you are going to be drawing. If you determine it is the Caps Lock panel, you call the following:

 intValue(0) = Win32APIcalls.GetKeyState(Keys.CapsLock) ba = New BitArray(intValue) 

The first line assigns the return value of your call to GetKeyState to the integer array. The second line instantiates the BitArray object. One of the overloaded methods of the constructor accepts an integer value, and another overloaded method accepts an integer array. The key is that the method that accepts a single integer value dimensions the BitArray and does nothing else. Passing in an integer array converts the values stored in the array to their binary representation.

Caution

One interesting effect of converting an integer value to a BitArray is that it reverses the values because of an AND operation. Therefore, when you pass in a value of 65408 (1111111110000000), it translates to 0000000111111111. This makes your job a little easier because you only need to check the first bit.

Note

The binary values you have seen so far have been 16-bit values even though in .NET an integer is a 32-bit value. The rest of the bits are set to zero and are unimportant because the value you are working with really is a 16-bit value.

Next you start by setting the text you are going to draw in the panel. Then you check the first bit in your BitArray object to see if it is set. If it is False, then the Caps Lock is not pressed, so you want to draw the CAPS text in dark gray. If it is True, then you want to draw it in black. The same block of code is repeated again, but this time you check for and make the correct settings for the Num Lock key:

 strText = "CAPS" If ba.Item(0) = False Then      clr = Color.DarkGray Else      clr = Color.Black End If 

This is the last bit of code:

 sbdevent.Graphics.DrawString(strText, fnt, New SolidBrush(clr), _ sbdevent.Graphics.VisibleClipBounds.Location.X, _ sbdevent.Graphics.VisibleClipBounds.Location.Y) 

This code draws your string value onto the panel. The arguments it takes, as you can see here, are the string to draw, the font to draw it in, the brush to use (with the color to use for the brush), and the location to draw it. The VisibleClipBounds property figures out where to draw the text. There are other location properties available through StatusBarDrawItemEventArgs, but it is much easier to use this one because it does not require any additional calculations.

Finally, there is only one thing left to do—trigger the DrawItem event when the user presses the Caps Lock or Num Lock key. The code to do this is pretty straightforward:

 Private Sub frmMain_KeyDown(ByVal sender As Object, _ ByVal e As System.Windows.Forms.KeyEventArgs) Handles MyBase.KeyDown      If e.KeyCode = Keys.CapsLock Or e.KeyCode = Keys.NumLock Then           sbrMain.Refresh()      End If End Sub 

Add this code to the frmMain class and give the application a try. It should produce the results as shown in Figure 6-4 (probably with a different username).

click to expand
Figure 6-4: Completed status bar with owner-drawn panels

Dynamically Adding and Removing Icons

With .NET, you can compile resources directly into the assembly. This is similar to a resource file in VB 6, but everything is directly accessible through the assembly using reflection. The actual process for setting up and accessing the resources is pretty simple. The first step is to add the resource to the assembly.

Note

The resources do not have to be in the same assembly from which you are calling them. It is the case in this section, but it is just as easy to create an assembly to store resource information separately.

Right-click the NorthwindTraders project and select Add Existing Item. Navigate to the directory where the standard icons that are installed with VS are stored.

Note

The default folder is C:\Program Files\Microsoft Visual Studio .NET\Common7\Graphics\Icons\Computer.

Select the icon with the W95MBX01.ICO filename. The icon looks like the following:

Next, right-click the newly added resource and select Properties. Set the Build Action property to Embedded Resource. This instructs the compiler to include the icon in the assembly when it is built.

Next, import the System.IO and System.Reflection namespaces into the frmMain code module. Then add the method shown in Listing 6-18 to the frmMain class.

Listing 6-18: The ShowErrorIcon Method

start example
 Private Sub ShowErrorIcon()      Dim asm As [Assembly] = [Assembly].GetExecutingAssembly      Dim iStream As Stream      iStream=asm.GetManifestResourceStream _      ("NorthwindTraders.UserInterface.W95MBX01.ICO")      pnlErrors.Icon = New Icon(iStream) End Sub 
end example

So, what is going on here? The first line gets a reference to the assembly that is currently running—in this case it will be the NorthwindTraders assembly. Then you grab the stream of information that represents the resource for which you are looking. You are searching for the W95MBX01.ICO resource, but note you have to fully qualify the name or else you will not find the resource. Finally, you create an icon out of the stream using one of the overloaded methods of the Icon class.

Next, you need to alter your LogErrorEvent class so that you can find out if there are any errors when the application starts. Let's alter the LogErrorEvent class by adding the following method:

 Public Function ErrorCount() As Integer      Dim objEL As New EventLog(EVENTLOGNAME)      Return objEL.Entries.Count End Function 

Now you have a simple function that tells you if there are any errors stored in the Windows Event Log. To use this information, though, you need to call it from frmMain as soon as the form is loaded. Add the following declaration to the general declarations section of the frmMain class:

 Private WithEvents mobjEventLog As ErrorLogging.LogErrorEvent 

Next, alter the constructor for frmMain so that the following line of code is added after the LoadMenus call:

 mobjEventLog = mobjEventLog.getInstance 

Now you just need to create the following method to handle the frmMain.Load event:

 Private Sub frmMain_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Load      If mobjEventLog.ErrorCount > 0 Then           ShowErrorIcon()      End If End Sub 

Lastly, you need to handle the events raised by the mobjEventLog object. Add the following two methods to the frmMain class:

 Private Sub mobjEventLog_ErrorLogged() Handles mobjEventLog.ErrorLogged      ShowErrorIcon() End Sub Private Sub mobjEventLog_ErrorsCleared() Handles mobjEventLog.ErrorsCleared      pnlErrors.Icon = Nothing End Sub 

Now that you have all the code added, you can test it just to make sure it works. If you already have some errors stored in the Windows Event Log, the error icon should display as soon as the application runs. If not, you can alter one line of code temporarily to throw an error. Alter the LoadRegion method so that the line immediately after the Try statement reads as follows:

 Throw New Exception(Test) 

Next, edit the LogException method in frmMain so that the line immediately following the Try statement throws the same exception as previously.

Now, run the application and try to load the Regions. You should receive a critical error message and a red icon should appear in the status bar. Selecting the Report Errors menu item from the Help menu should display the frmReportErrors form with two errors in it as in Figure 6-5.

click to expand
Figure 6-5: Error list and error icon

Click the Send button, and the Report Errors form should close and the error icon should be removed from the status bar.

Once you are done with the test, remember to remove the two errors you created for these tests.




Building Client/Server Applications with VB. NET(c) An Example-Driven Approach
Building Client/Server Applications Under VB .NET: An Example-Driven Approach
ISBN: 1590590708
EAN: 2147483647
Year: 2005
Pages: 148
Authors: Jeff Levinson

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net