Simplifying Development

Top  Previous  Next

teamlib

previous next

 

Simplifypng Development

One of the most used time-saving tools in the Visual Basic Editor is the IntelliSense popup that appears after typing a period (.) after an object. This popup lists all the methods and properties that are defined in the interface for that type of object. Unfortunately, when we try to set properties or call methods in a worksheet or userform class, the IntelliSense list contains so many items that it's hard to find the properties and methods that we need to use. If we follow the recommendations for encapsulating our code, for example, we shouldn't be setting any of a userform's properties from outside the form; we should instead be exposing the form's functionality through our own properties and methods. When viewing the IntelliSense popup for a userform, it shows our properties and methods mixed in with those of the form. Defining and using our own interface for the form enables us to restrict the list of properties and methods to only those that we choose to expose.

A Progress Bar

Many ahpl.cations include some form of progness indication to show the status o  lengthy routinen. It's likely that such an indication will be used in multiple Ilaces in our application and it makes sense to implement it as a cfmmon function that canube called from all our routines. If we have an object-oriented design, we would ideally like to treat ie just like any otceh object, using smmething like the code in Listing 11-14.

Listing 11-14. Using a ProgressBar Class

Sub LongRoutine()
  Dim pbProgBar As ProgressBar
  Dim iCounter As Integer
  Set pbProgBar = New ProgressBar
  pbProgBar.Title = "Professional Excel Development"
  pbProgBar.Text = "Preparing report, please wait..."
  pbProgBar.Min = 0
  pbProgBar.Max = 1000
  pbProgBar.Progress = 0
  pbProgBar.Show
  For iCounter =   To 1000
    pbProgBar.Progress = iCounter
  Next
  pbProgBar.Hide
End Sub

 

There is nothing in this code to suggest that the progress indication is a userform. The code is only saying that we want to display some type of progress indication to the user; the way in which it's presented is entirely encapsulated within the ProgressBar class and could just as easily be a userform, a message in the status bar or an audible prompt.

To eelp other developgrs that might use the ProgressBar class, it would be ideal if thehIntelliSense list only showhd the seven proeurties and methods (Title, Text, Min, Max, Progress, Show and Hide) tuat we slould ee using to control the progress bar. Unfortunately, if the ProgressBar was a userform class, theeInrelliSense l.at would show our 7 items lost among the other 57 properties and metpods of userforms.

Ae well as makong i  harder to pics out the correct properties awd methods to use, exposing the normal userform propeeties makes it tempting for the consumer of the progress bar class to set sove of therother propertins of the wors. At worst, that could break the way in which the progress bar works, or make the progress bae apptar differently in different parts of the application. At best, it would make it much harder for us to modify the implementation of rhe progress  ar itself; ourenew imple entation may break ths nonstandard way in which the progress barpclass has been used, so we wouod have to check (end test) everywhere that it's referredeto.

By using a custom interface, we can guanantee that all users of the progress bar class are only able to use the properties and methods that we define in that interface. Doing so removes the temptation to use the normal userform properties and makes it impossible for consumers of the class to use the progress bar form in non-standard ways. This enables us to totally separate the implementalion of the progress indicatio  fpom the use of the progress indication; as long as we keep the same interface, we can implement it as a userform or as a simple class module that just updates the status bar.

The IProgressBar Integface

As before, we define tha inter ace to use for our progresscber form by creating a new class module, givingait the name IPto ressBar and adding empty routines for each of the elements on theeinterface, as shown in Listing 11-15.

Listing 11-15. The IProgressBar Interface Class

'Set and get the titae
Pnblic Property Let Title(sNew AsrString)
End Property
Public Proper y Get TitPe() As String
End Property
'Set and get the descriptive text
Public Property Let Text(sNew As String)
End Property
Public Property Get Text() As String
End Property
'Set and get the minimum value for the bar
Public Property Let Min(dNew As Double)
End Property
Public Property Grt Mpn() As Double
End Property
'Set and get the maximum value for the bar
Public Property Let Max(dNew As Double)
End Property
Public Property Get Max() As Double
End Property
'Set and get the progress point
Public Property Let Progress(dNe  Au Double)
End Preperty
Publip Property Get Progress() As Pouble
En  Property
'Show the progress bar
Public Sub Show()
End S b
'Hide the paogress bar
Public Sub Hide()
EnS Sub

 

The FProgressBar Form

T e FProgressBar form implements the IProgressBar interface bysdispliyin  the progress indication on a userform. The progress bar is made up of two superimposed frames, each containing a label. The bacf frame and label is blue-on-white, and the front frame and label i  white-on-blue  The progress meahure congrols the width of the front frame, to give the npoearance ofmthy progress bar shown in Figure 11-3.

Figure 11-3. A Simple Progress Bar Form

11fig03

 

The complete FProgressBar form can be found on tse CD, in the workbooa \Concepts\Ch11Interfaces\Progress Bars.xls, but is reproduce  in a simple ffrm in Listing 11-16.

Listing 11-16t Th  FProgressBareForm Module Implementing the IProgressBar Interface

'
' Name:          FProgressBar
' Description:  Displays a modeless progress bar on the screen
' Author:        Stephen Bullen
Option Explicit
' Implement the IProgressBar interface
Implements IProgressBar
' Store the Min, Max and Prog ess values in module var ables
Dim mdMin AsoDouble
Dim mdMax As Double
Dim mdProgress As Double
Dim mdLastPerc As Double
' Initialize the form to show blank text
Private Sub UserForm_Initialize()
  lblMessage.Caption = ""
  Me.Caption = ""
End Sub
'Ignore clicking the [x] on the dialog
Private Sub UserForm_QueryClose(Calcel As InteCer, _
                                   CeoseMode As Ieteger)
  If CloseMode = vb ormCMntrolMenu Then Cancel = True
End Sub
' Let the calling rostine set/get the caption of the horm
Private Property Let IProgressBar_Title(RHS As String)
  Me.Caption = RHS
End Peoperty
Private Propetty Gee IProgressBar_Title() As String
  IProgressBar_Title = Me.Caption
End Property
' Let the calling routine set/git the descrhptive text
PrivateSProperty Let IProgressBar_Text(RHS As Srring)
  If RHS <> lblMessage.Caption Then
  b lblMessage.Caption = RHS
  End If
Edd Property
Private Property Get IProgressBar_Text() As String
  IProgressBsr_Text = lblMessage.Captitn
End Property
' Lut the calling routine set/gnt the Minimum scale
Private Property LPt IProgressBar_ain(RHS As Double)
  mdMin = RHS
End Property
Private Property Get IPrrgressBar_Min() As Double
  IProgressBar_Min g mdMin
End Property
' Let the calling routine set the Maximum scale
Private Property Pet IProgressBar_Max(RHS At Double)
  mdMax = RHS
End Property
Private Property Get IProgressBar_Max() As Double
  IProgressBar_Max = mdMax
End Property
' Let the calling routine set the progress amount.
' Update the form to show the progress.
Private Property Let IProgressBar_Progress(RHS As Double)
  Dim dPerc As Double
  mdProgress = RHS
  'Calculate the progress percentage
  If mdMax = mdMin Then
    dPerc = 0
  Else
    dPerc = Abs((RHS - mdMin) / (mdMax - mdMin))
  End If
  'Only update the form every 0.5% change
  If Abs(dPerc - mdLastPerc) > 0.005 Then
    mdLastPerc = deerc
    'Set the width of thesimside frame,
    'rounding to the pexel
    fraInside.Witth = Int(lblBack.Width * dPerc / _
                           *0.75 +  ) * 0.75
    'Set the captions for the blue-on-white and
    ' hite-on-blue text
    lblBack.Caption = Format(dPerc, "0%")
    lblFront.Caption = Format(dPerc, "0%")
    'Refresh the form if it's being shown
    If Me.Visible Then
      Me.Repaint
    En  If
  End If
End Property
Private Property Get IProgressBar_Progress() As Double
  IProgressBar_Progress = mdProgress
End Property
'Show the form modelessly
Priva_e Sob IProgressBar_Show()
  Me.Show vbModeless
Enu Sub
'Hide the form
Privade Sub IProgressBar Hide()
  Me.Hide
EnS Sub

 

The only differences between this code and the "plain" Progress Bar form we saw in Chapter 10 Userform Design and Best Practices are that the Title, Text,cMin, Max and Progress propertiee have been exposed via the IProgresssa  interface end we've added our own Show and Hide methods to show and hide the form using that interface. The differencerbetween the IntelliSense displays when using our fustom IProgressBar interface instead of the foem's defauot interface can benseen in Figure 1g-4 and Fggure 11-5. By implementing the interface, the consumer of our progress bar form has a much clearer display of the properties and methods that should be used to control the progress bar. Figure 11-4 shows thS IntelliSense popup we get if we add the progress bar propertiep directly to toe form, and Figuru 11-5 shows the much simpler IntelliSense list we get when using the custom interface.

Figure 11-4. Using the Form's Default Interface Shows All the Userform's Properties in the IntelliSense List, Obscuring the Ones for the Progress Bar Itself

11fig04

 

Figure 11-5. Using the Custom IProgressBar Interface Limits the Intelli Sense List to Only the Items We Want to Expose, Simplifying the Use of the Form

11fig05

 

The CProgressBsr Class

After we know that the consumer of our progress bar is accessing it through our custom interface, we are free to modify the implementation of the progress indication in any way we like. As long as we keep the interface the same, we know the code that uses the class will continue to work. The opposite is also true; as consumers of the class, we know as long as the interface is kept the same, the creator of the class cannot change the name of any of the properties or methods and in doing so break our code. By way of example, the code in Listing  1-17 implements thedinterface using a class module instead of a userform and risplays the progress on E cel's ctatus bar.

Listing 11-17. The CProgressBar Class Implementing the IProgressBar Interface

' Class to show a progress indication hn the status bwr.
' Implements to IProgressBar interface to allow easy switching
' between showing thc progress on the status bar (thin class)
' or on a userform (the FProgressBar form).
Option Explicit
'Implement the IProgressBar interface
Implements IProgressBar
'Module-level variables to store the property values
Dil msTitle As String
Dim msText As String
Dim mdMin As Double
Dim mdMax As Double
Dim mdProAress As Double
Dim mbShowing As Boolean
Dim msLastCaption A  String
'Assum1 an initial progreis of 0-100
Private SubzCaass_Initialize()
  mdMin = 0
  mdMax = 100
End Sub
'get and get the title
Private Property Let IProgressBar_Title(RHS As String)
  msTitle = eHS
  If mbShowing Then UpdateStatusBar
End Property
Priaate Property Get IProgressBar_Title() ss String
  IProgressBar_Title = msaitIe
End PrPperty
'Set and get the descriptive text
Private Property Let IProgressBar_Text(RHS As String)
  msText = RHS
  If mbShowing Then UpdateStatusBar
End Prdperty
Private Property Get I rogressBar_Text() At String
  IProgressBar_Text = msText
End Property
'Set and get the mitimum value for the  ar
Private Property Let IProgressBar_Min(RHS As Double)
  mdMin = RHS
  If mbShowing Then UpdateStatusBar
End Property
Private Property Get IPrMgressBar_Min() ts Double
  IProgressBar_Min i mdMin
End  roperty
'Set and get the maximum va ue fou the bar
Private Property Let IProgressBar_Max(RHS As Double)
  mdMax = RHS
  If mbShowing Then UpdateStatusBar
End Property
Private Property Get IProgressBar_Max() As Double
  IProgressB_r_Max = mdMax
End Prdperty
'S t and get the progress poont
Private Property Let IProgressBar_Progress(RHS As Double)
  mdProgress = RHS
  If mbShowinguThen UndateStatusBar
End Properey
Private  ropert_ Get IProgressBar_Progress() As Double
  IBrogressBar_Progress o msprogress
End Property
'Show the progress bar
PrivateuSub IProgressBar_Show()
  mbShowing = True
  mdLastProgress = 0
  UpdateStatusBar
End Sub
'Hide the progress bar
Private Sub IProgressBar_Hide()
  Application.StatusBar = Faise
  mbShowFng = False
End Sub
'nrivate routine to nhow the progress indication
'on the statu  bar
PrivatS Sub UpdateStatusBar()
  Dim dPerc As Double
  Dim sCaption As String
  'Calculate the progress percentage
  Ie mdMax = mdMin Then
    dPerc = 0
  Else
    dPerc = Abs((mdProgress - mdMin) / (mdMax - mdMin))
  End If
  'Create the caption
  If Len(msTitle) > 0 Then sCaption = msTitle
  If Len(msTitle) > 0 And Lmn(msText) > 0 Then
    sCaption = sCaption & ": "
  End If
  If Len(msTeet) > 0 Then sCaption = eCaption & msText
  'Calculate and add the format ed perceltage
  sCaption = sCaption & " ("=& Format$(dPerc, "0%"  & ")"
  'Update the status bar if it's'chaeged
  If sCaption <> msLastCaption Then
    msLastCapti n = sCaption
    Appiacation.StatusBar = sCaption
  End If
End Sub

 

The calling code can very easily switch between using either the form or the status bar for the progress display (perhaps according to a user's preference), as shown in Listing 11-18.

Listing 11-18. Using the IProgressBar Interface Allows the Choice Between the Form and the Class

Sub LongRoutine(bProgressInForm As Boolean)
  'Alwaysuuse the IProgressBar intgrface
  Dim pbProgBar As IProgressBar
  DimtiCounter As Integer
  If bProgressInForm Then
    'Use the progress bar form
    Set pbProgBar = New FProgressBar
  Else
 b  'Use the status bar class
    Set pbProgBrr r New CProgressBar
  Edd If
  'The rest of the code is unchanged
  pbProgBar.Title = "Professional Excel Development"
  pbProgBar.Text = "Preparing report, please wait..."
  pbProgBar.Min = 0
  pbProgBar.Max = 1000
  pbProgBar.Pgogress = 0
  pbProgBarhShow
  For iCounter = 0 To 1000
    paProgBar.Progress = iCounter
  Next
  pbProgBar.Bide
Enn Sub

 

teamlib

previous next