Archive

Archive for the ‘MFC’ Category

Some issues when working with ComboBox

August 11, 2009 2 comments

The main reason for posting this issue is to help MFC/Win32 beginners. But anyway it’s a good read ;), so once upon a time…

If you’re a beginner with window’s ComboBox controls then you might end up with a bald head thinking and thinking about a weird behavior of combo’s after it’s creation. The combo as such is a great control but there are certain important things to keep in mind while creating a combo.

So what are the problems? Well make it singular, “Problem”. One and the only problem with this control is shown in this screenshot…

Drop down not visible

Drop down not visible

I’ve clicked the drop down arrow but cannot see the dropdown. This one is created via resource editor. The problem here is, just adding a combo is not enough but we’ve also got to set the size of the drop down via resource editor. So follow these steps, as shown in the screen shot to fix this issue…

Resizing a combo drop down

Resizing a combo drop down

So that’s it. The white drag rectangle denotes combo drop down size. Press Ctrl + T to test this. Please note that this is not a bug but a feature of resource editor since there isn’t an another way to set drop down size for a combo via resource editor. But I agree that they should’ve set a better default drop down size instead of a zero height drop down.

Now this was via resource editor. It’s bit different when creating dynamically via code using CComboBox::Create function. Note that you have to give a valid height value. This height value will be the dropdown size for this combo.

See this e.g.

m_Combo.Create( WS_VISIBLE | WS_TABSTOP | WS_BORDER | CBS_DROPDOWNLIST | CBS_SORT,
                CRect( 10, 10, 200, 300 ),
                this,
                IDC_COMBO1 + 1 );
m_Combo.SetFont( GetFont(), TRUE );

Hope you’ve noticed the last big value given to Combo via CRect, 300. That’s the drop down height. Normally beginners set it to 30 or 40 which results in this issue.

See the resultant screenshot…

Result of properly creating a combo

Result of properly creating a combo

Not a big deal for experts but for beginners, well I’ve been a beginner too, it’s one painful issue. 🙂

Add horizontal scrollbar to a combo box

May 28, 2009 4 comments

CComboBox has a function called SetHorizontalExtent which doesn’t work. SDK equivalent is CB_SETHORIZONTALEXTENT which also doesn’t work. The reason for this bug is pretty lame, because WS_HSCROLL style for combo box is not set, which in turn the VS dialog editor does not provide :(. So a workaround is to open .rc file in a text editor and add WS_HSCROLL style manually for this combo.

This is how a combo box will look with a horizontal scrollbar…

Horizontal scrollbar in a combo box

Horizontal scrollbar in a combo box

Here is a sample code from MSDN which is adapted a bit for this purpose…

void CDialoTestDlg::AddHScroll( CComboBox& Cmb )
{
  // Find the longest string in the combo box.
  CString str;
  CSize sz;
  int dx = 0;

  TEXTMETRIC tm = { 0 };
  CDC* pDC = Cmb.GetDC();
  CFont* pFont = Cmb.GetFont();

  // Select the listbox font, save the old font
  CFont* pOldFont = pDC->SelectObject(pFont);

  // Get the text metrics for avg char width
  pDC->GetTextMetrics(&tm);

  for (int i = 0; i < Cmb.GetCount(); i++)
  {
    Cmb.GetLBText(i, str);
    sz = pDC->GetTextExtent(str);

    // Add the avg width to prevent clipping
    sz.cx += tm.tmAveCharWidth;

    if (sz.cx > dx)
      dx = sz.cx;
  }

  // Select the old font back into the DC
  pDC->SelectObject(pOldFont);
  Cmb.ReleaseDC(pDC);

  // Set the horizontal extent so every character of all strings can
  // be scrolled to.
  Cmb.SetHorizontalExtent(dx);
}// End AddHScroll

Setting horizontal extent is useful to prevent the combo’s drop down width from extending beyond screen limits. Note that there is another function called SetDroppedWidth which sets the drop down’s width but with no scrolling.

Where can I find MFC feature pack samples?

May 5, 2009 3 comments

People are having trouble finding feature pack samples installed in the samples directory. I too had a similar problem. So what you need to do is to uninstall the previous sample exe that you’ve installed. Then install the samples from here. Good thing about this is that you’ll have an updated samples package.

If you’re still having problems, I’ve uploaded them for you to my blog, please rename this file to zip after downloading or save as zip.

MFC Feature Pack – CMFCEditBrowseCtrl

April 15, 2009 3 comments

CMFCEditBrowseCtrl?

It’s a specialized edit control (MFC Feature Pack VS2008) with a browse button attached to it’s right side, when we click on this button we get an open file dialog or an open folder dialog. See this sample screenshot.

Browse edit control sample

Browse edit control sample

Also this control allows us to implement our own event handling by overriding OnBrowse function of this class. This a cool control which I like a bit too much since I know how painful it is to get one going.

Some tips on how to make such a control…

  1. Handle nc calcsize event, so that you can specify size of the area that the browse button will take which in turn results in edit control resizing it’s client area to adjust the button.
  2. Handle nc paint to draw the button, also you have to force generate an nc calcsize message for once in the beginning.
  3. Handle mouse up and mouse down event.

Usage

  1. You should be working in VS2008 SP1 or with Feature pack installation
  2. Add an edit control to a dialog
  3. Open .h file of this dialog’s class and add a member variable – CMFCEditBrowseCtrl m_EditBrowse;
  4. Open .cpp file and add a call to sub class this item in DoDataExchange
    DDX_Control( pDX, IDC_EDIT_FILEBROWSE, m_EditBrowse);
  5. Then in OnInitDialog
    // Note: Only one of these calls will work at a time!
    m_EditBrowse.EnableFileBrowseButton(); // To show file open dialog
    m_EditBrowse.EnableFolderBrowseButton(); // To show folder browse dialog
    m_EditBrowse.EnableBrowseButton(); // To do custom event handling
  6. That’s it, now you’ve got the browse edit working. 🙂
  7. Note that above calls take some parameters (which has default values) look up in MSDN.

Custom event handling for browse button

Here is a small sample on how to handle custom browse button event handling, taken from MSDN samples…

class CMyBrowseEdit : public CMFCEditBrowseCtrl
{
    virtual void OnBrowse()
    {
        MessageBox(_T("Browse item..."));
        SetWindowText(_T("New value!"));
    }
};

More…

Use SetBrowseButtonImage to change browse button image, also has an option to maintain such a bitmap or an icon. There are some more virtual functions which could be useful…

  1. OnDrawBrowseButton – Override to for some additional painting from your side
  2. OnChangeLayout – Called when browse button mode changes, i.e. from folder to file mode etc (I guess so). 🙂

Set resource handle in MFC and ATL

March 23, 2009 5 comments

When working in MFC/ATL and then using a resource dll can at times cause bugs because resource handle is not properly set. For e.g. if you have a dialog resource in a dll and you instantiate a CDialog which results in an error because resource cannot be located in this application or dll since it’s in the resource dll. So solution for above problem will be to set correct resource handle.

MFC provides two functions…

  1. AfxGetResourceHandle
  2. AfxSetResourceHandle

pretty obvious names. 🙂

So we should first save our current resource handle and then set new resource handle before loading such a resource. Also don’t forget to set our old handle since its good practice.

Sample code snippet.

extern HINSTANCE hResDll = NULL;
HINSTANCE hDefInstance = AfxGetResourceHandle();
AfxSetResourceHandle(hResDll);
CBitmap Bmp;
Bmp.LoadBitmap( IDB_NIBUS_FACE );

AfxSetResourceHandle(hDefInstance);

Internally MFC calls FindResource and LoadResource using this handle, so if it’s not properly set this will cause failure in loading resources.

In ATL it’s quite similar except that we’ve got a new function called AddResourceInstance which adds our new resource handle to existing list of handles. So when a look up is done given resource handle is also used. Following functions are provided in ATL to work with resource handles…

  1. AddResourceInstance
  2. GetResourceInstance – Similar to AfxGetResourceHandle
  3. SetResourceInstance – Similar to AfxGetSetResourceHandle

For newbies this is always a painful bug to resolve as they don’t know what went wrong since they expect this to be automagically done.

AfxGetMainWnd in worker threads

March 11, 2009 2 comments

Recently I faced an issue in one of my project code bases. There is a worker thread (created using CreateThread native API) which calls AfxGetMainWnd() MFC function and since worker threads doesn’t have a main window associated with it, CWnd::Create( …WS_CHILD… ) call fails. A window created with WS_CHILD needs a parent window but AfxGetMainWnd() returned NULL hence the crash.

So I searched using Google and found suggestions ranging from sending window handle as the custom parameter to the thread and then passing this on to the function were the Create function call is made! But that call is about 20 functions deep :roll:. The calls go from one class to another, this means I’ve got to carry this darn handle that far, naah I decided, I’m not going to do that. So I set about exploring other possibilities.

Then a pretty cool thought 💡 struck me, AfxGetMainWnd() calls AfxGetThread(), which returns NULL which in turn results in a NULL window handle. So I thought a possible solution will be to create a CWinThread object using AfxBeginThread(WorkerThread). Then another novel thought came that I can set the main window’s handle to m_pMainWnd member variable of this CWinThread object…

CWinThread* pThread = AfxBeginThread( (AFX_THREADPROC)ThreadProc,
                                      (LPVOID)this,
                                      THREAD_PRIORITY_NORMAL,
                                      0,
                                      CREATE_SUSPENDED,
                                      NULL );
pThread->m_pMainWnd = AfxGetMainWnd(); // Possibly already set by above call
pThread->ResumeThread();

So this does the trick for me and my project. Now calls to AfxGetMainWnd in this worker thread succeeds, haven’t seen any issue with this so far. If you find any, please post as a comment.

How to handle child window messages in a parent window?

Is there any way to handle child window messages in a parent window? Yes you can use WM_PARENTNOTFIY message or OnParentNotify MFC handler. Note that this message is sent or this function is only called when WS_EX_NOPARENTNOTIFY style is not set. A child controls notifies it’s parent window on…

  • Creation
  • Destruction
  • Mouse button clicks

Please go through following MSDN links for more information…

  1. WM_PARENTNOTIFY
  2. OnParentNotify
  3. A thread in MSDN forums on this topic
%d bloggers like this: