[MSDN文章]CHtmlCtrl 来实现在对话框中中链接效果
| Code for this article: Jan00CQA.exe (248KB) Paul DiLascia is the author of Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992) and a freelance consultant and writer-at-large. He can be reached at askpd@pobox.com or http://pobox.com/~askpd. |
| Q I noticed that MFC 6.0 has a new CHtmlView that lets you view Web pages within a view. I would like to use CHtmlView in a dialog, but when I do, it crashes. Is there a corresponding CHtmlCtrl class like there is for CListView and CListCtrl? If not, how can I display HTML inside a control in my dialog? Max Burton A First of all, that's not MFC 6.0—that's the Visual Studio® 6.0 version of MFC 4.2. For an explanation of this perplexing version number scheme, see my article, "Visual C++® 6.0 Brings Home a Full Bag of Tricks and Treats for MFC Developers," in the October 1998 issue of MSJ. As for your question: no, there's no CHtmlCtrl corresponding to CHtmlView, but—lucky you—it's not hard to write one.San Diego |
if (m_pDocument!=NULL) { }
|
| So a view doesn't really need a document. Nor does CHtmlView require one. You might think the document in CHtmlView is the HTML file, but in fact CHtmlView is implemented using IWebBrowser2, which has no knowledge of the MFC doc/view architecture. |
void CView::OnDestroy()
{
CFrameWnd* pFrame = GetParentFrame();
if (pFrame != NULL && pFrame->GetActiveView() ==
this)
// deactivate during death
pFrame->SetActiveView(NULL);
CWnd::OnDestroy();
}
|
| Here the view deactivates itself when it's destroyed. As an aside—something for you to learn from—both of these frame dependencies could be avoided by sending notifications to the parent window instead of calling C++ methods. GetParentFrame could return a CWnd, not a CFrameWnd, since the important thing is that it's the top-level window—not that it's derived from any particular class. And instead of calling CFrameWnd methods, the view could send a notification like WM_IAMGOINGBYEBYENOW, which the "frame" (whether it's a CFrameWnd or a CFooWnd) would be responsible for handling appropriately. After all, it should be the frame, not the view, that decides what to do when a view is activated or destroyed. This is a general rule of thumb in any system; function calls go down (from parent to child), and events go up (from child to parent). A child class should never know what kind of container it lives in. |
int CHtmlCtrl::OnMouseActivate(...)
{
// bypass CView doc/frame stuff
return CWnd::OnMouseActivate(...);
}
void CHtmlCtrl::OnDestroy()
{
// bypass CView doc/frame stuff
CWnd::OnDestroy();
}
|
void CHtmlCtrl::PostNcDestroy()
{
// Do nothing. Don't let CView get it.
}
|
| CView's implementation of PostNcDestroy does a "delete this" to destroy the view that's normal procedure. This is normal procedure for views, which are allocated directly from the heap. But controls customarily live as data members inside some other window object |
class CMyDialog ...
{
CHtmlCtrl m_htmlCtrl;
}
|
| in which case you don't want to delete the object in PostNcDestroy because it'll be deleted with the parent object. |
// in AboutHtml.rc ABOUT.HTM HTML DISCARDABLE "res\\about.htm" PD.JPG HTML DISCARDABLE "res\\pd.jpg" OKUP.GIF HTML DISCARDABLE "res\\okup.gif" OKDN.GIF HTML DISCARDABLE "res\\okdn.gif" MOZART.WAV HTML DISCARDABLE "res\\mozart.wav" |
<IMG src="pd.jpg"> |
| then pd.jpg is assumed to live in the current directory—that is, wherever the file containing the IMG element resides. In the case of a file stored in your EXE as a resource, the same is true. In this case, however, you have to give the browser a little help by adding the following code at the top of your HTML file: |
<BASE url="res://AboutHtml.exe/about.htm"> |
| This tells the browser that the current "directory" is res://AboutHtml.exe, so when it encounters <IMG src="pd.jpg">, it will look for res://AboutHtml.exe/pd.jpg. Otherwise, it would look in the directory where your program file resides. |
![]() |
BOOL CAboutDialog::OnInitDialog()
{
VERIFY(CDialog::OnInitDialog());
VERIFY(m_page.CreateFromStatic(IDC_HTMLVIEW, this));
m_page.LoadFromResource(_T("about.htm"));
return TRUE;
}
|
| CHtmlCtrl::CreateFromStatic is a handy function I wrote to simplify designing dialogs in the resource editor. It's way, way, WAY too much trouble to make an insertable COM object for something like this, so instead I created a blank static control with the ID I wanted in the position I wanted. Then I called CreateFromStatic, which created a CHtmlCtrl with the exact same ID, size, and position as the static, then destroyed the static. It's very effective. To launch the page, I called CHtmlCtrl::LoadFromResource, inherited from CHtmlView. Alternatively, I could have navigated to res://AboutHtml.exe/about.htm. |
<A href="ok"><IMG ...></A> |
| Now when the user clicks, the browser goes to the ok file. But before it does, control passes through CHtmlCtrl::OnBeforeNavigate2. CHtmlCtrl can do anything it wants. |
void CMyHtmlCtrl::OnBeforeNavigate2(
LPCTSTR lpszURL,...,BOOL* pbCancel)
{
if (_tcscmp(lpszURL,_T("ok"))==0) {
// "ok" clicked:
*pbCancel=TRUE; // abort
// will close dialog
GetParent()->
SendMessage(WM_COMMAND,IDOK);
}
}
|
void CMyHtmlCtrl::OnAppCmd(
LPCTSTR lpszWhere)
{
if (_tcsicmp(lpszWhere,
_T("ok"))==0) {
GetParent()->SendMessage
(WM_COMMAND,IDOK);
}
}
|
Q I need to work with a Microsoft Access 2000 database and Data Access Objects (DAO) 3.6 from C++ with MFC. When I try to use CDaoRecordset, I get an "Unrecognized database format" error message. MFC probably uses DAO 3.5. How can I open files in MFC that were created with Microsoft Access 2000? Ing. Jozef Sakalos A This question is also discussed in the Knowledge Base article Q236991 (http://support.microsoft.com/support/kb/articles/ Q236/9/91.asp), but since so many people have asked, I'll cover it here. The problem is that MFC is using the wrong DAO DLL. When you call AfxDaoInit to initialize DAO for MFC, MFC first determines which version of the DAO engine to load. Slovakia |
// in daocore.cpp
void AFXAPI AfxDaoInit()
{
•
•
•
BYTE bUseDao = _AfxDetermineDaoVersion();
switch (bUseDao) {
case 35:
// Use DAO350.DLL
break;
case 30:
// Use DAO300.DLL
break;
case 36:
// Use DAO360.DLL
break;
}
}
|
| _AfxDetermineDaoVersion is a macro that expands to 30, 35, or 36, depending on which version of MFC you have. |
#if _MFC_VER >= 0x0601 #define _AfxDetermineDaoVersion() (36) #else #define _AfxDetermineDaoVersion() (35) #endif |
static inline BYTE _AfxDetermineDaoVersion()
{
BYTE bReturn = 35;
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
if (pModuleState->m_dwVersion < 0x421)
bReturn = 30;
else if (pModuleState->m_dwVersion >= 0x0601)
bReturn = 36;
return bReturn;
}
|
// in afxver_.h #define _MFC_VER 0x0600 |
AfxGetModuleState()->m_dwVersion = 0x0601; AfxDaoInit(); |
| That is, set the version number in your module state to 6.1 before you call AfxDaoInit. If you're the paranoid sort—and who isn't paranoid from time to time—you can save the original version and restore it after doing your business. A simple grep reveals, however, that _AfxDetermineDaoVersion is the only function in all of MFC that uses the mysterious 0x0601 version, so changing the version to 0x0601 should have no side effects. |
// Add these lines to the top of daocore.cpp #include "daoimpl.h" #define _countof(array) \ (sizeof(array)/sizeof(array[0])) #undef _MFC_VER #define _MFC_VER 0x0601 |
| Have a question about programming in C or C++? Send it to Paul DiLascia at askpd@pobox.com |
| From the January 2000 issue of Microsoft Systems Journal. Get it at your local newsstand, or better yet, subscribe. |

浙公网安备 33010602011771号