Program examples compiled using Visual C++ 6.0 compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this tutorial are listed below. Don’t forget to read Tenouk’s small disclaimer. The supplementary notes for this tutorial are cdib.h, cdib.cpp, formatetc and IDataObject.
The MYMFC30A Example: A Data Object Clipboard
This example uses the CDib class from Module 21 (MYMFC26C). Here you'll be able to move and resize the DIB image with a tracker rectangle, and you'll be able to copy and paste the DIB to and from the clipboard using a COM data object. The example also includes functions for reading DIBs from and writing DIBs to BMP files. If you create such an example from scratch, use AppWizard to create MDI, without any ActiveX or Automation options and then add the following line in your StdAfx.h file:
|
Add the following call at the start of the application's InitInstance() function:
To prepare MYMFC30A, open the Mymfc30a.dsw workspace and then build the project. Run the application, and paste a bitmap into the rectangle by choosing Paste From on the Edit menu. You'll see an MDI application similar to the one shown in Figure 2.
Figure 2: The MYMFC30A program in operation. |
The MYMFC30A Steps From Scratch
The following are the steps to build MYMFC30A program from scratch. This is MDI without Automation and ActiveX support. The View base class is CScrollView.
Figure 3: MYMFC30A – Visual C++ new project dialog.
Figure 4: MYMFC30A – AppWizard step 1 of 6, select Multiple documents option.
Figure 5: MYMFC30A – AppWizard step 2 of 6.
For step 3 of 6, don’t forget to deselect the Automation and ActiveX Controls check boxes.
Figure 6: MYMFC30A – AppWizard step 3 of 6.
Figure 7: MYMFC30A – AppWizard step 4 of 6.
Figure 8: MYMFC30A – AppWizard step 5 of 6.
Change the CView to CScrollView base class for CMymfc30aView class.
Figure 9: MYMFC30A – AppWizard step 6 of 6.
Figure 10: MYMFC30A project summary.
Firstly, add the CDib class (device independent bitmap).
Figure 11: Adding new class to project.
Figure 12: Adding an empty cdib.h, a header file to project.
Copy and paste the cdib.h file content. Repeat the same step but select the C++ Source File for cdib.cpp. Here, we do not use the ClassWizard to add new class, so you won’t find the CDib class in ClassWizard, though the ClassWizard database file (CLW) can be rebuilt to include the class.
Figure 13: Adding cdib.cpp, a source file to project.
Add menu items under the Edit menu of the IDR_MYMFC3TYPE. Use the information in the following Table.
ID |
Menu caption |
Prompt |
Separator |
- |
- |
ID_EDIT_COPYTO |
Copy T&o |
Copy directly to a BMP file |
ID_EDIT_PASTEFROM |
P&aste From |
Load a dib from a BMP file |
ID_EDIT_CLEAR_ALL |
Clear A&ll |
- |
Table 4. |
Figure 14: Adding separator.
Figure 15: Adding Copy To menu item.
Figure 16: Adding Paste From menu item.
Figure 17: Adding Clear All menu item.
Our completed menu items look something like the following.
Figure 18: A completed menu items.
Add WM_PALETTECHANGED and WM_QUERYNEWPALETTE windows messages to the CMainFrame class.
Figure 19: Adding Windows message handlers to CMainFrame class.
Add the following #define directive in MainFrm.h.
#define WM_VIEWPALETTECHANGED WM_USER + 5
Listing 1.
In MainFrm.cpp, modify and/or add the following codes.
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
{
// TODO: Modify the Window class or styles here by modifying
// the CREATESTRUCT cs
return CMDIFrameWnd::PreCreateWindow(cs);
}
Listing 2.
BOOL CMainFrame::OnQueryNewPalette()
{
TRACE("CMainFrame::OnQueryNewPalette\n");
CClientDC dc(this);
// don't bother if we're not using a palette
if((dc.GetDeviceCaps(RASTERCAPS) & RC_PALETTE) == 0) return TRUE;
// determine the active view
HWND hActiveView = NULL;
CFrameWnd* pActiveFrm = GetActiveFrame();
if(pActiveFrm != NULL) {
CView* pActiveView = pActiveFrm->GetActiveView();
if(pActiveView != NULL) {
hActiveView = pActiveView->GetSafeHwnd();
}
}
// iterate through all views
BOOL bBackground;
CView* pView;
CDocument* pDoc;
CDocTemplate* pTemplate;
POSITION posView;
POSITION posDoc;
POSITION posTemplate = AfxGetApp()->GetFirstDocTemplatePosition();
while(posTemplate != NULL) {
pTemplate = AfxGetApp()->GetNextDocTemplate(posTemplate);
posDoc = pTemplate->GetFirstDocPosition();
while(posDoc != NULL) {
pDoc = pTemplate->GetNextDoc(posDoc);
posView = pDoc->GetFirstViewPosition();
while(posView != NULL) {
pView = pDoc->GetNextView(posView);
bBackground = !(hActiveView == pView->GetSafeHwnd());
// background mode if view is not the active view
pView->SendMessage(WM_VIEWPALETTECHANGED, bBackground);
}
}
}
return TRUE;
}
void CMainFrame::OnPaletteChanged(CWnd* pFocusWnd)
{
TRACE("CMainFrame::OnPaletteChanged\n");
if(GetSafeHwnd() != pFocusWnd->GetSafeHwnd())
{
OnQueryNewPalette();
}
}
Listing 3.
Add in the following #define directive in mymfc30aView.h file.
#define WM_VIEWPALETTECHANGED WM_USER + 5
Listing 4.
Then, manually or using ClassView, add member variables as shown below. These variables are private by default.
// for tracking
CRectTracker m_tracker;
CRect m_rectTracker; // logical coordinates
CSize m_sizeTotal; // document size
Listing 5.
Use ClassView to add private member functions as shown here to CMymfc30aView class.
private:
BOOL DoPasteDib(COleDataObject* pDataObject);
COleDataSource* SaveDib();
Listing 6.
Figure 1: Adding member function, DoPasteDib() to CMymfc30aView class. |
Figure 20: Adding member function, SaveDib() to CMymfc30aView class.
Then, add the following codes (mymfc30aView.cpp).
//////////////////////////////////////////////////////////////
// helper functions used for clipboard and drag-drop
BOOL CMymfc30aView::DoPasteDib(COleDataObject* pDataObject)
{
// update command user interface should keep us out of here if not CF_DIB
if (!pDataObject->IsDataAvailable(CF_DIB)) {
TRACE("CF_DIB format is unavailable\n");
return FALSE;
}
CMymfc30aDoc* pDoc = GetDocument();
// Seems to be MOVEABLE memory, so we must use GlobalLock!
// (hDib != lpDib) GetGlobalData copies the memory, so we can
// hang onto it until we delete the CDib.
HGLOBAL hDib = pDataObject->GetGlobalData(CF_DIB);
ASSERT(hDib != NULL);
LPVOID lpDib = ::GlobalLock(hDib);
ASSERT(lpDib != NULL);
pDoc->m_dib.AttachMemory(lpDib, TRUE, hDib);
pDoc->SetModifiedFlag();
pDoc->UpdateAllViews(NULL);
return TRUE;
}
Listing 7.
COleDataSource* CMymfc30aView::SaveDib()
{
CDib& dib = GetDocument()->m_dib;
if (dib.GetSizeImage() > 0) {
COleDataSource* pSource = new COleDataSource();
int nHeaderSize = dib.GetSizeHeader();
int nImageSize = dib.GetSizeImage();
HGLOBAL hHeader = ::GlobalAlloc(GMEM_SHARE, nHeaderSize + nImageSize);
LPVOID pHeader = ::GlobalLock(hHeader);
ASSERT(pHeader != NULL);
LPVOID pImage = (LPBYTE) pHeader + nHeaderSize;
memcpy(pHeader, dib.m_lpBMIH, nHeaderSize);
memcpy(pImage, dib.m_lpImage, nImageSize);
// Receiver is supposed to free the global memory
::GlobalUnlock(hHeader);
pSource->CacheGlobalData(CF_DIB, hHeader);
return pSource;
}
return NULL;
}
Listing 8.
Using ClassWizard, add OnPrepareDC(), a virtual function for CMymfc30aView class.
Figure 21: Adding/overriding OnPrepareDC() virtual function.
Add window messages to the CMymfc30aView class as shown below.
OnLButtonDown()
OnSetCursor()
OnSetFocus()
Figure 22: Adding Window messages to CMymfc30aView class.
Add command and update command handlers. Take note that the update command handler for ID_EDIT_COPY, ID_EDIT_COPYTO and ID_EDIT_CUT is same.
ID |
Handler |
|
ID_EDIT_COPY |
Command. |
OnEditCopy() |
ID_EDIT_COPY |
Update command |
OnUpdateEditCopy() |
ID_EDIT_COPYTO |
Command. |
OnEditCopyto() |
ID_EDIT_COPYTO |
Update command |
OnUpdateEditCopy() |
ID_EDIT_CUT |
Command. |
OnEditCut() |
ID_EDIT_CUT |
Update command |
OnUpdateEditCopy() |
ID_EDIT_PASTE |
Command |
OnEditPaste() |
ID_EDIT_PASTE |
Update command. |
OnUpdateEditPaste() |
ID_EDIT_PASTEFROM |
Command. |
OnEditPastefrom() |
Table 5. |
Figure 23: Adding Command and Update Commands to CMymfc30aView class.
Figure 24: Adding Command and Update Commands to CMymfc30aView class.
Figure 25: A completed commands and command updates addition.
Add codes to mymfc30aView.cpp.
CMymfc30aView::CMymfc30aView() : m_sizeTotal(800, 1050), // 8 by 10.5 inches
// when printed
m_rectTracker(50, 50, 250, 250)
{ }
Listing 9.
void CMymfc30aView::OnDraw(CDC* pDC)
{
CDib& dib = GetDocument()->m_dib;
m_tracker.m_rect = m_rectTracker;
pDC->LPtoDP(m_tracker.m_rect); // tracker wants device coordinates
m_tracker.Draw(pDC);
dib.Draw(pDC, m_rectTracker.TopLeft(), m_rectTracker.Size());
}
Listing 10.
void CMymfc30aView::OnInitialUpdate()
{
SetScrollSizes(MM_TEXT, m_sizeTotal);
m_tracker.m_nStyle = CRectTracker::solidLine | CRectTracker::resizeOutside;
CScrollView::OnInitialUpdate();
}
Listing 11.
BOOL CMymfc30aView::OnPreparePrinting(CPrintInfo* pInfo)
{
pInfo->SetMaxPage(1);
return DoPreparePrinting(pInfo);
}
Listing 12.
void CMymfc30aView::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
{
// custom MM_LOENGLISH; positive y is down
if (pDC->IsPrinting())
{
int nHsize = pDC->GetDeviceCaps(HORZSIZE) * 1000 / 254;
int nVsize = pDC->GetDeviceCaps(VERTSIZE) * 1000 / 254;
pDC->SetMapMode(MM_ANISOTROPIC);
pDC->SetWindowExt(nHsize, nVsize);
pDC->SetViewportExt(pDC->GetDeviceCaps(HORZRES), pDC->GetDeviceCaps(VERTRES));
}
else {
CScrollView::OnPrepareDC(pDC, pInfo);
}
}
Listing 13.
void CMymfc30aView::OnEditCopy()
{
COleDataSource* pSource = SaveDib();
if (pSource)
{
pSource->SetClipboard(); // OLE deletes data source
}
}
Listing 14.
void CMymfc30aView::OnUpdateEditCopy(CCmdUI* pCmdUI)
{
// serves Copy, Cut, and Copy To
CDib& dib = GetDocument()->m_dib;
pCmdUI->Enable(dib.GetSizeImage() > 0L);
}
Listing 15.
void CMymfc30aView::OnEditCopyto()
{
CDib& dib = GetDocument()->m_dib;
CFileDialog dlg(FALSE, "bmp", "*.bmp");
if (dlg.DoModal() != IDOK) return;
BeginWaitCursor();
dib.CopyToMapFile(dlg.GetPathName());
EndWaitCursor();
}
Listing 16.
void CMymfc30aView::OnEditCut()
{
OnEditCopy();
GetDocument()->OnEditClearAll();
}
Listing 17.
void CMymfc30aView::OnEditPaste()
{
CMymfc30aDoc* pDoc = GetDocument();
COleDataObject dataObject;
VERIFY(dataObject.AttachClipboard());
DoPasteDib(&dataObject);
CClientDC dc(this);
pDoc->m_dib.UsePalette(&dc);
pDoc->SetModifiedFlag();
pDoc->UpdateAllViews(NULL);
}
Listing 18.
void CMymfc30aView::OnUpdateEditPaste(CCmdUI* pCmdUI)
{
COleDataObject dataObject;
BOOL bAvail = dataObject.AttachClipboard() && dataObject.IsDataAvailable(CF_DIB);
pCmdUI->Enable(bAvail);
}
Listing 19.
void CMymfc30aView::OnEditPastefrom()
{
CMymfc30aDoc* pDoc = GetDocument();
CFileDialog dlg(TRUE, "bmp", "*.bmp");
if (dlg.DoModal() != IDOK) return;
if (pDoc->m_dib.AttachMapFile(dlg.GetPathName(), TRUE))
{ // share
CClientDC dc(this);
pDoc->m_dib.SetSystemPalette(&dc);
pDoc->m_dib.UsePalette(&dc);
pDoc->SetModifiedFlag();
pDoc->UpdateAllViews(NULL);
}
}
Listing 20.
void CMymfc30aView::OnLButtonDown(UINT nFlags, CPoint point)
{
if (m_tracker.Track(this, point, FALSE, NULL)) {
CClientDC dc(this);
OnPrepareDC(&dc);
m_rectTracker = m_tracker.m_rect;
dc.DPtoLP(m_rectTracker); // Update logical coordinates
Invalidate();
}
}
Listing 21.
BOOL CMymfc30aView::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
{
if (m_tracker.SetCursor(pWnd, nHitTest))
{ return TRUE; }
else
{ return CScrollView::OnSetCursor(pWnd, nHitTest, message); }
}
Listing 22.
void CMymfc30aView::OnSetFocus(CWnd* pOldWnd)
{
CScrollView::OnSetFocus(pOldWnd);
AfxGetApp()->m_pMainWnd->SendMessage(WM_PALETTECHANGED, (UINT) GetSafeHwnd());
}
Listing 23.
Manually add the following message map function in mymfc30aView.h
afx_msg LONG OnViewPaletteChanged(UINT wParam, LONG lParam);
Listing 24.
And in mymfc30aView.cpp, add the following code.
ON_MESSAGE(WM_VIEWPALETTECHANGED, OnViewPaletteChanged)
Listing 25.
Finally add the code in mymfc30aView.cpp
LONG CMymfc30aView::OnViewPaletteChanged(UINT wParam, LONG lParam)
{
TRACE("CMymfc30aView::OnViewPaletteChanged, HWND = %x, code = %d\n", GetSafeHwnd(), wParam);
CClientDC dc(this);
GetDocument()->m_dib.UsePalette(&dc, wParam);
Invalidate();
return 0;
}
Listing 26.
Add the #include statement in mymfc30aView.cpp and mymfc30aDoc.cpp.
#include "cdib.h"
Listing 27.
Listing 28.
Using ClassWizard add the DeleteContents() virtual function to CMymfc30aDoc class.
Figure 26: Adding DeleteContents() to CMymfc30aDoc class.
Then add command and update command for ID_EDIT_CLEAR_ALL f the CMymfc30aDoc class.
Figure 27: Adding command and update command for ID_EDIT_CLEAR_ALL.
Change the protected to public for the generated message map functions in mymfc30aDoc.h (or you can use friend keyword).
// Generated message map functions
public:
//{{AFX_MSG(CMymfc30aDoc)
afx_msg void OnEditClearAll();
afx_msg void OnUpdateEditClearAll(CCmdUI* pCmdUI);
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
Listing 29.
Add member variable to CMymfc30aDoc class.
public:
CDib m_dib;
Figure 28: Adding member variable, m_dib to CMymfc30aDoc class.
Finally add the codes.
void CMymfc30aDoc::Serialize(CArchive& ar)
{
m_dib.Serialize(ar);
}
Listing 30.
void CMymfc30aDoc::OnEditClearAll()
{
DeleteContents();
UpdateAllViews(NULL);
SetModifiedFlag();
}
Listing 31.
void CMymfc30aDoc::OnUpdateEditClearAll(CCmdUI* pCmdUI)
{
pCmdUI->Enable(m_dib.GetSizeImage() > 0);
}
Listing 32.
void CMymfc30aDoc::DeleteContents()
{
m_dib.Empty();
}
Listing 33.
Finally add the following line in your StdAfx.h file for automation support.
#include <afxole.h>
Listing 34.
Then, add the following call at the start of the application's InitInstance() function.
AfxOleInit();
Listing 35.
Further reading and digging:
DCOM at MSDN.
COM+ at MSDN.
COM at MSDN.
Win32 process, thread and synchronization story can be found starting from Module R.
MSDN MFC 9.0 class library online documentation - latest version.
Unicode and Multibyte character set: Story and program examples.