![]() |
Platforms, Frameworks & Libraries »
WTL »
General
Intermediate
Ultima Online Treasure Hunter ToolsBy Tim SmithUOTH is collection of little bleeding edge UI techniques. Oh, and a tool for UO games players. |
VC7, VC7.1, VC8.0Win2K, WinXP, ATL, WTL, Dev
|
|
Advanced Search |
|
|
|
||||||||||||||||

I bet you are asking yourself "What in the world is a stupid tool for an online game doing on Code Project?" This program is my entry in the New C++ Competition. UO Treasure Hunter Tools (UOTH) uses VC7, WTL7 and ATL7 as its core technology.
So what makes UOTH worthy of consideration for the new competition? Well, there isn't one thing in UOTH that stands out as great or innovative programming. However, what UOTH does contain is a diverse collection of smaller bleeding edge UI and programming techniques.
Following are short descriptions of some of the programming highlights contained in UOTH.
Toolbars these days are much more complex than just the simple 16 color, 16x15 bitmap images. As programmers we have to deal with large and small image sets. We have to deal with the display of text under the button or to the right. We also have to deal with allowing the user to configure which style of toolbar he likes best.
Luckily for the programmer, the common controls provide us with complete support for the advance toolbar color and text options common in today's application. However, a few minor points of interest are left out.
If you right click on an IE6 toolbar and select "customize" you will be presented with the standard toolbar customization dialog. However, this dialog includes two extra combo boxes at the bottom that allow the user to specify the text and icon options. At first glance, it would be reasonable to assume that IE6 includes their own private customization dialog, but that turns out not to be the case.
If you look at the method CMainWnd::OnCustomizeToolbar file MainWnd.cpp, it contains the notification handler for the toolbar. Specifically, the handling of the TBN_INITCUSTOMIZE notification contains a hack used to get the window of the toolbar customization dialog.
// // Ok, this is an UNDOCUMENTED hack. The initialize message // actually contains the handle of the dialog for customization. // typedef struct hack_tagNMTOOLBARINIT { NMHDR hdr; HWND hWndDialog; } hack_NMTOOLBARINIT; hack_NMTOOLBARINIT *pNMHack = (hack_NMTOOLBARINIT *) pnmh; HWND hDlg = pNMHack ->hWndDialog;
As well noted in the code, the TBN_INITCUSTOMIZE notification structure actually contains the handle of the customization dialog. As with any undocumented feature, it is always a risky proposition to utilize it. However, given that IE6 uses this, I doubt it is going away any time soon.
Once we have the handle to the customization dialog, we can create a child dialog inside this dialog. The source code shows exactly how to do this.
Customizing the "Save As" dialog is very common these days. However, many developers just place their controls in their customization dialogs without regard to how they align with the other controls inside the "Save As" dialog. The sad part is, this is trivial. The CUOAMExportDlg class contains an example of customizing the "Save As" dialog. We are going to look specifically at what is required to properly reposition controls. This example assumes our customization dialog will appear below the rest of the "Save As" dialog.
Every control on the "Save As" dialog has specific control IDs that we can depend on being constant. These IDs are defined in the system include file "dlgs.h". We can use these control IDs to locate controls on the "Save As" dialog and reposition our controls relative to their positions.
In UOTH, the repositioning of the controls is handled by a routine named RepositionControl (and to think, I am someone who claims self documenting code is a myth). In this example, since our customization dialog is at the bottom, all we are concerned about is the horizontal position and size of our controls. Their vertical position and size is dictated by their position and size in our dialog.
//-------------------------------------------------------------------------- // // @mfunc Reposition a control // // @parm CWindow & | wnd | Control to be reposition // // @parm UINT | nID | ID of the control used for positioning // // @parm bool | fSize | If true, adjust the width of the control // // @rdesc None. // //-------------------------------------------------------------------------- void CUOAMExportDlg::RepositionControl (CWindow &wnd, UINT nID, bool fSize) { // // Get the window rect in the client area of the // control we are interested in. // CWindow wndParent = GetParent (); CWindow wndAnchor = wndParent .GetDlgItem (nID); CRect rectAnchor; wndAnchor .GetWindowRect (&rectAnchor); wndParent .ScreenToClient (&rectAnchor); // // Reposition the control // DWORD dwSWFlags = SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOSIZE; CRect rectCtrl; wnd .GetWindowRect (&rectCtrl); ScreenToClient (&rectCtrl); rectCtrl .OffsetRect (rectAnchor .left - rectCtrl .left, 0); if (fSize) { rectCtrl .right = rectCtrl .left + rectAnchor .Width (); dwSWFlags &= ~SWP_NOSIZE; } wnd .SetWindowPos (NULL, rectCtrl .left, rectCtrl .top, rectCtrl .Width (), rectCtrl .Height (), dwSWFlags); return; }
Supplied to this routine is the window of the control to be repositioned, the ID of the "Save As" dialog control, and a flag stating if we wish for our control to be resized. As you can see from the code, it is actually a simple process. This routine is invoked during dialog initialization and resize.
One other final note. If you have a control, such as another button that needs to be placed below the "Ok" button, you might run into problems with the resize grip in the lower right corner of the dialog. The third block of code in the OnInitDialog method takes care of this problem.
In many applications, you might find the need to place a common dialog elements in multiple locations. In UOTH, the filter dialog is not only used as it's own independent dialog, but also appears in the "Print..." dialog and the "UOAM Export..." dialog. A normal programmer's natural reaction would be to duplicate the code for the dialog in multiple places. However, with some simple adjustments a modal framed dialog can act like a child dialog.
The first thing to consider when a dialog is to act as both a modal framed dialog and a child dialog is that in the case of a child dialog, a WM_COMMAND message for the IDOK and IDCANCEL buttons are never received. Since the programmer is in total control of the dialog, this is a trivial problem to resolve. Instead of placing all the code to save the dialog settings in an OnOK routine, place the code in another routine that will be invoked by OnOK and the dialog that will using this dialog as a child dialog. For users of DDX, this would be very trivial. In my case, I don't use DDX since I have always found it to be more of a hassle than benefit.
Handling how data is saved when the user presses the "Ok" button is only half the battle. In order to use the dialog as a child window, you have to invoke the Create method on the dialog instead of DoModal. However, the dialog resource is still setup for the dialog to be displayed as a popup with a dialog frame. Luckily, with a little bit of code, this can be fixed dynamically. The following is the CFilterDlg::Create method.
//-------------------------------------------------------------------------- // // @mfunc Create a modeless dialog // // @parm HWND | hWndParent | Parent window // // @parm LPARAM | dwInitParam | Initialization param // // @rdesc Window handle // //--------------------------------------------------------------------------- HWND CFilterDlg::Create (HWND hWndParent, LPARAM dwInitParam) { // // Find the region // HRSRC hRsrc = FindResource (_Module .GetResourceInstance (), MAKEINTRESOURCE (IDD), RT_DIALOG); if (hRsrc == NULL) return NULL; // // Get the size of the resource // DWORD dwSize = ::SizeofResource (_Module .GetResourceInstance (), hRsrc); // // Allocate the global memory to contain the regions // HGLOBAL hTemplate = ::GlobalAlloc (GPTR, dwSize); if (hTemplate == NULL) return NULL; DLGTEMPLATE *pTemplate = (DLGTEMPLATE *) ::GlobalLock (hTemplate); DLGTEMPLATEEX *pTemplateEx = (DLGTEMPLATEEX *) pTemplate; // // Load and lock the resource // HGLOBAL hSource = ::LoadResource (_Module .GetResourceInstance (), hRsrc); LPVOID pSource = ::LockResource (hSource); memcpy (pTemplate, pSource, dwSize); UnlockResource (hSource); ::FreeResource (hSource); // // Adjust the flags // DWORD dwStyle = WS_CHILD | WS_VISIBLE | WS_BORDER | WS_DLGFRAME | DS_3DLOOK | DS_FIXEDSYS | DS_SETFONT | DS_CONTROL; DWORD dwExStyle = 0; if (pTemplateEx ->signature == 0xFFFF) { pTemplateEx ->exStyle = dwExStyle; pTemplateEx ->style = dwStyle; } else { pTemplate ->dwExtendedStyle = dwExStyle; pTemplate ->style = dwStyle; } // // Create the window // ATLASSERT (m_hWnd == NULL); _AtlWinModule .AddCreateWndData (&m_thunk.cd, (CDialogImplBaseT <CWindow> *) this); #ifdef _DEBUG m_bModal = false; #endif //_DEBUG HWND hWnd = ::CreateDialogIndirectParam ( _AtlBaseModule.GetResourceInstance(), pTemplate, hWndParent, StartDialogProc, dwInitParam); ATLASSERT (m_hWnd == hWnd); // // If we created the window, delete OK and CANCEL :) // if (m_hWnd) { ::DestroyWindow (GetDlgItem (IDOK)); ::DestroyWindow (GetDlgItem (IDCANCEL)); } // // Unlock the globals // ::GlobalUnlock (hTemplate); ::GlobalFree (hTemplate); return hWnd; }
The first thing that must be done is to load the dialog resource into volatile memory. This allows us to modify the create parameters. Next, the style and extended style flags are changed to force the dialog to display as a child window with no border. Finally, after the dialog is created, the OK and CANCEL buttons are removed.
This code will create the dialog at the default window position. The dialog will need to be repositioned to the proper place by the parent window. This is usually done in the WM_SIZE message handler.
Tim has been a professional programmer for way too long. He currently works at a company he co-founded that specializes in data acquisition software for industrial automation.
| You must Sign In to use this message board. | |||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||
General
News
Question
Answer
Joke
Rant
Admin
|
PermaLink |
Privacy |
Terms of Use
Last Updated: 31 Jul 2002 Editor: Nishant Sivakumar |
Copyright 2002 by Tim Smith Everything else Copyright © CodeProject, 1999-2009 Web16 | Advertise on the Code Project |