|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionI 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. Fancy Toolbar CustomizationToolbars 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 // // 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 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. Explorer Style Customization of the "Save As" Dialog.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 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 //-------------------------------------------------------------------------- // // @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 Hoisting one Dialog Into AnotherIn 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 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 //-------------------------------------------------------------------------- // // @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 Other Code of Note (The Brown Nose Section)
About the AuthorTim 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.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||