New-ish REALbasic features (Part Six)

| | Comments (5)

Last time we saw the bulk of the fun -- how to put a dialog box onto the screen. Now we're going to extend that to make it useful. The first way we're going to do this is by allowing the user to interact with the dialog. When the user hits the OK button, or the close button (in the upper-right of the window), the dialog should close. But the caller should be able to differentiate between the two operations (in case they care). We're going to accomplish this with our knowledge of shared methods by providing a callback for the dialog procedure.

Looking at the MSDN documentation for DialogBoxIndirect, we see that we need to specify a DialogProc. Translating this into REALbasic parlance will look like this:

Private Shared Function DialogProc(hwnd as Integer, msg as Integer, wParam as Integer, lParam as Integer ) as Integer

If you're wondering why we're using a shared method, please refer to part four of the series. Now that we've gotten the declaration done, let's go hook it up to our dialog box call. Your call to DialogBoxIndirectParam should now read:

if System.IsFunctionAvailable( "DialogBoxIndirectParamW", "User32" ) then
  ret = DialogBoxIndirectParamW( moduleHandle, dlgTemplatePtr, self.Handle, AddressOf DialogProc, 0 )
else
  ret = DialogBoxIndirectParamA( moduleHandle, dlgTemplatePtr, self.Handle, AddressOf DialogProc, 0 )
end if

There! Now we've hooked up a dialog box procedure, we need to make it useful. There's really only two things we're interested in: what happens when the user clicks the close button, and what happens when the user clicks the OK button. In either case, we want to dismiss the dialog and return a value to the caller. This is accomplished by calling the EndDialog API -- and thankfully, that API also allows you to specify a return value. This is what the declare will look like:

Declare Sub EndDialog Lib "User32" ( hwnd as Integer, result as Integer )

The way to tell the user has clicked the close button is when we receive a WM_CLOSE message in our DialogProc. So let's take care of that case first:

Const WM_CLOSE = &h10
select case msg
case WM_CLOSE
  EndDialog( hwnd, 0 )
else
  return 0
end select
return 1

As you can see, we simply call EndDialog and pass in a result of 0. We're going to use 0 to mean that the user canceled the dialog, and 1 means that they've hit the OK button. You may be wondering why we return a value of 0 and 1 from DialogProc and when to do each. For this callback, a value of 1 means "we handled the message, don't do anything else" and a value of 0 means "we didn't handle it -- you take care of it." That's why our else clause of the select case returns 0 -- it means we didn't handle the message. All the other cases miss the else clause and end up returning 1.

If you run the example project, you'll see that you can now click the close button and the dialog will actually be dismissed. That's a good start! Now we're going to make the OK button useful. We do this by processing the WM_COMMAND message. When we get this message, the low WORD of wParam will specify the ID of the control that generated the command. If you look back at our template code, you'll see that we assigned an ID of 500 to the OK button. So we're going to grab the ID from the wParam and if it's the OK button, we're going to close the dialog with the result of 1.

Const WM_COMMAND = &h111
case WM_COMMAND
  // Note that we *want* this to truncate to the low word
  dim command as Int16 = wParam
  select case command
  case 500
    EndDialog( hwnd, 1 )
  else
    // We didn't handle this command, so do the default
    return 0
  end select
 

If you add that code to the example and run it, you'll see that the OK button now closes the dialog, which is exactly what we wanted. The only thing left to do now is check the return values for the call to DialogBoxIndirect to make sure the proper values are being returned. You'll see how we're storing the results into the variable ret -- let's do a MsgBox on the value to see what's actually returned.

Now you've got a fully functional dialog made entirely in code by using pure Win32 APIs! You can grab the updated project here.

But this dialog is rather ugly (don't you think?). Thankfully, you can still fix it up. Next time we're going to show how to specify a shell dialog font to be used for the entire dialog instead of the blocky system font.

5 Comments

Aaron, the formatting of this page is incorrect on Safari, Opera and FireFox. The code examples are being truncated after about 40 characters per line.

Yup, I'm aware of that -- it's why I'm hoping to find some kind soul who knows more about MT than I do to get syntax highlighting to work so people can view the source code better.

Any takers? Puh-lease? :-)

Thanks, Aaron. The most important thing about formatting is th

(just kidding!)

Har har har! :-P Feel free to step up to the plate funn guy! ;-)

Hm, instead of a pre (which requires browser specific CSS properties to wrap), how about a div with a monospace font? Only drawback is you'd have to do line breaks yourself.

http://archivist.incutio.com/viewlist/css-discuss/59351

Leave a comment

Disclaimer

I'm currently an employee of REAL Software. My blog is mine. The opinions represented in this blog are mine as well and may not represent my employer's opinions. All original material is copyrighted and property of the author.

REALbasic® is a registered trademark of REAL Software, Inc. REAL SQL Server™ and Lingua™ are pending trademarks of REAL Software, Inc. All rights reserved.