您的位置:首页 > 其它

<<出现问题的自绘组合框>>的分析和修正

2015-06-21 23:25 387 查看


<<出现问题的自绘组合框>>http://download.csdn.net/detail/wxf041041/7414585

在csdn下载中, 有一个demo, 描述了自绘CComboBox出现的问题.
下载了Demo看了一下,是参考别的工程, 写的Demo.
Debug模式下报错. 报错问题比较明显, 是lpDrawItemStruct->itemID无效引起的问题.
这个Demo是去年上传的, 不知道这个问题他解决了没有, 我分析并修正了一下

修正后的工程下载点 : owerncombox_fixed_2015_0621_2314.rar

发现的问题和分析如下:
在Debug模式下, 直接运行程序, 程序报错, 并没有断言.



单步一下, 到报错时, 可以看到出错的语句为 :
void CMyComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
	 ASSERT(lpDrawItemStruct->CtlType == ODT_COMBOBOX );

	 CDC dc;
	 dc.Attach(lpDrawItemStruct->hDC);
	 CRect rcItem(lpDrawItemStruct->rcItem);
	 
	 int nCount = GetCount();

	 CString csItem;
	 GetLBText(lpDrawItemStruct->itemID, csItem); ///< ! 执行这句过后, 就异常了
在watch窗口可以看到 lpDrawItemStruct->itemID = -1, 是个无效值, 报错的直接原因是这样
-		lpDrawItemStruct	0x0016f180 {CtlType=0x00000003 CtlID=0x00000064 itemID=0xffffffff ...}	tagDRAWITEMSTRUCT *
		CtlType	0x00000003	unsigned int
		CtlID	0x00000064	unsigned int
		itemID	0xffffffff	unsigned int ///< ! 直接的报错原因
		itemAction	0x00000001	unsigned int
		itemState	0x00001000	unsigned int


itemID无效的原因是: 填充数据后, 没有设置当前条目.
修正如下:
/// BOOL COwerncomboxDlg::OnInitDialog()
    /// ...
	m_mycom2.Create(WS_VISIBLE|WS_CHILD|CBS_DROPDOWNLIST|CBS_OWNERDRAWFIXED|CBS_HASSTRINGS, CRect(110,60,160,85),this,100);
	m_mycom2.AddString("1");
	m_mycom2.AddString("2");
	m_mycom2.AddString("3");
 	m_mycom2.AddString("4");
	m_mycom2.AddString("5");
    m_mycom2.SetCurSel(0); ///< 设置 ItemID = 0, 防止 CMyComboBox::DrawItem()中, GetLBText(lpDrawItemStruct->itemID, csItem); 报错
好, 现在运行ok了, 剩下的就是完善细节.



我说下解决这类问题的思路:
* 这些问题,都是人家搞过的,找些开源的工程, 关于CComboBox自绘的, 看看人家的基本用法, 有啥区别?
从这个Demo看, 用法也是从开源工程或M$帮助中摘录出来的. e.g. codeproject上的Demo, 一般都是说明一个Demo说明一个特色, 很容易找出区别.
e.g. 人家的Demo运行正常, 我从那个Demo摘录出来的功能不正常,这区别, 应该是好找的.
* 如果找不出区别,那单步下, 看看走到哪报错了. 再看看人家正常运行的Demo, 走到相应的地方参数值都有什么区别.
然后再分析参数值为啥不同?
* 如果自己的用法和人家的用法稍有区别, 可以将自己的用法先整的和人家Demo相同, 看看运行是否正常。

总之, 就是一个做实验的过程. 不断的缩小问题的范围,这类直接报错的BUG, 是比较容易搞定的.

下面是我修正后的工程源码的预览.



//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by owerncombox.rc
//
#define IDM_ABOUTBOX                    0x0010
#define IDD_ABOUTBOX                    100
#define IDS_ABOUTBOX                    101
#define IDD_OWERNCOMBOX_DIALOG          102
#define IDR_MAINFRAME                   128
#define IDC_COMBO1                      1000
#define IDC_MY_COMBO                    1001 ///< ! 自绘控件ID还是搞一个不会和别的控件或对话框重复的值

// Next default values for new objects
// 
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE        129
#define _APS_NEXT_COMMAND_VALUE         32771
#define _APS_NEXT_CONTROL_VALUE         1002
#define _APS_NEXT_SYMED_VALUE           101
#endif
#endif
初始化自绘控件
BOOL COwerncomboxDlg::OnInitDialog()
{
    int iPosLeft = 0;
    int iPosTop = 0;
    int iPosRight = 0;
    int iPosBottom = 0;

    CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	
    /// 控件没有处理滚动条, 滚动条应该也要自己画的
    /// 现在只能用键盘来滚动
    iPosLeft = 110;
    iPosTop = 60;
    iPosRight = iPosLeft + 50;
    /// 高度应该>=5行, 否则点击下拉框时, 数据显示不出来, 设成5行后,下拉框能显示3行
    iPosBottom = iPosTop + m_mycom2.getRowHeight() * 5;
	m_mycom2.Create(
        WS_VISIBLE | WS_CHILD | CBS_DROPDOWNLIST | CBS_OWNERDRAWFIXED | CBS_HASSTRINGS, 
        CRect(iPosLeft, iPosTop, iPosRight, iPosBottom), 
        this,
        IDC_MY_COMBO); ///< 这里的控件ID不能和resource.h中使用过的ID冲突

	m_mycom2.AddString("1");
	m_mycom2.AddString("2");
	m_mycom2.AddString("3");
 	m_mycom2.AddString("4");
	m_mycom2.AddString("5");
    m_mycom2.SetCurSel(0); ///< 必须在填好数据后,设置当前Item, 否则 CMyComboBox::DrawItem 处ItemId为-1, 引起无效ItemId不处理

	return TRUE;  // return TRUE  unless you set the focus to a control
}


自绘控件的.h
#if !defined(AFX_MYCOMBOBOX_H__89EA435C_83F5_43AB_BD37_19B38DB628FB__INCLUDED_)
#define AFX_MYCOMBOBOX_H__89EA435C_83F5_43AB_BD37_19B38DB628FB__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// MyComboBox.h : header file
//

/////////////////////////////////////////////////////////////////////////////
// CMyComboBox window

class CMyComboBox : public CComboBox
{
// Construction
public:
	CMyComboBox();

// Attributes
public:

// Operations
public:
    int getRowHeight()
    {
        return m_rowHeight;
    }

// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CMyComboBox)
	public:
	virtual void DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct);
    virtual int CompareItem(LPCOMPAREITEMSTRUCT lpData); ///< 应该重载这个函数, 因为CComboBox中有断言
	virtual void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct);
	//}}AFX_VIRTUAL

// Implementation
public:
	virtual ~CMyComboBox();

	// Generated message map functions
protected:
	//{{AFX_MSG(CMyComboBox)
		// NOTE - the ClassWizard will add and remove member functions here.
	//}}AFX_MSG

	DECLARE_MESSAGE_MAP()

private:
    int const m_rowHeight;
};

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MYCOMBOBOX_H__89EA435C_83F5_43AB_BD37_19B38DB628FB__INCLUDED_)


自绘控件的.cpp
// MyComboBox.cpp : implementation file
//

#include "stdafx.h"
#include "owerncombox.h"
#include "MyComboBox.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CMyComboBox

CMyComboBox::CMyComboBox() :
m_rowHeight(20) ///< 行高20ppx
{
}

CMyComboBox::~CMyComboBox()
{
}

BEGIN_MESSAGE_MAP(CMyComboBox, CComboBox)
	//{{AFX_MSG_MAP(CMyComboBox)
		// NOTE - the ClassWizard will add and remove mapping macros here.
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CMyComboBox message handlers

void CMyComboBox::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) 
{
    BOOL bRowWasSelected = FALSE;
    int iRowCnt = 0;
    CString csItem;
    CRect rcItem;
    CDC dc;

    do 
    {
        if ((NULL == lpDrawItemStruct)
            || (ODT_COMBOBOX != lpDrawItemStruct->CtlType))
        {
            break;
        }
        
        iRowCnt = GetCount();

        /// 必须判断是否为为有效的Item, 否则在GetLBText处报错
        if ((lpDrawItemStruct->itemID < 0) 
            || (lpDrawItemStruct->itemID > (iRowCnt - 1)))
        {
            break;
        }

        dc.Attach(lpDrawItemStruct->hDC);

        rcItem = CRect(lpDrawItemStruct->rcItem);
        GetLBText(lpDrawItemStruct->itemID, csItem); ///< 如果 lpDrawItemStruct->itemID 无效, 有断言

        bRowWasSelected = (lpDrawItemStruct->itemState & ODS_SELECTED) ? TRUE : FALSE;
        if (bRowWasSelected)
        {
            dc.FillSolidRect(rcItem, RGB(238, 238, 238));
            dc.SetTextColor(RGB(255, 0, 0));
        }
        else
        {
            dc.FillSolidRect(rcItem, RGB(218, 218, 218));
            dc.SetTextColor(RGB(0, 255, 0));
        }

        dc.DrawText(csItem, csItem.GetLength(), rcItem, DT_LEFT | DT_SINGLELINE);
        dc.Detach(); ///< !
    } while (0);
}

int CMyComboBox::CompareItem(LPCOMPAREITEMSTRUCT lpData)
{
    return 0;
}

void CMyComboBox::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) 
{
	// TODO: Add your code to determine the size of specified item
	if (lpMeasureItemStruct->itemID == -1)
    {
		lpMeasureItemStruct->itemHeight = 16;
    }
	else
    {
		lpMeasureItemStruct->itemHeight = m_rowHeight; ///< 按照20ppx作为行高
    }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: