wxWidgets/samples/mfc/mfctest.cpp
Vadim Zeitlin 9be928d6fb Fix destruction of wxNativeContainerWindow in wxMSW.
WM_CLOSE was not processed at all for native windows wrapped by
wxNativeContainerWindow because we don't handle it ourselves at wxWindow level
but still mark it as processed in order to prevent DefWindowProc() from
destroying the window. Unfortunately this also prevented the original handler
for this message in the native window from being called.

Calling just the original handler and not the wxWidgets one is not ideal
neither but is much better as it allows to e.g. close MFC frames wrapped in
wxNativeContainerWindow whereas before this didn't work at all as WM_CLOSE was
completely ignored.

Also call the original handler for WM_DESTROY to avoid similar potential
problems with this message, even if it doesn't seem to create any with MFC.

Extend the mfc sample to show how a wxPanel can be embedded into the existing
CFrameWnd.
2015-07-09 20:49:05 +02:00

463 lines
12 KiB
C++

/////////////////////////////////////////////////////////////////////////////
// Name: mfctest.cpp
// Purpose: Sample to demonstrate mixing MFC and wxWidgets code
// Author: Julian Smart
// Copyright: (c) Julian Smart
// Licence: wxWindows licence
/////////////////////////////////////////////////////////////////////////////
// This sample pops up an initial wxWidgets frame, with a menu item
// that allows a new MFC window to be created. Note that CDummyWindow
// is a class that allows a wxWidgets window to be seen as a CWnd
// for the purposes of specifying a valid main window to the
// MFC initialisation.
//
// You can easily modify this code so that an MFC window pops up
// initially as the main frame, and allows wxWidgets frames to be
// created subsequently.
//
// (1) Make MyApp::OnInit not create a main window.
// (2) Make MFC's InitInstance create a main window, and remove
// creation of CDummyWindow.
//
// This can be accomplished by setting START_WITH_MFC_WINDOW to 1 below.
#define START_WITH_MFC_WINDOW 0
// NOTES:
//
// * You should link with MFC DLL, not static libraries: or, to use static
// run-time libraries, use this command for both building wxWidgets and
// the sample:
//
// nmake -f makefile.vc BUILD=debug SHARED=0 DEBUG_RUNTIME_LIBS=0 RUNTIME_LIBS=static all
//
// Unless the run-time library settings match for wxWidgets and MFC, you
// will get link errors for symbols such as __mbctype, __argc, and __argv
//
// * If you see bogus memory leaks within the MSVC IDE on exit, in this
// sample or in your own project, you must be using __WXDEBUG__ +
// WXUSINGDLL + _AFXDLL
// Unfortunately this confuses the MSVC/MFC leak detector. To do away with
// these bogus memory leaks, add this to the list of link objects, make it
// first: mfc[version][u]d.lib
// - [version] -> 42 or 70 or 80 etc
// - u if using Unicode
// Disable deprecation warnings from headers included from stdafx.h for VC8+
#ifndef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#endif
// Also define WINVER to avoid warnings about it being undefined from the
// platform SDK headers (usually this is not necessary because it is done by wx
// headers but here we include the system ones before them)
#ifndef WINVER
#define WINVER 0x0600
#endif
#include "stdafx.h"
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include "wx/evtloop.h"
#include "wx/nativewin.h"
#include "wx/spinctrl.h"
#include "resource.h"
#include "mfctest.h"
/////////////////////////////////////////////////////////////////////////////
// theApp:
// Just creating this application object runs the whole application.
//
CTheApp theApp;
// wxWidgets elements
// Define a new application type
class MyApp: public wxApp
{
public:
virtual bool OnInit();
// we need to override this as the default behaviour only works when we're
// running wxWidgets main loop, not MFC one
virtual void ExitMainLoop();
wxFrame *CreateFrame();
};
class MyCanvas: public wxScrolledWindow
{
public:
MyCanvas(wxWindow *parent, const wxPoint& pos, const wxSize& size);
void OnPaint(wxPaintEvent& event);
void OnMouseEvent(wxMouseEvent& event);
wxDECLARE_EVENT_TABLE();
};
class MyChild: public wxFrame
{
public:
MyChild(wxFrame *frame, const wxString& title, const wxPoint& pos, const wxSize& size, const long style);
virtual ~MyChild();
void OnQuit(wxCommandEvent& event);
void OnNew(wxCommandEvent& event);
void OnActivate(wxActivateEvent& event);
MyCanvas *canvas;
wxDECLARE_EVENT_TABLE();
};
class MyPanel : public wxPanel
{
public:
MyPanel(wxWindow *parent, const wxPoint& pos)
: wxPanel(parent, wxID_ANY, pos)
{
wxSizer* const sizer = new wxFlexGridSizer(2, wxSize(5, 5));
sizer->Add(new wxStaticText(this, wxID_ANY, "Enter your &name:"),
wxSizerFlags().Center().Right());
m_textName = new wxTextCtrl(this, wxID_ANY);
m_textName->SetHint("First Last");
sizer->Add(m_textName, wxSizerFlags().Expand().CenterVertical());
sizer->Add(new wxStaticText(this, wxID_ANY, "And your &age:"),
wxSizerFlags().Center().Right());
m_spinAge = new wxSpinCtrl(this, wxID_ANY);
sizer->Add(m_spinAge, wxSizerFlags().Expand().CenterVertical());
wxStaticBoxSizer* const
box = new wxStaticBoxSizer(wxVERTICAL, this, "wxWidgets box");
box->Add(sizer, wxSizerFlags(1).Expand());
SetSizer(box);
// We won't be resized automatically, so set our size ourselves.
SetSize(GetBestSize());
}
private:
wxTextCtrl* m_textName;
wxSpinCtrl* m_spinAge;
};
// ID for the menu quit command
#define HELLO_QUIT 1
#define HELLO_NEW 2
wxDECLARE_APP(MyApp);
// Notice use of wxIMPLEMENT_APP_NO_MAIN() instead of the usual wxIMPLEMENT_APP!
wxIMPLEMENT_APP_NO_MAIN(MyApp);
#ifdef _UNICODE
// In Unicode build MFC normally requires to manually change the entry point to
// wWinMainCRTStartup() but to avoid having to modify the project options to do
// it we provide an adapter for it.
extern "C" int wWinMainCRTStartup();
int WINAPI WinMain(HINSTANCE, HINSTANCE, char *, int)
{
wWinMainCRTStartup();
}
#endif // _UNICODE
CMainWindow::CMainWindow()
{
LoadAccelTable( wxT("MainAccelTable") );
Create( NULL, wxT("Hello Foundation Application"),
WS_OVERLAPPEDWINDOW, rectDefault, NULL, wxT("MainMenu") );
// Create a container representing the MFC window in wxWidgets window
// hierarchy.
m_containerWX = new wxNativeContainerWindow(m_hWnd);
// Now we can create children of this container as usual.
new MyPanel(m_containerWX, wxPoint(5, 5));
// An ugly but necessary workaround to prevent the container TLW from
// resizing the panel to fit its entire client area as it would do if it
// were its only child.
new wxWindow(m_containerWX, wxID_ANY, wxPoint(4, 4), wxSize(1, 1));
}
void CMainWindow::OnPaint()
{
CString s = wxT("Hello, Windows!");
CPaintDC dc( this );
CRect rect;
GetClientRect( rect );
dc.SetTextAlign( TA_BASELINE | TA_CENTER );
dc.SetTextColor( ::GetSysColor( COLOR_WINDOWTEXT ) );
dc.SetBkMode(TRANSPARENT);
dc.TextOut( ( rect.right / 2 ), ( rect.bottom / 2 ),
s, s.GetLength() );
}
void CMainWindow::OnAbout()
{
CDialog about( wxT("AboutBox"), this );
about.DoModal();
}
void CMainWindow::OnTest()
{
wxMessageBox(wxT("This is a wxWidgets message box.\nWe're about to create a new wxWidgets frame."), wxT("wxWidgets"), wxOK);
wxGetApp().CreateFrame();
}
// CMainWindow message map:
// Associate messages with member functions.
//
// It is implied that the ON_WM_PAINT macro expects a member function
// "void OnPaint()".
//
// It is implied that members connected with the ON_COMMAND macro
// receive no arguments and are void of return type, e.g., "void OnAbout()".
//
BEGIN_MESSAGE_MAP( CMainWindow, CFrameWnd )
//{{AFX_MSG_MAP( CMainWindow )
ON_WM_PAINT()
ON_COMMAND( IDM_ABOUT, OnAbout )
ON_COMMAND( IDM_TEST, OnTest )
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BOOL CTheApp::InitInstance()
{
if ( !CWinApp::InitInstance() )
return FALSE;
// TODO: cmd line parsing
WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst);
wxSetInstance(m_hInstance);
wxApp::m_nCmdShow = m_nCmdShow;
int argc = 0;
wxChar **argv = NULL;
wxEntryStart(argc, argv);
if ( !wxTheApp || !wxTheApp->CallOnInit() )
return FALSE;
#if START_WITH_MFC_WINDOW
// Demonstrate creation of an initial MFC main window.
m_pMainWnd = new CMainWindow();
m_pMainWnd->ShowWindow( m_nCmdShow );
m_pMainWnd->UpdateWindow();
#else
// Demonstrate creation of an initial wxWidgets main window.
// Wrap wxWidgets window in a dummy MFC window and
// make the main window.
if (wxTheApp && wxTheApp->GetTopWindow())
{
m_pMainWnd = new CDummyWindow((HWND) wxTheApp->GetTopWindow()->GetHWND());
}
#endif
return TRUE;
}
int CTheApp::ExitInstance()
{
#if !START_WITH_MFC_WINDOW
delete m_pMainWnd;
#endif
if ( wxTheApp )
wxTheApp->OnExit();
wxEntryCleanup();
return CWinApp::ExitInstance();
}
// Override this to provide wxWidgets message loop compatibility
BOOL CTheApp::PreTranslateMessage(MSG *msg)
{
wxEventLoop * const
evtLoop = static_cast<wxEventLoop *>(wxEventLoop::GetActive());
if ( evtLoop && evtLoop->PreProcessMessage(msg) )
return TRUE;
return CWinApp::PreTranslateMessage(msg);
}
BOOL CTheApp::OnIdle(LONG WXUNUSED(lCount))
{
return wxTheApp && wxTheApp->ProcessIdle();
}
/*********************************************************************
* wxWidgets elements
********************************************************************/
bool MyApp::OnInit()
{
if ( !wxApp::OnInit() )
return false;
#if !START_WITH_MFC_WINDOW
// as we're not inside wxWidgets main loop, the default logic doesn't work
// in our case and we need to do this explicitly
SetExitOnFrameDelete(true);
(void) CreateFrame();
#endif
return true;
}
void MyApp::ExitMainLoop()
{
// instead of existing wxWidgets main loop, terminate the MFC one
::PostQuitMessage(0);
}
wxFrame *MyApp::CreateFrame()
{
MyChild *subframe = new MyChild(NULL, wxT("Canvas Frame"), wxPoint(10, 10), wxSize(300, 300),
wxDEFAULT_FRAME_STYLE);
subframe->SetTitle(wxT("wxWidgets canvas frame"));
// Give it a status line
subframe->CreateStatusBar();
// Make a menubar
wxMenu *file_menu = new wxMenu;
file_menu->Append(HELLO_NEW, wxT("&New MFC Window"));
file_menu->Append(HELLO_QUIT, wxT("&Close"));
wxMenuBar *menu_bar = new wxMenuBar;
menu_bar->Append(file_menu, wxT("&File"));
// Associate the menu bar with the frame
subframe->SetMenuBar(menu_bar);
int width, height;
subframe->GetClientSize(&width, &height);
MyCanvas *canvas = new MyCanvas(subframe, wxPoint(0, 0), wxSize(width, height));
canvas->SetCursor(wxCursor(wxCURSOR_PENCIL));
subframe->canvas = canvas;
subframe->Show(true);
// Return the main frame window
return subframe;
}
wxBEGIN_EVENT_TABLE(MyCanvas, wxScrolledWindow)
EVT_PAINT(MyCanvas::OnPaint)
EVT_MOUSE_EVENTS(MyCanvas::OnMouseEvent)
wxEND_EVENT_TABLE()
// Define a constructor for my canvas
MyCanvas::MyCanvas(wxWindow *parent, const wxPoint& pos, const wxSize& size)
: wxScrolledWindow(parent, -1, pos, size)
{
}
// Define the repainting behaviour
void MyCanvas::OnPaint(wxPaintEvent& WXUNUSED(event))
{
wxPaintDC dc(this);
dc.SetFont(* wxSWISS_FONT);
dc.SetPen(* wxGREEN_PEN);
dc.DrawLine(0, 0, 200, 200);
dc.DrawLine(200, 0, 0, 200);
dc.SetBrush(* wxCYAN_BRUSH);
dc.SetPen(* wxRED_PEN);
dc.DrawRectangle(100, 100, 100, 50);
dc.DrawRoundedRectangle(150, 150, 100, 50, 20);
dc.DrawEllipse(250, 250, 100, 50);
dc.DrawLine(50, 230, 200, 230);
dc.DrawText(wxT("This is a test string"), 50, 230);
}
// This implements a tiny doodling program! Drag the mouse using
// the left button.
void MyCanvas::OnMouseEvent(wxMouseEvent& event)
{
static long s_xpos = -1;
static long s_ypos = -1;
wxClientDC dc(this);
dc.SetPen(* wxBLACK_PEN);
wxPoint pos = event.GetPosition();
if (s_xpos > -1 && s_ypos > -1 && event.Dragging())
{
dc.DrawLine(s_xpos, s_ypos, pos.x, pos.y);
}
s_xpos = pos.x;
s_ypos = pos.y;
}
wxBEGIN_EVENT_TABLE(MyChild, wxFrame)
EVT_MENU(HELLO_QUIT, MyChild::OnQuit)
EVT_MENU(HELLO_NEW, MyChild::OnNew)
EVT_ACTIVATE(MyChild::OnActivate)
wxEND_EVENT_TABLE()
MyChild::MyChild(wxFrame *frame, const wxString& title, const wxPoint& pos, const wxSize& size, const long style)
: wxFrame(frame, -1, title, pos, size, style)
{
canvas = NULL;
}
MyChild::~MyChild()
{
if ( IsLastBeforeExit() )
PostQuitMessage(0);
}
void MyChild::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
void MyChild::OnNew(wxCommandEvent& WXUNUSED(event))
{
CMainWindow *mainWin = new CMainWindow();
mainWin->ShowWindow( TRUE );
mainWin->UpdateWindow();
}
void MyChild::OnActivate(wxActivateEvent& event)
{
if (event.GetActive() && canvas)
canvas->SetFocus();
}
// Dummy MFC window for specifying a valid main window to MFC, using
// a wxWidgets HWND.
CDummyWindow::CDummyWindow(HWND hWnd)
{
Attach(hWnd);
}
// Don't let the CWnd destructor delete the HWND
CDummyWindow::~CDummyWindow()
{
Detach();
}