Re: Problem with BN_CLICKED and/or SetCapture by Dennis
Dennis
Thu Jul 17 01:11:49 CDT 2003
Frank,
I use owner draw (OD) buttons in my app. It is a C++ non-MFC program. I'm
running on PPC2002 and I've been developing with eVC 3.0.
When I first tried OD buttons on the PPC, I had a lot of problems and what
you've described sounds like one of the sequences I worked through. To
sort things out, I did some message trapping to see which messages were
being triggered. At each message, I would record all the incoming stuff
(WPARAM, LPARAM, etc.) and then break them down (LOWORD, HIWORD, etc.). I
won't bore you with all the details through, really, you should dig through
things at this level at least once. You may be surprised at how things are
happening differently than you imagined.
This is how I run now:
Here's where messages to my dialog box come in.
// --------------------------------------------------------
LONG CStckDet::DoMsgs( HWND hw, UINT uMsg, WPARAM wP, LPARAM lP )
// --------------------------------------------------------
{
switch( uMsg )
{
case WM_COMMAND: return( DoMsgWM_COMMAND ( hw, uMsg, wP, lP ) );
case WM_ERASEBKGND: return( g_pUtl->DoMsgWM_ERASEBKGND ( m_hwDlg, HDC(
wP ), LT_GRAY ) );
case WM_CTLCOLORSTATIC: return( DoMsgWM_CTLCOLORSTATIC ( wP, lP ) );
case WM_PAINT: return( DoMsgWM_PAINT ( hw ) );
case WM_DRAWITEM: return( DoMsgWM_DRAWITEM ( wP, lP ) );
default: return( MSG_NOT_PROCESSED );
};
}
I further separate the WM_COMMAND stuff here. All the OD stuff we are
concerned with routes on to DoBtnDetType.
// --------------------------------------------------------
LONG CStckDet::DoMsgWM_COMMAND( HWND hw, UINT uMsg, WPARAM wP, LPARAM lP )
// --------------------------------------------------------
//...all commands for this dialog box will be sent here before
// being routed onward.
{
switch( CTRL_ID )
{
case IDOK: return( DoBtnOK ( hw, uMsg, wP, lP ) );
//...the left side buttons
case IDC_BTN_DET_GEN: return( DoBtnGen ( hw, uMsg, wP, lP ) ); // FIX
case IDC_BTN_DET_PIC: return( DoBtnPic ( hw, uMsg, wP, lP ) );
case IDC_BTN_DET_BOT: return( DoBtnBot ( hw, uMsg, wP, lP ) );
case IDC_BTN_DET_COM: return( DoBtnCom ( hw, uMsg, wP, lP ) );
case IDC_BTN_DET_LOC: return( DoBtnLoc ( hw, uMsg, wP, lP ) );
case IDC_BTN_DET_DET: return( DoBtnDet ( hw, uMsg, wP, lP ) );
case IDC_BTN_DET_SGN: return( DoBtnSgn ( hw, uMsg, wP, lP ) );
//...the central definable buttons
case IDC_BTN_DEFINE_1: return( DoBtnDef1 ( hw, uMsg, wP, lP ) );
case IDC_BTN_DEFINE_2: return( DoBtnDef2 ( hw, uMsg, wP, lP ) );
//...the remaining central controls
case IDC_BTN_DET_TREE:
case IDC_BTN_DET_SHRB:
case IDC_BTN_DET_VINE:
case IDC_BTN_DET_PERN:
case IDC_BTN_DET_EVGN:
case IDC_BTN_DET_DCID:
case IDC_BTN_DET_JAPM:
case IDC_BTN_DET_RHOD:
case IDC_BTN_DET_H2OH:
case IDC_BTN_DET_H2OM:
case IDC_BTN_DET_H2OL:
case IDC_BTN_DET_HDGE:
case IDC_BTN_DET_GNCV:
case IDC_BTN_DET_RKGD:
case IDC_BTN_DET_SUNH:
case IDC_BTN_DET_SUNM:
case IDC_BTN_DET_SUNL:
case IDC_BTN_DET_WEEP:
case IDC_BTN_DET_FLOW:
case IDC_BTN_DET_COLM:
case IDC_BTN_DET_FRUT:
case IDC_BTN_DET_DWRF:
case IDC_BTN_DET_NATV:
case IDC_BTN_DET_SALE:
case IDC_BTN_DET_GRAS: return( DoBtnDetType ( hw, uMsg, wP, lP ) );
case IDC_DAT_PIC:
case IDC_DAT_PIC2: return( DoDatPic( hw, uMsg, wP, lP ) );
default: return( MSG_NOT_PROCESSED );
};
}
Here's where I actually deal with the OD stuff. odBtn is an index into a
memory based table (gl_BTN) of data about my OD buttons. Stuff I only want
to setup once and then always go to the table later to get. DoUpdateBtn is
what causes the button to get redrawn.
// --------------------------------------------------------
LONG CStckDet::DoBtnDetType( HWND hw, UINT uMsg, WPARAM wP, LPARAM lP )
// --------------------------------------------------------
{
if ( NOTIFY_CODE == BN_CLICKED )
{
OD_BTNDET odBtn = DoXlateCtrl( wP );
//...toggle the button clicked and update its image
gl_BTN[ odBtn ].BtnState = ( gl_BTN[ odBtn ].BtnState == BTNDN ) ? BTNUP
: BTNDN;
DoUpdateOdBtn( odBtn );
return( MSG_PROCESSED );
}
else return( MSG_NOT_PROCESSED );
}
And here's where I force the button's redraw.
// --------------------------------------------------------
void CStckDet::DoUpdateOdBtn( OD_BTNDET BtnIndx )
// --------------------------------------------------------
//...by disabling and then reenabling the button, we force Windows to
// issue a WM_DRAWITEM message with member itemAction = ODA_DRAWENTIRE
// and with member itemState = 0 (undefined). it is this message and
// members combination which, when recieved, causes us to redraw the
// button's bitmap for a visual update.
{
EnableWindow( ( gl_BTN + BtnIndx )->hBtn, FALSE );
EnableWindow( ( gl_BTN + BtnIndx )->hBtn, TRUE );
}
Now we need to go back up to the top where we came in and follow the
WM_DRAWITEM message's path. If you do that, you will see that the message
goes to DoMsgWM_DRAWITEM which is shown next. If you don't discriminate
among the various control types, actions and states associated with the
WM_DRWITEM message correctly and react to the just ones you need to, you can
get badly tangled up in your underware in this area. Most of the stuff
after the opening if statement here probably won't be of interest. The if
statement makes the *essential* discrimination and only processes the
WM_DRAWITEM message when all the necessary auxilliary conditions (CtlType,
itemAction and itemState) are correct.
// --------------------------------------------------------
LONG CStckDet::DoMsgWM_DRAWITEM( WPARAM wCtrlID, LONG lP )
// --------------------------------------------------------
//...flow of control comes here whenever the OS believes that an
// owner-draw item we are responsible for may need redrawing.
// we can make it believe this by calling the DoUpdateOdBtn()
// subr.
{
LPDRAWITEMSTRUCT lpdis = LPDRAWITEMSTRUCT( lP );
//...determine if this is something we want to redraw.
if ( lpdis->CtlType == ODT_BUTTON &&
lpdis->itemAction == ODA_DRAWENTIRE &&
lpdis->itemState == UNDEFINED )
{
_TCHAR szLbl[ MINBUFSIZ ];
_TCHAR szSubr[] = _T( "CStckDet::DoMsgWM_DRAWITEM" );
HDC hdcBtn = lpdis->hDC;
OD_BTNDET BtnIdx = DoXlateCtrl( wCtrlID );
UINT uLblLen;
SIZE sizLbl;
BOOL fRslt;
g_pUtl->DoLdStr( ( gl_BTN + BtnIdx )->uStrID, szLbl, MINBUFSIZ ); // load
btn string text
uLblLen = _tcslen( szLbl ); // load btn string length
fRslt = GetTextExtentPoint( hdcBtn, szLbl, uLblLen, &sizLbl ); // get
size of text
if ( fRslt == SUCCESS ) // if get size OK then
{
RECT rcBtn = lpdis->rcItem; // shorter form
HFONT hFontPrev = HFONT( SelectObject( hdcBtn, m_hTxtFont ) ); // load
button font
BTNSTATE BtnState = ( gl_BTN + BtnIdx )->BtnState;
//...write the button text out to the button's device context
// centering it vertically and horizontally.
fRslt = ExtTextOut( hdcBtn, // btn device context
( ( rcBtn.right - sizLbl.cx ) >> 1 ) + 0, // x offset in dc
( ( rcBtn.bottom - sizLbl.cy ) >> 1 ) + 0, // y offset in dc
0, // no options defined
NULL, // no opt. rect used
szLbl, // string to write
uLblLen, // length of string
NULL ); // no special chr spacing
SelectObject( hdcBtn, hFontPrev ); // restore prev font
if ( fRslt == SUCCESS ) // if text write OK then
{
HDC hdcMem = CreateCompatibleDC( lpdis->hDC ); // make memory dc
BTNBMP bbIdx = ( gl_BTN + BtnIdx )->BmpIdx; // get bitmap index
HBITMAP hBmp = ( BtnState == BTNDN ) ?
( gl_BtnBmps + bbIdx )->hDnBmp :
( gl_BtnBmps + bbIdx )->hUpBmp; // get bitmap up or dn
version
SelectObject( hdcMem, hBmp ); // load bitmap to memory dc
BitBlt( hdcBtn, // handle of button cd (dest)
rcBtn.left, // dest rect x
rcBtn.top, // dest rect y
rcBtn.right - rcBtn.left, // dest rect dx
rcBtn.bottom - rcBtn.top, // dest rect dy
hdcMem, // handle of memory dc (srce)
0, // srce rect x
0, // srce rect y
SRCAND ); // raster operation to perform
DeleteDC( hdcMem ); // done with memory dc
}
else g_pRpt->Err( IDS_EL_ERR, gl_szFile, szSubr, _T( "ExtTextOut" ),
GetLastError() );
}
else g_pRpt->Err( IDS_EL_ERR, gl_szFile, szSubr, _T(
"GetTextExtentPoint" ), GetLastError() );
return( MSG_PROCESSED );
}
return( MSG_NOT_PROCESSED );
}
Hope that helps, Frank. It works for me.
--
Dennis Gallagher
Monroe, WA, USA
"Frank Apap" <gunygoogoo@yahoo.com> wrote in message
news:1oiRa.2440$P36.509765@news4.srv.hcvlny.cv.net...
> I have a problem that my owner drawn buttons cannot be clicked twice in a
> row in any quick manner. I figured out (on my windows port of the
program)
> that if I add case BN_DOUBLECLICKED: my problem disapeared on windows. So
I
> would have:
>
> case BN_DOUBLECLICKED:
> case BN_CLICKED:
> {
> do stuff
> }
>
> On the PPC there is now bn_doubleclicked. So then I tried capturing
> WM_LBUTTONDBLCLK; that didn't work unless I used SetCapture(mainwin);.
Then
> once I used SetCapture BN_CLICKED events stopped occuring...I'm going
> crazy!!! Could anyone explain the right way to do this?
>
> Thanks
> Frank
>
>
>