Image of Navigational Map linked to Home / Contents / Search Formula One - Please Call(back) Home

by David Thompson - GUI Computing
Image of Line Break

We use Formula One a lot, and it's a very flexible and useful tool. But, as with all tools, there are sometimes things you need to do which the designers never thought of... which is where using Callback functions can come in very handy.

Auto-Resizing Columns

The Formula One grid allows users to resize columns when the cursor is between two columns at the top of the grid. What it doesn't do is automatically resize the column immediately to the left of the cursor when the user double clicks. And it doesn't even generate an event in this situation to allow you to do it yourself. This article describes how to get this behaviour into your Formula One grids.

Firstly, we need to generate an event when the user double clicks on the grid. To do this, I used mhCallBack from VBTools. Any control, from MSGBLAST to SpyWorks, will do so long as it allows you to get at windows messages that VB normally doesn't allow. I just happened to be using some other VBTools controls, so mhCallBack made sense.

To set it up, I first put a Formula One grid and an mhCallBack control on a form and set the Style property of the Callback control to '3 - MsgFilter Hook.' On the load event of the form, we tell the callback control which window and which messages we are interested in.

sub Form_Load()

   gSS = Sheet.SS

   CallBack.hWndSubclass = Sheet.hWnd
   CallBack.MsgList(0) = WM_LBUTTONDBLCLK

End Sub

Now each time our user double clicks on the grid, we get a SubclassMsg event on the callback control. In this event, we have to determine if a column is currently being resized. The easiest way to do this is to look at the cursor, with the GetCursor Windows API function. The value of the resize cursor handle in Formula One is 5230. If GetCursor returns this value, then we know a column is being resized. The only thing left to do is to work out which column the cursor is on, and to use the SSSetColWidthAuto function to do the auto resize.

Sub CallBack_SubclassMsg (Msg As Integer, wParam As Integer, lParam As Long, ReturnVal As Long)
   Dim iCol As Integer
   Dim iResult As Integer

      If GetCursor() = 5230 Then
         iCol = GetColAtPoint()
         iResult = SSSetColWidthAuto(gSS, -1, iCol, -1, iCol, True)
      End If
   End If

End Sub

The GetColAtPoint function returns the column immediately to the left of the cursor. The GetCursorPos function gets the coordinates of the cursor and places them into a point structure. The ScreenToClient function adjusts the coordinates so that they are relative to the top left corner of the grid. The resulting X value is then converted to Twips so that it can be compared using the SSRangeToTwips function in Formula One.

Function GetColAtPoint () As Integer

   Dim xTwips As Long
   Dim lOffsetX As Long
   Dim lTwipsX As Long
   Dim iCol As Integer
   Dim lp As POINTAPI

   GetCursorPos lp
   ScreenToClient Sheet.hWnd, lp
   lTwipsX = lp.X * Screen.TwipsPerPixelX

   For iCol = 1 To Sheet.MaxCol
      SSError SSRangeToTwips(gSS, 1, iCol, 1, iCol, lOffsetX, 0, 0, 0, 0)
      If Abs(lOffsetX - lTwipsX) < 500 Then
         GetColAtPoint = iCol - 1
      End If
   Next iCol
End Function

And you might need these.

    x As Integer
    y As Integer
End Type

Declare Sub GetCursorPos Lib "User" (lpPoint As POINTAPI)
Declare Function GetCursor Lib "User" () As Integer
Declare Sub ScreenToClient Lib "User" (ByVal hWnd As Integer, lpPoint As POINTAPI)

Global Const WM_LBUTTONDBLCLK = &H203

Enabling and Disabling Paste

Another thing we need to do quite regularly with Formula One is to catch the WM_INITMENU message. WM_INITMENU is sent when a menu is opened and before it is displayed. This is a good time to enable and disable menu items. - no surprise there. We need to disable paste for lots of reasons, all related to the way Formula One handles the clipboard - but that's another story.

Again, we first set up the callback control.

Sub Form_Load()

   CallBack2.hWndSubclass = hWnd
   CallBack2.MsgList(0) = WM_INITMENU

End Sub

Again, ensure that the style property of the callback is set to '3 - MsgFilter Hook'. Formula One has a really handy function called SSCanEdit Paste which we can use to enable or disable the paste menu

Sub CallBack2_SubclassMsg (Msg As Integer, wParam As Integer, lParam As Long, ReturnVal As Long)
   Dim iResult As Integer
   Dim iPaste As Integer

   If Msg = WM_INITMENU Then
      iResult = SSCanEditPaste(gSS, iPaste)
      mnuEditPaste.Enabled = iPaste
   End If

End Sub

Now your paste menu will be correctly enabled or disabled, even if your user copies data from other applications. This technique can also be used for cut and copy as well as setting fixed rows with SSGetFixedRows and SSSetFixedRows.

Written by: David Thompson
March '97

Image of Arrow linked to Next Article
Image of Line Break