CELL Worksheet Function

I learned something today. I “knew” that omitting the optional reference argument from the CELL worksheet function meant that the cell that contained the worksheet function was used. Patrick McDonald correctly pointed out that it uses the last changed cell, not the host cell.

In a new worksheet, type

<span class="text">=CELL("width")</span>

in cell B3. If you’re using Excel 2003 like me, you’ll probably get 8 as the answer. Now increase the width of column D and type a ’1′ in D3. Cell B3 will report the width of D3 because it’s the last cell changed.


I like learning things. I don’t like learning things that I should have already known. Hopefully this is the last one and that now I know everything.

Password Userform Module

This was inspired by Dick Kusleika’s post Masking Passwords (http://www.dailydoseofexcel.com/archives/2011/07/12/masking-passwords/). It’s an enhanced version of DK’s approach in the sense that the same userform module supports both entering a new password as well as entering a password for confirmation. There are other enhancements. The user can choose to either mask or unmask the password. The developer has more choices about what is and is not allowed in the password.

For a version in a page by itself (i.e., not in a scrollable iframe as below) visit http://www.tushar-mehta.com/publish_train/xl_vba_cases/1061%20Password%20Userform%20module.shtml

Tushar Mehta

Extract the last token in a cell

There are times when one wants to extract the last part of a string, say the file name from a string that contains the filename including the path. This short note describes a few ways to do that.

The example we will use is the following. Cell A1 contains the value c:xyz.jpg. What we want is the yz.jpg part.

This note describes three approaches to this task.

For a version in a page by itself (i.e., not in a scrollable iframe as below) visit http://www.tushar-mehta.com/publish_train/xl_vba_cases/0131%20Extract%20last%20token.shtml

Tushar Mehta

Masking Passwords

Why do I keep creating userforms to get passwords? That’s it. This is the last time I’m doing it. From now on, I’ll just import UPassword.frm

Here’s the code behind the form. Nothing special, I’m just tired of typing it.

Option Explicit

Private mbUserCancel As Boolean
Private msPassword As String

Public Property Get Password() As String: Password = msPassword: End Property
Public Property Let Password(ByVal sPassword As String): msPassword = sPassword: End Property
Public Property Get UserCancel() As Boolean: UserCancel = mbUserCancel: End Property
Public Property Let UserCancel(ByVal bUserCancel As Boolean): mbUserCancel = bUserCancel: End Property

Private Sub EnableDisable(ctl As MSForms.CommandButton, bEnable As Boolean)
   
    ctl.Enabled = bEnable
   
End Sub

Private Sub cmdCancel_Click()
   
    Me.UserCancel = True
    Me.Hide
   
End Sub

Private Sub cmdOK_Click()
   
    Me.Password = Me.tbxPassword.Text
    Me.UserCancel = False
    Me.Hide
   
End Sub

Private Sub tbxPassword_Change()
   
    Const lMINLENGTH As Long = 4
   
    EnableDisable Me.cmdOK, Len(Me.tbxPassword.Text) >= lMINLENGTH
   
End Sub

‘Example of how to use.  Put this in a standard module
‘Public Function GetPassword() As String

‘    Dim ufPassword As UPassword

‘    Set ufPassword = New UPassword

‘    ufPassword.Show

‘    With ufPassword
‘        If Not .UserCancel Then
‘            GetPassword = .Password
‘        End If
‘    End With

‘    Unload ufPassword
‘    Set ufPassword = Nothing

‘End Function

‘Sub Test_GetPassword()

‘    Dim lUser As Long

‘    lUser = Val(GetPassword)

‘    If lUser > 0 Then
‘        Debug.Print lUser
‘    End If

End Sub

TRANSPOSE Changes Array Dimension

Here’s an odd one, I think. I have code that creates a two-dimensional array and I want to use

<span class="vb">Application.WorksheetFunction.Transpose</span>

to switch the rows and columns.

Sub TestTrans()
   
    Dim aTest(0 To 3, 0 To 1) As Long
    Dim vaTrans As Variant
    Dim i As Long, j As Long
   
    For i = 0 To 3
        For j = 0 To 1
            aTest(i, j) = (10 ^ i) * (j + 1)
        Next j
    Next i
   
    vaTrans = Application.WorksheetFunction.Transpose(aTest)
   
    Stop
   
End Sub

No problem. I check the locals window at Stop and it seems to have done the trick. I didn’t know it converted it to one-based arrays, but I guess that’s OK.

I ran into a situation today where my two-dimensional array only had one column dimension. When I ran it through Transpose, it came back as a one-dimensional array.

That’s not what I want. And it broke my code. I can’t tell you how much code I have that uses Transpose. Honestly, I can’t! That’s the problem. If I knew, I would go fix it.

Although the above simple example doesn’t demonstrate it, I need to Transpose because I’m using a dynamic array. I have to keep the last dimension variable or Redim Preserve won’t work. But in reality the dynamic portion of my array is the “row” portion, so I transpose it at the end. I guess I’ll have to go steal Chip’s transpose code and try to find all the places I’ve used this method.

So who’s got code that searches all the VBA on my hardrive?

Enable or Disable custom Ribbon buttons in Excel 2007-2010

Hi all

FYI, I add this page last weekend to my site :

Enable or Disable custom Ribbon buttons in Excel 2007-2010
http://www.rondebruin.nl/enable.htm

I also have a page for hiding and displaying :

Hide or Display Custom Ribbon Tab/Group/Control with getVisible
http://www.rondebruin.nl/hidevisible.htm

If you have suggestions or good or bad comments let me know(I am no expert)

Ron de Bruin
http://www.rondebruin.nl/tips.htm

ByVal or ByRef Parameters

Procedure arguments, either for a subroutine or a function, are declared either as ByVal or ByRef. The use of ByVal means that the called procedure does not want to change the value of the argument passed to it whereas the use of ByRef means that the called procedure may change the value.
This note looks at how the called procedure and the calling routine are affected by three different kinds of arguments passed ByVal and passed ByRef.

For a version in a page by itself (i.e., not in a scrollable iframe as below) visit http://www.tushar-mehta.com/publish_train/xl_vba_cases/1004%20ByVal%20ByRef.shtml

Tushar Mehta

Understanding the ParamArray

The ParamArray argument makes it possible for a procedure (a function or a subroutine) to accept an arbitrary number of arguments, each of a possibly different type. This enables the developer to write a function that will work with one or two or however many arguments that the user of the function passes to it. When used with a User Defined Function (UDF), it works as an Excel function.

image001

For a version in a page by itself (i.e., not in a scrollable iframe as below) visit http://www.tushar-mehta.com/publish_train/xl_vba_cases/1005%20ParamArray.shtml

Tushar Mehta

Opening Paint files in XL

The Great Wave of Kanagawa
Specifically, MacPaint files. Why MacPaint? 1. With a maximum size of 576 pixels wide by 720 pixels tall, MacPaint images fit on a modern spreadsheet when using cells as pixels, and 2. MacPaint prototyped the lossless PackBits compression algorithm, which is the default compression scheme for TIFF.

PackBits is a signed-byte file structure with flag-counter bytes interspersed with data bytes. If the flagbyte is negative, the next byte (always a data byte) is repeated, zero-based, the two’s-complement of the flagbyte times. If the flagbyte is a positive number, the next number of bytes are copied directly. Each data byte represents the status of eight pixels, with 1 indicating a black pixel and 0 representing a white pixel, as shown below.

The PackBits algorithm is defined here. The following code, cross-posted to Wikipedia, handles Apple’s demonstration case.

Sub UnpackBitsDemo()

   Dim File    As Variant
   Dim MyOutput As String
   Dim Count   As Long
   Dim i As Long, j As Long
 
   File = “FE AA 02 80 00 2A FD AA 03 80 00 2A 22 F7 AA”
   File = Split(File, ” “)
   
   For i = LBound(File) To UBound(File)
      Count = Application.WorksheetFunction.Hex2Dec(File(i))
      Select Case Count
      Case Is >= 128
         Count = 256 – Count ‘Two’s Complement
         For j = 0 To Count ‘zero-based
            MyOutput = MyOutput & File(i + 1) & ” “
         Next j
         i = i + 1 ‘Adjust the pointer
      Case Else
         For j = 0 To Count ‘zero-based
            MyOutput = MyOutput & File(i + j + 1) & ” “
         Next j
         i = i + j ‘Adjust the pointer
      End Select
   Next i
   Debug.Print MyOutput
   ’AA AA AA 80 00 2A AA AA AA AA 80 00 2A 22 AA AA AA AA AA AA AA AA AA AA’
End Sub

It works out then that a PackBits stream is a flagbyte followed by data bytes followed by a flagbyte followed by data bytes… Each flagbyte indirectly points to the location of the next flagbyte.
 
Excel’s VBA does not have signed bytes, but if we look at the flagbyte’s value, and “Select Case” all flagbytes greater or equal to 128 (b10000000) we have a test that handles negative flag bytes. The two’s complement of a number N is 2B-N, where B is the word size, in our case, 8. In other words, the two’s-complement of the negative flagbyte is 256 minus the value of the flagbyte.
 
The MacPaint file has a 512-byte header that we can ignore. It also can have, as data bytes, internal bytes that would otherwise be read as end-of-file. We handle that by reading the file in for binary access.
 
The basic approach is to read in the file similarly to a text file. Turn the flagbytes characters into ascii, with the ascii as the count to copy or to take the two’s complement of. Byte(513) has to be a flagbyte. The data bytes are input as characters, turned to ascii, then turned to byte-strings. The cells are turned black or white by looping through the byte-string. After 576 1′s and 0′s (or 72 data bytes) a new row is started. The file structure does not reveal the length of the data, but we can infer that from the file’s length.

Sub OpenMacPaint()
   Dim TotalBytes As Long
   Dim Buffer As String
   Dim File    As Variant
   Dim Char  As String * 1
   Dim NextChar As String * 1
   Dim NextInt As Integer
   Dim NextByte As String * 8
   Dim Count   As Long
   Dim i As Long, j As Long, r As Long, c As Long, b As Long
   Dim Rng As Range
   
   File = Application.InputBox(“Enter the full path to the MacPaint file.”, “Path to the MacPaint file…”, _
      “Macintosh HD:Users:User:Downloads:GREAT WAVE.mac”, , , 2) ‘ Your path here
   If File = False Then Exit Sub

   Open File For Binary Access Read As #1
   TotalBytes = FileLen(File)
   Buffer = Input(TotalBytes, #1)
   Close #1
   If TotalBytes = 0 Then
      MsgBox “Exiting!”, vbCritical + vbOKOnly, “File not found!”
      Exit Sub
   End If
   
   c = 1
   r = 1
   Application.ScreenUpdating = False
   For i = 513 To TotalBytes ‘skip the header
      Char = VBA.Mid$(Buffer, i, 1)
      Count = Asc(Char)
      Select Case Count
         Case Is >= 128
            Count = 256 – Count ‘Two’s Complement
            NextChar = VBA.Mid$(Buffer, i + 1, 1)
            NextInt = Asc(NextChar)
            NextByte = Application.WorksheetFunction.Dec2Bin(NextInt, 8)
            For j = 0 To Count ‘zero-based repeat of the next byte
                For b = 1 To 8
                    If VBA.Mid$(NextByte, b, 1) = “1″ Then
                        Worksheets(“Sheet4″).Cells(r, c).Interior.ColorIndex = 1 ‘Black
                    Else
                        Worksheets(“Sheet4″).Cells(r, c).Interior.ColorIndex = 2 ‘White
                    End If
                    c = c + 1
                    If c > 576 Then ‘a new row
                        c = 1
                        r = r + 1
                    End If
                Next b
            Next j
            i = i + 1 ‘adjust the counter
         Case Else
            For j = 0 To Count ‘zero-based copy of Count bytes
                NextChar = VBA.Mid$(Buffer, i + j + 1, 1)
                NextInt = Asc(NextChar)
                NextByte = Application.WorksheetFunction.Dec2Bin(NextInt, 8)
                For b = 1 To 8
                    If VBA.Mid$(NextByte, b, 1) = “1″ Then
                        Worksheets(“Sheet4″).Cells(r, c).Interior.ColorIndex = 1
                    Else
                        Worksheets(“Sheet4″).Cells(r, c).Interior.ColorIndex = 2
                    End If
                    c = c + 1
                    If c > 576 Then ‘a new row
                         c = 1
                         r = r + 1
                    End If
                Next b
            Next j
            i = i + j ‘adjust the counter
         End Select
   Next i
   
   Set Rng = Worksheets(“Sheet4″).Range(“A1:VE720″)
   Rng.ColumnWidth = 1
   Rng.RowHeight = 12
   ActiveWindow.Zoom = 10
   Application.ScreenUpdating = True
Exit Sub

What I don’t like about the code is that for the case of Count>=128, the repetitive bytes, I’m parsing it Count times. Rather I should parse it once and copy it Count times. I just don’t see how. I know somebody out there does.
 
If you want the MacPaint copy of Hokusai’s The Great Wave off Kanagawa shown above, it’s here. A nice thing about the Excel code is that you can change the color indices to suit. An appropriate blue might be color index 32, RBG(0,0,255).

To show why PackBits is still used, the lossless JPEG used above for HTML purposes is 211 Kbytes. The lossless Mac counterpart is 17 Kbytes. While a MacPaint file can define 576*720 pixels (0.4 megapixels), its maximum size is 512 + (72*90) bytes, or just over 51 Kbytes.

 
…mrt

A Binary Clock in Excel

A binary clock, or more accurately a binary-coded decimal clock, shows each digit in binary. I first saw one at a friend’s place and it served as an interesting ice breaker.

The below image is from Excel. Specifically, it is an Excel chart, though most would be hard pressed to recognize it as such. It represents the time 14:30:26 (i.e., 26 seconds past 2:30 pm).

image002

For a version in a page by itself (i.e., not in a scrollable iframe as below) visit http://www.tushar-mehta.com/publish_train/xl_vba_cases/0907%20Binary%20Clock.shtml

Tushar Mehta