Trouble with Owner Draw Combos
Jeff Lindholm -- JeffL@inter-intelli.com Wednesday, October 23, 1996 Environment: VC++ 4.2 Windows NT 4.0 I am trying to make an OwnerDraw combo box with icons and text. I am not sorting the data in the combobox. My problem is that when you drop down the list the items are not painted until you either scroll past them or move the mouse over an item. The first item or the selected item is always displayed. Thanks for any help; Jeff Here is the ComboBox code: CDrawComboBox::CDrawComboBox() { } CDrawComboBox::~CDrawComboBox() { } BEGIN_MESSAGE_MAP(CDrawComboBox, CComboBox) //{{AFX_MSG_MAP(CDrawComboBox) // NOTE - the ClassWizard will add and remove mapping macros here. //}}AFX_MSG_MAP END_MESSAGE_MAP() //////////////////////////////////////////////////////////////////////// ///// // CDrawComboBox message handlers void CDrawComboBox::Initialize() { m_Images.Create(16, 16, FALSE, 1, 1); HICON icon; icon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); m_Images.Add(icon); icon = AfxGetApp()->LoadIcon(IDI_ICON1); m_Images.Add(icon); } int CDrawComboBox::CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct) { return 0; } void CDrawComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { if ( lpDrawItemStruct->itemID == -1 ) { return; } CDC dc; dc.Attach(lpDrawItemStruct->hDC); int nState = dc.SaveDC(); int nImage = GetItemData(lpDrawItemStruct->itemID); // Get the text string for this item out of the combo box CString strText; GetLBText(lpDrawItemStruct->itemID, strText); // Determine colors-selected disk shown with blue background, etc if (ODS_SELECTED & lpDrawItemStruct->itemState) { // Select the appropriate text colors dc.SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT)); dc.SetBkColor(GetSysColor(COLOR_HIGHLIGHT)); } else { dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT)); dc.SetBkColor(GetSysColor(COLOR_WINDOW)); } int nImageWidth = 16; RECT& r = lpDrawItemStruct->rcItem; // Draw the text & bounding rectangles with proper colors dc.ExtTextOut(lpDrawItemStruct->rcItem.left + nImageWidth + 4, lpDrawItemStruct->rcItem.top, ETO_CLIPPED | ETO_OPAQUE, &lpDrawItemStruct->rcItem, strText, lstrlen(strText), (LPINT)NULL); // Now draw the bitmap associated with the disk drive CPoint p(lpDrawItemStruct->rcItem.left, lpDrawItemStruct->rcItem.top); if (ODS_SELECTED & lpDrawItemStruct->itemState) { m_Images.Draw(&dc, nImage, p, ILD_SELECTED); } else { m_Images.Draw(&dc, nImage, p, ILD_TRANSPARENT); } if ((ODA_FOCUS & lpDrawItemStruct->itemAction) || (ODS_FOCUS & lpDrawItemStruct->itemState)) { DrawFocusRect(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem); } dc.RestoreDC(nState); } void CDrawComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) { lpMeasureItemStruct->itemHeight = 16; }
Kostya Sebov -- sebov@is.kiev.ua Friday, November 01, 1996 [Mini-digest: 2 responses] > Environment: VC++ 4.2 Windows NT 4.0 > > I am trying to make an OwnerDraw combo box with icons and text. I am not > sorting the data in the combobox. My problem is that when you drop down > the list the items are not painted until you either scroll past them or > move the mouse over an item. The first item or the selected item is > always displayed. > > Thanks for any help; > Jeff > > Here is the ComboBox code: > > CDrawComboBox::CDrawComboBox() > { > } > > CDrawComboBox::~CDrawComboBox() > { > } > > > BEGIN_MESSAGE_MAP(CDrawComboBox, CComboBox) > //{{AFX_MSG_MAP(CDrawComboBox) > // NOTE - the ClassWizard will add and remove mapping macros here. > //}}AFX_MSG_MAP > END_MESSAGE_MAP() > > //////////////////////////////////////////////////////////////////////// > ///// > // CDrawComboBox message handlers > > void CDrawComboBox::Initialize() > { > m_Images.Create(16, 16, FALSE, 1, 1); > HICON icon; > icon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); > m_Images.Add(icon); > icon = AfxGetApp()->LoadIcon(IDI_ICON1); > m_Images.Add(icon); > } > > int CDrawComboBox::CompareItem(LPCOMPAREITEMSTRUCT lpCompareItemStruct) > { > return 0; > } > > void CDrawComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) > { > if ( lpDrawItemStruct->itemID == -1 ) > { > return; > } > > CDC dc; > dc.Attach(lpDrawItemStruct->hDC); > int nState = dc.SaveDC(); > > int nImage = GetItemData(lpDrawItemStruct->itemID); > > // Get the text string for this item out of the combo box > CString strText; > GetLBText(lpDrawItemStruct->itemID, strText); > > // Determine colors-selected disk shown with blue background, etc > if (ODS_SELECTED & lpDrawItemStruct->itemState) > { > // Select the appropriate text colors > dc.SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT)); > dc.SetBkColor(GetSysColor(COLOR_HIGHLIGHT)); > } > else > { > dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT)); > dc.SetBkColor(GetSysColor(COLOR_WINDOW)); > } > > int nImageWidth = 16; > RECT& r = lpDrawItemStruct->rcItem; > > // Draw the text & bounding rectangles with proper colors > dc.ExtTextOut(lpDrawItemStruct->rcItem.left + nImageWidth + 4, > lpDrawItemStruct->rcItem.top, > ETO_CLIPPED | ETO_OPAQUE, > &lpDrawItemStruct->rcItem, > strText, lstrlen(strText), > (LPINT)NULL); > > // Now draw the bitmap associated with the disk drive > CPoint p(lpDrawItemStruct->rcItem.left, lpDrawItemStruct->rcItem.top); > if (ODS_SELECTED & lpDrawItemStruct->itemState) > { > m_Images.Draw(&dc, nImage, p, ILD_SELECTED); > } > else > { > m_Images.Draw(&dc, nImage, p, ILD_TRANSPARENT); > } > > > if ((ODA_FOCUS & lpDrawItemStruct->itemAction) || (ODS_FOCUS & > lpDrawItemStruct->itemState)) > { > DrawFocusRect(lpDrawItemStruct->hDC, &lpDrawItemStruct->rcItem); > } > > dc.RestoreDC(nState); > } > > void CDrawComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) > { > lpMeasureItemStruct->itemHeight = 16; > } > > I believe the bug lives in the following lines og your code: CDC dc; dc.Attach(lpDrawItemStruct->hDC); The thing is that that Windows reuses a single HDC for drawing all the items in the owner-draw combo. On the other hand, you're attaching this reusable HDC to a local C++ object, whose destructor calls something like DeleteDC when the object goes out of scope. That's why the first item gets drawn and prevents the others from being drawn since the HDC is no longer valid. You can solve this problem by calling cd.Detach(); just before the function returns. This will decouple the two entities and saves the HDC. However the better way of coding (IMO) is to use a temporarily allocated CDC: CDC* pDC = CDC::FromHandle( lpDrawItemStruct->hDC); ASSERT(pDC); The later one is just an MFC alias to the system object, whict I think is exactly what yoiu need here. HTH --- Kostya Sebov. ---------------------------------------------------------------------------- Tel: (38 044) 266-6387 | Fax: (38 044) 266-6195 | E-mail: sebov@is.kiev.ua -----From: John.Bell@xcellenet.com Environment: VC++ 4.1 Windows NT 4.0 Jeff: I had the same problem until I replaced the line -dc.Attach(lpDrawItemStruct->hDC); in CDrawComboBox::DrawItem() with -CDC *pdc = CDC::FromHandle(lpDrawItemStruct->hDC); then it worked on Windows95 and Windows NT 4.0. John john.bell@xcellenet.com
Dulepov Dmitry -- dima@ssm6000.samsung.ru Tuesday, November 05, 1996 [Mailer: "Groupware E-Mail". Version 1.01.035] I remember I used CDC::Attach() with lpDIS->hDC and in 16-bit app u= nder Win 3.11 (debug) I got "Attempt to delete object owned by the = system" message on Debug Messages Logger (DbWin.Exe). In your code simply add: dc.Detach(); To Jeff: this explains why CDC::FromHandle() works -> their HDCs ne= ver deleted. Dima. ----------------------------- > [From: Kostya Sebov > [Address: sebov@is.kiev.ua > [To: Dmitry A. Dulepov > [CC: mfc-l@netcom.com > [Date: Mon Nov 04 08:04:31 1996 >[Mini-digest: 2 responses] > >> Environment: VC++ 4.2 Windows NT 4.0 >> >> I am trying to make an OwnerDraw combo box with icons and text= =2E I am not >> sorting the data in the combobox. My problem is that when you = drop down >> the list the items are not painted until you either scroll pas= t them or >> move the mouse over an item. The first item or the selected it= em is >> always displayed. >> >> Thanks for any help; >> Jeff >> >> Here is the ComboBox code: >> >> CDrawComboBox::CDrawComboBox() >> { >> } >> >> CDrawComboBox::~CDrawComboBox() >> { >> } >> >> >> BEGIN_MESSAGE_MAP(CDrawComboBox, CComboBox) >> //{{AFX_MSG_MAP(CDrawComboBox) >> // NOTE - the ClassWizard will add and remove mapping = macros here. >> //}}AFX_MSG_MAP >> END_MESSAGE_MAP() >> >> //////////////////////////////////////////////////////////////= ////////// >> ///// >> // CDrawComboBox message handlers >> >> void CDrawComboBox::Initialize() >> { >> m_Images.Create(16, 16, FALSE, 1, 1); >> HICON icon; >> icon =3D AfxGetApp()->LoadIcon(IDR_MAINFRAME); >> m_Images.Add(icon); >> icon =3D AfxGetApp()->LoadIcon(IDI_ICON1); >> m_Images.Add(icon); >> } >> >> int CDrawComboBox::CompareItem(LPCOMPAREITEMSTRUCT lpCompareIt= emStruct) >> { >> return 0; >> } >> >> void CDrawComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct= ) >> { >> if ( lpDrawItemStruct->itemID =3D=3D -1 ) >> { >> return; >> } >> >> CDC dc; >> dc.Attach(lpDrawItemStruct->hDC); >> int nState =3D dc.SaveDC(); >> >> int nImage =3D GetItemData(lpDrawItemStruct->itemID); >> >> // Get the text string for this item out of the combo box= >> CString strText; >> GetLBText(lpDrawItemStruct->itemID, strText); >> >> // Determine colors-selected disk shown with blue backgrou= nd, etc >> if (ODS_SELECTED & lpDrawItemStruct->itemState) >> { >> // Select the appropriate text colors >> dc.SetTextColor(GetSysColor(COLOR_HIGHLIGHTTEXT)); >> dc.SetBkColor(GetSysColor(COLOR_HIGHLIGHT)); >> } >> else >> { >> dc.SetTextColor(GetSysColor(COLOR_WINDOWTEXT)); >> dc.SetBkColor(GetSysColor(COLOR_WINDOW)); >> } >> >> int nImageWidth =3D 16; >> RECT& r =3D lpDrawItemStruct->rcItem; >> >> // Draw the text & bounding rectangles with proper colors= >> dc.ExtTextOut(lpDrawItemStruct->rcItem.left + nImageWidth = + 4, >> lpDrawItemStruct->rcItem.top, >> ETO_CLIPPED | ETO_OPAQUE, >> &lpDrawItemStruct->rcItem, >> strText, lstrlen(strText), >> (LPINT)NULL); >> >> // Now draw the bitmap associated with the disk drive >> CPoint p(lpDrawItemStruct->rcItem.left, lpDrawItemStruct->= rcItem.top); >> if (ODS_SELECTED & lpDrawItemStruct->itemState) >> { >> m_Images.Draw(&dc, nImage, p, ILD_SELECTED); >> } >> else >> { >> m_Images.Draw(&dc, nImage, p, ILD_TRANSPARENT); >> } >> >> >> if ((ODA_FOCUS & lpDrawItemStruct->itemAction) || (ODS_FOC= US & >> lpDrawItemStruct->itemState)) >> { >> DrawFocusRect(lpDrawItemStruct->hDC, &lpDrawItemStruct= ->rcItem); >> } >> >> dc.RestoreDC(nState); >> } >> >> void CDrawComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureI= temStruct) >> { >> lpMeasureItemStruct->itemHeight =3D 16; >> } >> >> >I believe the bug lives in the following lines og your code: > CDC dc; > dc.Attach(lpDrawItemStruct->hDC); > >The thing is that that Windows reuses a single HDC for drawing all= the items >in the owner-draw combo. On the other hand, you're attaching this = reusable >HDC to a local C++ object, whose destructor calls something like D= eleteDC when >the object goes out of scope. > >That's why the first item gets drawn and prevents the others from = being drawn >since the HDC is no longer valid. > >You can solve this problem by calling cd.Detach(); just before the= function >returns. This will decouple the two entities and saves the HDC. > >However the better way of coding (IMO) is to use a temporarily all= ocated CDC: > CDC* pDC =3D CDC::FromHandle( lpDrawItemStruct->hDC); > ASSERT(pDC); > >The later one is just an MFC alias to the system object, whict I t= hink is >exactly what yoiu need here. > >HTH > > > > >--- = >Kostya Sebov. = >------------------------------------------------------------------= ---------- >Tel: (38 044) 266-6387 | Fax: (38 044) 266-6195 | E-mail: sebov@is= =2Ekiev.ua >-----From: John.Bell@xcellenet.com > >Environment: VC++ 4.1 Windows NT 4.0 > >Jeff: > >I had the same problem until I replaced the line >-dc.Attach(lpDrawItemStruct->hDC); >in CDrawComboBox::DrawItem() with >-CDC *pdc =3D CDC::FromHandle(lpDrawItemStruct->hDC); >then it worked on Windows95 and Windows NT 4.0. > >John >john.bell@xcellenet.com=
| Вернуться в корень Архива |