Introduction
Imagine you have a dialog, where you can enter text and other data related to the text. While the text should be formatted, the normal data entries should not. So you place a toolbar into your dialog, that has all the formatting buttons you need and find out, that it will dock only at the edges of your dialog. But I (the customer) wants this thing, where it belongs to: right over the format-able text entry.
To achieve the customer's need, there is a little more involved than just placing a button row in the middle of a form.
Creating the toolbar
Step one
Create a toolbar as usual and add a CToolBar
member to your dialog, giving it any name that suits you. For a formatting toolbar a name like m_wndFormatBar
sounds great.
Create a static control at that spot, where you want the toolbar to reside. Give this frame an ID (i.e. IDC_STC_TOOLBARFRAME
). Make it slightly bigger then the tools, so you can see the space that becomes occupied by the toolbar. Within class wizard assign a member to this control. Make sure it is assigned to CStatic
and not CString
. Name it whatever you want (like m_Stc_ToolbarFrame
).
Step two
Now comes the hard part. In the OnInitDialog
method of your dialog, place following code. You may omit the comments.
CSize sizeToolbar;
CRect mrect;
mrect.SetRectEmpty();
m_wndFormatBar.Create(this);
m_wndFormatBar.LoadToolBar(IDR_TOOLBAR_FORMAT);
m_wndFormatBar.SetBarStyle(CBRS_ALIGN_TOP | CBRS_TOOLTIPS | CBRS_FLYBY);
m_wndFormatBar.ShowWindow(SW_SHOW);
sizeToolbar = m_wndFormatBar.CalcFixedLayout(false,true);
m_Stc_ToolbarFrame.GetWindowPlacement(&wpl);
wpl.rcNormalPosition.bottom = wpl.rcNormalPosition.top +
sizeToolbar.cy + 4;
wpl.rcNormalPosition.right = wpl.rcNormalPosition.left +
sizeToolbar.cx + 4;
m_Stc_ToolbarFrame.SetWindowPlacement(&wpl);
m_wndFormatBar.SetWindowPlacement(&wpl);
m_Stc_ToolbarFrame.RepositionBars(AFX_IDW_CONTROLBAR_FIRST,
AFX_IDW_CONTROLBAR_LAST, 0);
m_Stc_ToolbarFrame.ShowWindow(SW_HIDE);
How this works
m_wndFormatBar.Create(this)
tells the toolbar, to which window the commands are to be sent. Loading the images, setting the bar styles and displaying the toolbar is quite obvious. Then you calculate the size of the toolbar, and adjust the size of the static control accordingly with some margin.
After having done heavy numerical considerations we place the static control and put the toolbar in its lap. m_Stc_ToolbarFrame.RepositionBars
tells the toolbar, which window it shall snuggle up to. Then make the static control disappear.
Omitting lines or doing the calculations wrong will be sentenced with weird optical results and strange behavior of your application. Check it out.
While the command routing is very simple, the visual updating of these commands is a little bit tricky.
Step three
Add the UpdateCommandUI
handlers to your dialog and edit the methods as you would, in every Frame/View application. Do not care, when they do not seem to work. Actually they can't.
Step four
Add the line:
#include "afxpriv.h"
at the top of the dialog's cpp file, or put it inside stdafx.h. In your dialog's header file put the line:
afx_msg LRESULT OnKickIdle(WPARAM, LPARAM);
inside the message map. The best place is a line between the
line and the DECLARE_MESSAGE_MAP()
line. If there is no line, then make one by pressing return. In your dialog's cpp file look for the word END_MESSAGE_MAP()
. Before that line, enter ON_MESSAGE(WM_KICKIDLE, OnKickIdle)
. If you forgot to include afxpriv.h the compiler will tell you, that it does not know anything about WM_KICKIDLE
. (By the way, does this message mean that someone is idly kicking?)
Add the body of the OnKickIdle
routine to your dialog class and call herein the CommandUpdateUI
handlers. How? Have a look.
LRESULT CArbitraryToolbarDlg::OnKickIdle(WPARAM, LPARAM)
{
CCmdUI cmdUI;
cmdUI.m_nID = ID_FORMAT_BOLD;
cmdUI.DoUpdate(this, FALSE);
cmdUI.m_nID = ID_FORMAT_DURCHSTRICH;
cmdUI.DoUpdate(this, FALSE);
cmdUI.m_nID = ID_FORMAT_KURSIV;
cmdUI.DoUpdate(this, FALSE);
cmdUI.m_nID = ID_FORMAT_UNTERSTRICH;
cmdUI.DoUpdate(this, FALSE);
cmdUI.m_nID = ID_EDIT_CUT;
cmdUI.DoUpdate(this, FALSE);
cmdUI.m_nID = ID_EDIT_COPY;
cmdUI.DoUpdate(this, FALSE);
cmdUI.m_nID = ID_EDIT_PASTE;
cmdUI.DoUpdate(this, FALSE);
return TRUE;
}
Step five...
Add the command handlers and test it. Drink a cup of coffee.
During this whole process you should compare your source code with mine. What you may adapt to your needs, what may not be altered I can't tell. This is taught only by experience, your experience.
The sample supplied has only the first seven buttons working. This is by design! After all, it is a sample not a full blown application.
The routines for squeezing the RichEditCtrl
will be coming soon with its own class. So stay tuned.
After having read so long, may I ask you a favour? My little brother has been sent to war in the middle east now, would you please pray for him, so he will return safe and sound? Thank you.