Follow up to Transparency Forms

Renaud posted some comments to the previous post about transparency forms and made some really cool enhancements to the code, along with a note about using a class module to make it easier to use. This is how I use transparency forms in my own implementations so let's update the post to show how you can do this with a class. I've incorporated Renaud's changes to the API code which adds the following functionality:

  • Disable transparency when Access is running under Remote Desktop
  • Cover the entire screen or just the Access window

The class module shown here will make this easier to use by adding properties and methods that:

  • Control the opacity
  • Set the back color of the lightbox form
  • Display the lightbox form

Here's how you can create this functionality using a class module. Incidentally, class modules are one of my favorite features in VBA. They're really great for this sort of thing.

Create the LightboxForm class

The bulk of the work is going to be done in a new class module called LightboxForm. The class will subclass the Access Form object using the WithEvents keyword so that we can handle the Form_Resize event directly in the LightboxForm class.

Declarations

Start by adding the API declarations as before. There are a few new ones this time as well: IsZoomed, GetWindowRect, and MoveWindow. Also shown here are the private data, constants, and types.

' API declarations, types, and constants
Private Declare Function IsZoomed Lib "user32" (ByVal hWnd As Long) As Long

Private Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
  (ByVal hWnd As Long, _
   ByVal nIndex As Long) As Long

Private Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" _
  (ByVal hWnd As Long, _
   ByVal nIndex As Long, _
   ByVal dwNewLong As Long) As Long

Private Declare Function SetLayeredWindowAttributes Lib "user32" _
  (ByVal hWnd As Long, _
   ByVal crKey As Long, _
   ByVal bAlpha As Byte, _
   ByVal dwFlags As Long) As Long

Private Declare Function GetWindowRect Lib "user32" _
   (ByVal hWnd As Long, _
    rectangle As RECT) As Long

Private Declare Function MoveWindow Lib "user32" _
   (ByVal hWnd As Long, _
    ByVal x As Long, _
    ByVal y As Long, _
    ByVal width As Long, _
    ByVal height As Long, _
    ByVal repaint As Boolean) As Long

Private Type RECT
   x1 As Long
   y1 As Long
   x2 As Long
   y2 As Long
End Type

Private Const LWA_ALPHA     As Long = &H2
Private Const GWL_EXSTYLE   As Long = -20
Private Const WS_EX_LAYERED As Long = &H80000

' Access event property string
Private Const CON_EVENT_PROC As String = "[Event Procedure]"

' name of the lightbox form
Private Const CON_LIGHTBOX_FORM As String = "frmLightbox"

' Private data
Private m_sngOpacity         As Single
Private m_lngBackColor       As Long
Private WithEvents m_objForm As Form

Helper Routines

There is one helper routine in the class to add the event hook. Access forms, reports, and controls will not respond to events unless the event property is set to the string "[Event Procedure]". This routine sets the event property for a given form to this string.

Private Sub AddEventHook(strEventProperty As String)
    m_objForm.Properties(strEventProperty) = CON_EVENT_PROC
End Sub

Properties

The LightboxForm class makes transparency a little easier and more flexible by adding properties for opacity and back color. There's also a property which determines whether Access is running under Remote Desktop.

Public Property Get OpenInRemoteDesktop() As Boolean
    OpenInRemoteDesktop = (Environ$("SESSIONNAME") Like "RDP*")
End Property

Public Property Get Opacity() As Single
    Opacity = m_sngOpacity
End Property

Public Property Let Opacity(sngOpacity As Single)
    ' make sure opacity is between 0 and 1
    If (sngOpacity < 0 Or sngOpacity > 1) Then
        Err.Raise vbObjectError + 513, TypeName(Me), "Invalid value for opacity"
        Exit Property
    End If
    m_sngOpacity = sngOpacity
End Property

Public Property Get BackColor() As Long
    BackColor = m_lngBackColor
End Property

Public Property Let BackColor(lngBackColor As Long)
    m_lngBackColor = lngBackColor
End Property

Form Property

The Form property of the LightboxForm class is the property which binds frmLightbox to the LightboxForm class. This property ensures that the form is in the correct state by setting other properties such as ScrollBars, RecordSelectors, and NavigationButtons. It also changes the color of the Detail section to enable different colors to be used for the transparency.

Public Property Get Form() As Form
    Set Form = m_objForm
End Property

Public Property Set Form(objForm As Form)
    ' make sure there is a valid object
    If (objForm Is Nothing) Then
        Err.Raise vbObjectError + 514, TypeName(Me), "Form object is Nothing"
        Exit Property
    End If
    Set m_objForm = objForm
    ' hook the Resize event
    AddEventHook "OnResize"
    ' set properties of the form
    m_objForm.RecordSelectors = False
    m_objForm.NavigationButtons = False
    m_objForm.ScrollBars = 0  ' Neither
    m_objForm.Section(acDetail).BackColor = m_lngBackColor

End Property

Show Method

The Show method in the class is used to display the frmLightbox form.

Public Sub Show()
    On Error GoTo ShowErrorHandler
    With DoCmd
        .Echo False
        .OpenForm CON_LIGHTBOX_FORM
    End With

ShowExit:
    DoCmd.Echo True
    Exit Sub

ShowErrorHandler:
    If (Err = 2501) Then
        Resume ShowExit
    Else
        ' return to the error
        Resume
    End If
End Sub

Resize Event

In the previous post, we used the Resize event of the form to set the opacity. Since we're handling this event from the LightboxForm class, we'll add this code here. This code also includes Renaud's updates to enable you to choose whether to cover the entire screen or just the Access window. I've modified this only slightly to maximize the lightbox form if the Access window is maximized (as determined by the IsZoomed API). If Access is not maximized, then we'll position the lightbox form to cover the Access window using the MoveWindow API.

Private Sub m_objForm_Resize()

    Dim lngStyle As Long
    Dim r        As RECT

    ' disable screen updates
    m_objForm.Painting = False

    ' If the Access window is maximized, then maximize the lightbox form.
    ' If the Access window is not maximized, then
    ' position the lightbox form so that it covers the Access window

    If IsZoomed(hWndAccessApp()) Then
        DoCmd.Maximize
    Else
        GetWindowRect Application.hWndAccessApp(), r
        MoveWindow m_objForm.hWnd, r.x1, r.y1, (r.x2 - r.x1), (r.y2 - r.y1), True
    End If

    ' get the current window style, then set transparency
    lngStyle = GetWindowLong(m_objForm.hWnd, GWL_EXSTYLE)
    SetWindowLong m_objForm.hWnd, GWL_EXSTYLE, lngStyle Or WS_EX_LAYERED
    SetLayeredWindowAttributes m_objForm.hWnd, 0, (m_sngOpacity * 255), LWA_ALPHA

    ' enable screen updates
    m_objForm.Painting = True

End Sub

Initialization

Lastly, there are a couple defaults we want to set in the class for opacity and back color:

Private Sub Class_Initialize()
    m_sngOpacity = 1         ' initialize opacity to 100% (not transparent)
    m_lngBackColor = vbBlack ' initialize back color to black
End Sub

Create supporting module

Since we're wrapping most of the functionality in a class module, we need an instance of the class. To do this, create a new standard module with the following code:

Private m_objLightbox As LightboxForm

Public Property Get LightboxForm() As LightboxForm
    If (m_objLightbox Is Nothing) Then
        Set m_objLightbox = New LightboxForm
    End If
    Set LightboxForm = m_objLightbox
End Property

To interact with the instance of the LightboxForm class, you'll use this LightboxForm property in the standard module.

Create the Lightbox form

We'll start this time by creating the lightbox form. This is the form that we'll use to display the transparency. Create a new form called frmLightbox and set the following properties:

  • AutoCenter = Yes
  • Popup = Yes
  • BorderStyle = None

The other properties and the back color are managed in the class.

Next, add the following code to the Open event of frmLightbox. This will "bind" the form to the class by setting the Form property of the class.

Private Sub Form_Open(Cancel As Integer)
    ' if the form is open in a remote desktop window, then
    ' cancel the lightbox form effect
    If (LightboxForm.OpenInRemoteDesktop) Then
        Cancel = True
        Exit Sub
    End If

    ' bind the lightbox form class
    Set LightboxForm.Form = Me
End Sub

Create the Login form

The last thing to do before we try this out is to create the login form. You can create a straight forward dialog form as was created in the previous post. This time however, we've modified the code in the Load and Unload events to use the LightboxForm property created in the previous step.

Create another form called frmLogin as described in the previous post. Add the following code to this form.

Private Sub Form_Load() 
    LightboxForm.BackColor = vbBlue
    LightboxForm.Opacity = 0.5
    LightboxForm.Show
End Sub

Private Sub Form_Unload(Cancel As Integer)
    If (Not LightboxForm.Form Is Nothing) Then
        If (CurrentProject.AllForms(LightboxForm.Form.Name).IsLoaded) Then
            DoCmd.Close acForm, LightboxForm.Form.Name, acSaveNo
        End If
    End If
End Sub

Try it out

Try this out by opening the Login dialog. Play around with different values for the .Opacity property and .BackColor property of the class. Here's a screen shot showing a green lightbox with 50% opacity:

TransparencyFormFollowUp

Many thanks to Renaud for extending the code and for a great post!

Office Blogs Comments

Comments: (22) Collapse

  • Very need indeed, but doesn’t seem to work properly on my system (XP).

    The login form seems to sit behind the lightbox form and seems not to be active. I can type information in the login box, but the buttons don’t work. Can you help?

  • Hi Rob, I'll have a hard time one-upping this now :-)

  • HEy guys this is great, i was wondering how to acomplish this, but you have come with a solution. I was wondering, can ypu make this in an downloadable example. It will be great to have such coded already implemented. You guys talk about renaud's class and Rob Cooper Love for class modules . . . Can you just create and publish it here, so we can all bennefit. What do you say?

  • Hi Rob, Thanks for sharing with us your great work !!, i´ve just implemented in a new project that I am working on.. By the way I post a code problem in the Wrox Forum, Do you have the opprotunity to read it ?? Thanks again .

    Maria

  • Opa-Horst: Make sure that the frmLogin form has the Popup and Modal properties set to Yes. Renaud: :) Thanks for your comments in the previous post! Edwin: Good suggestion! I'll see if this is something we can do. Maria: Thanks - glad to hear you'll be able to take advantage of this! I read the question in the Wrox forum and will try to respond through there. Rob

  • Yes, an example mdb would be helpful because I get an error in the line:

    Private m_objLightbox As LightboxForm no matter what version of Access I use.

  • Thanks Rob, login form wasn’t set to pop-up, changed it and it works beautifully now! Will apply it to my current project. Really great!

  • Opa-Horst: Thanks for the feedback! David: What is the error you are seeing?

  • For anyone interested, I have included Rob's code and made some minor improvements to my sample databases.

    You an get them from my blog, just click on my name above.

    Thanks again Rob for sharing this with us, it was enlightening :-)

  • Can this trick be implemented in the next version of Access so that no coding is needed?

  • Access 2007: Is there any function to highlight current row in continuous form? I'm talking about form with conditionally formatted fields, ie. not just a datasheet.

  • To Vladimir: you should have a look at some of the forums and websites like AccessMonster and the sites from the Access MVP (just search for it, there are a few interesting web sites).

    I'm not giving links here because it seems the comments don't get through.

    There is not -to my knowledge- any function to highlight the current row in a continuous form. There are a few tricks that can work using a textbox behind the other controls and conditional formatting to change its backcolor for the current record.

    Have a look at Stephen Lebans' website.

  • To Vladimir again: after experimenting a bit I got row highlighting working in a way that's simple and probably nicer than the usual stuff I've seen. In your continuous form, add a Panel and call it Highlighter, make it fit the size of the area to highlight (the size of the Detail section in the form). Send the Panel behind the other controls. Add the following to your Form's code (supposing that ID is the Unique ID field of your record): Dim currentId As Variant Private Sub Form_Current() currentId = ID

    End Sub Add the following code to the Detail section's OnPaint event: Private Sub Detail_Paint() If (ID = currentId) Then Highlighter.BackColor = vbYellow Else Highlighter.BackColor = vbWhite End If

    End Sub If anyone is interested I will make a post on this with a sample database.

  • Reneaud, and Vladimir. Reneaud, you said, :"Add a panel", what do you meant by a panel, i think you meant a textbox, correct? I have been trying with a lot of coding for the current row highlight, and i have seen the code of stephens , by the way great implementation. I was using lebans solution until this new verssion of access, the problem was that new verssion uses a different image format, than old verssions of access. I personally contacted stephen and he told me to use other solutions because his was outdated. Well, i started looking and in utter access i found a great solution, working with just a textbox(one by each color) and changing the expression inside the text box, and using a block character to fill the textbox. It just works, and is great, very light solution, the only problem is that it takes a lot of textboxes to acomplish what we need. Vladimir, did you were able to make the ribbon work, or to just enable the old command bars in your solution, we just left that conversation somewhere in space. Will like to know what happened?

  • Edwin: Still trying to get it to work. There's a bug in A2007: when I open 2002 MDB I can see my CommandBar - that's what I need; but when I save the MDB as ACDDB the CommandBar disappears - I can see other CommandBars instead. :-(

1 2  Next >
Comments

Comments: (loading) Collapse