Create a Titleless/Borderless draggable wxFrame in wxWidgets - c++

Hi i am trying to create a wxApp that will have not have a titlebar(including the minimize, maximize and close) icons that are by default provided. The code that i have is as follows:
main.h
class MyApp : public wxApp
{
public:
virtual bool OnInit();
};
main.cpp
bool MyApp::OnInit()
{
MyFrame *prop = new MyFrame(wxT("MyFrame"));
prop->Show(true);
return true;
}
myframe.h
class MyFrame : public wxFrame
{
public:
MyFrame(const wxString& title);
void OnClick(wxMouseEvent& event);
};
myframe.cpp
MyFrame::MyFrame(const wxString& title)
: wxFrame(NULL, wxID_ANY, title, wxDefaultPosition, wxSize(250, 130))
{
MyPanel *panel = new MyPanel(this, wxID_ANY);
panel->SetBackgroundColour(wxColour(255,255,255));
MyButton *button = new MyButton(panel, wxID_ANY, wxT("Ok"));
Connect( wxEVT_LEFT_UP,
wxMouseEventHandler(MyFrame::OnClick));
panel->Centre();
}
void MyFrame::OnClick(wxMouseEvent& event)
{
std::cout << "event reached frame class" << std::endl;
}
mypanel.h
class MyPanel : public wxPanel
{
public:
MyPanel(wxFrame *frame, int id);
void OnClick(wxMouseEvent& event);
};
mypanel.cpp
MyPanel::MyPanel(wxFrame *frame, int id)
: wxPanel(frame, id)
{
Connect( wxEVT_LEFT_UP,
wxMouseEventHandler(MyPanel::OnClick));
}
void MyPanel::OnClick(wxMouseEvent& event)
{
std::cout << "event reached panel class" << std::endl;
}
mybutton.h
class MyButton : public wxPanel
{
public:
MyButton(wxPanel *panel, int id, const wxString &label);
void OnClick(wxMouseEvent& event);
};
mybutton.cpp
void MyButton::onClick(wxMouseEvent &event)
{
}
What i want is:
There should be no title bar(including the 3 maximize, minimize & close buttons) at the top.
Now since there is no titlebar at the top of the frame, there is no way to drag or close or maximize or minimize the window. For that i want to create a custom titlebar at the top which should have the three customized maximized,minimized and close button and also i should be able to drag the frame by double clicking and holding and dragging the topmost part of the newly created frame.
Is this possible in wxWidgets? If yes, how can i achieve this?
I am not proposing any new way of dragging. The new frame/window that we will have should also be dragged only by its own customized title bar. It's exactly like dragging the old frame by double clicking and dragging the frame in the native case. I just want to customize the native title bar. Like increase its height, change it's colour, change how to three buttons(minimize, maximize and close) look.

Here's the simplest example I can think of how to create a frame with a pseudo titlebar that can be clicked to drag the frame around. This example shows which mouse events need to be handled to drag the window around and how to do the calculations needed in those event handlers.
Note that moving the frame needs to be done in screen coordinates, but the coordinates received in the event handlers will be in client coordinates for the title bar. This example also shows how to do those coordinate conversions.
#include "wx/wx.h"
class CustomTitleBar:public wxWindow
{
public:
CustomTitleBar(wxWindow* p) : wxWindow(p,wxID_ANY)
{
m_dragging = false;
SetBackgroundColour(*wxGREEN);
Bind(wxEVT_LEFT_DOWN,&CustomTitleBar::OnMouseLeftDown,this);
Bind(wxEVT_MOUSE_CAPTURE_LOST, &CustomTitleBar::OnMouseCaptureLost,
this);
}
wxSize DoGetBestClientSize() const override
{
return wxSize(-1,20);
}
private:
void OnMouseLeftDown(wxMouseEvent& event)
{
if ( !m_dragging )
{
Bind(wxEVT_LEFT_UP,&CustomTitleBar::OnMouseLeftUp,this);
Bind(wxEVT_MOTION,&CustomTitleBar::OnMouseMotion,this);
m_dragging = true;
wxPoint clientStart = event.GetPosition();
m_dragStartMouse = ClientToScreen(clientStart);
m_dragStartWindow = GetParent()->GetPosition();
CaptureMouse();
}
}
void OnMouseLeftUp(wxMouseEvent&)
{
FinishDrag();
}
void OnMouseMotion(wxMouseEvent& event)
{
wxPoint curClientPsn = event.GetPosition();
wxPoint curScreenPsn = ClientToScreen(curClientPsn);
wxPoint movementVector = curScreenPsn - m_dragStartMouse;
GetParent()->SetPosition(m_dragStartWindow + movementVector);
}
void OnMouseCaptureLost(wxMouseCaptureLostEvent&)
{
FinishDrag();
}
void FinishDrag()
{
if ( m_dragging )
{
Unbind(wxEVT_LEFT_UP,&CustomTitleBar::OnMouseLeftUp,this);
Unbind(wxEVT_MOTION,&CustomTitleBar::OnMouseMotion,this);
m_dragging = false;
}
if ( HasCapture() )
{
ReleaseMouse();
}
}
wxPoint m_dragStartMouse;
wxPoint m_dragStartWindow;
bool m_dragging;
};
class Customframe : public wxFrame
{
public:
Customframe(wxWindow* p)
:wxFrame(p, wxID_ANY, wxString(), wxDefaultPosition, wxSize(150,100),
wxBORDER_NONE)
{
CustomTitleBar* t = new CustomTitleBar(this);
SetBackgroundColour(*wxBLUE);
wxBoxSizer* szr = new wxBoxSizer(wxVERTICAL);
szr->Add(t,wxSizerFlags(0).Expand());
SetSizer(szr);
Layout();
}
};
class MyFrame: public wxFrame
{
public:
MyFrame():wxFrame(NULL, wxID_ANY, "Custom frame Demo", wxDefaultPosition,
wxSize(400, 300))
{
wxPanel* bg = new wxPanel(this, wxID_ANY);
wxButton* btn = new wxButton(bg, wxID_ANY, "Custom frame");
wxBoxSizer* szr = new wxBoxSizer(wxVERTICAL);
szr->Add(btn,wxSizerFlags(0).Border(wxALL));
bg->SetSizer(szr);
Layout();
btn->Bind(wxEVT_BUTTON, &MyFrame::OnButton, this);
m_customFrame = NULL;
}
private:
void OnButton(wxCommandEvent&)
{
if ( m_customFrame )
{
m_customFrame->Close();
m_customFrame = NULL;
}
else
{
m_customFrame = new Customframe(this);
m_customFrame->CenterOnParent();
m_customFrame->Show();
}
}
wxFrame* m_customFrame;
};
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
On windows, it looks like this.
You should be able to add whatever buttons you want to the custom title bar exactly as you would add buttons to any other window.

"Is this possible in wxWidgets?"
Yes. You need to use a wxWindow instead of a wxFrame and set some style for it. like wxBORDER_NONE. But you will have to implement many things that wxFrame already provides.
And your proposed way of dragging seems wrong/confusing to me. 99.99% of users prefer a UI they are used to it, avoiding to learn a new way of doing simple things they already know.
If you just want to avoid resizing then you have two ways:
a) Catch the size-event and do nothing. Calling event.Skip(false)
(to prevent handling in parent) is not neccessary.
b) Once you have created your window and correctly sized, get its size and set it as max and min.
In both cases the user will see a "resize" mouse pointer when hovering the mouse at any border, but nothing else, no resizing, will be done.

#JasonLiam,
Don't forget to place the application icon in the top left corner of you title bar and handle the right/left mouse clicks appropriately (as in native application) (if you want to go this route and get rid of the native frame window).
Thank you.

Related

wxWidgets Panel not Receiving Mouse Events

I am having trouble capturing and handling mouse events within a panel.
Binding mouse events to my main window frame works as expected. However, when I bind events to a child panel, they successfully don't go my frame, but are not correctly handled by my panel. Any help would be appreciated.
I am using wxWidgets v3.1.5
Below is my simplest example: a single panel inside a parent frame.
Clicking the panel should turn itself yellow. Clicking the surrounding frame area should turn the panel green.
// wxWidgets in full of strcpy
#pragma warning(disable : 4996)
#include <wx/wx.h>
class cPanel : public wxPanel {
public:
cPanel(wxWindow* parent, wxSize size)
: wxPanel(parent, wxID_ANY, wxDefaultPosition, size) {
this->Bind(wxEVT_LEFT_DOWN, &cPanel::OnLeftClick, this);
}
void OnLeftClick(wxMouseEvent& event) {
SetBackgroundColour(wxColour("yellow"));
Refresh();
};
};
class cFrame : public wxFrame {
public:
wxPanel* child;
cFrame()
: wxFrame(nullptr, wxID_ANY, "Example Title", wxPoint(200, 200),
wxSize(800, 500)) {
child = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxSize(300, 300));
auto top = new wxBoxSizer(wxHORIZONTAL);
top->Add(child, 0, wxALL, 20);
SetSizer(top);
this->Bind(wxEVT_LEFT_DOWN, &cFrame::OnLeftClick, this);
};
void OnLeftClick(wxMouseEvent& event) {
child->SetBackgroundColour(wxColour("green"));
child->Refresh();
}
};
class cApp : public wxApp {
public:
cFrame* frame = nullptr;
cApp(){};
~cApp(){};
virtual bool OnInit() {
frame = new cFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(cApp);
Only wxCommandEvents (and classes derived from it) will filter up to parent windows. wxMouseEvent does not derive from wxCommandEvent, so a mouse event on the panel that is not handled by the panel will not filter up to the frame.
Consequently, you'll need to Bind the mouse event handler to the panel instead of the frame. Here's an example of how to change the last few lines of your cFrame class to do that.
this->Bind(wxEVT_LEFT_DOWN, &cFrame::OnLeftClick, this);
child->Bind(wxEVT_LEFT_DOWN, &cFrame::OnChildLeftClick, this);
};
void OnLeftClick(wxMouseEvent& event) {
child->SetBackgroundColour(wxColour("green"));
child->Refresh();
}
void OnChildLeftClick(wxMouseEvent& event) {
child->SetBackgroundColour(wxColour("yellow"));
child->Refresh();
}
};
There are many ways of accomplishing the same thing, but I think something like this is probably the simplest.

wxPopupTransientWindow not showing content correctly

I'm creating a C++ wxWidgets calculator application. I'm implementing trigonometric functions, and to save on space I've reunited all of them in a single button. If you right click on the button then, a popup is created, which contains buttons for all the functions. I'm using a derived wxPopupTransientWindow class for this job, the problem is, the buttons aren't displaying correctly.
This is my code:
expandMenu.h
#include "wx/wx.h"
#include "wx/popupwin.h"
struct expandMenuInfo
{
const wxString& label;
wxWindowID id;
expandMenuInfo(const wxString& l, wxWindowID i)
: label{l}
, id{i}
{}
};
class expandMenu : public wxPopupTransientWindow
{
wxWindow* panel;
wxBoxSizer* sizer;
public:
expandMenu(wxWindow* parent, wxPoint pos, std::vector<expandMenuInfo> buttons);
~expandMenu();
};
expandMenu.cpp
#include "expandMenu.h"
// PANNELLO ESTENSIONE
expandMenu::expandMenu(wxWindow* parent, wxPoint pos, std::vector<expandMenuInfo> buttons)
: wxPopupTransientWindow(parent, wxBORDER_NONE | wxPU_CONTAINS_CONTROLS)
{
this->SetPosition(pos);
this->SetSize(50 * buttons.size(), 50);
this->SetBackgroundColour(wxColour(90, 93, 121));
panel = new wxWindow(this, wxID_ANY);
sizer = new wxBoxSizer(wxHORIZONTAL);
// costruisci struttura
for (unsigned int i = 0; i < buttons.size(); i++)
{
wxButton* btn = new wxButton(this, buttons.at(i).id, buttons.at(i).label);
sizer->Add(btn, 1, wxEXPAND);
}
panel->SetSizer(sizer);
}
expandMenu::~expandMenu()
{
}
And this is the code I use for my custom wxButton's to actually create the popup (it's temporary):
void ikeButton::rightClick(wxMouseEvent& evt) // CREA PANNELLO ESTENSIONE
{
if (flags & EXPANDABLE)
{
std::vector<expandMenuInfo> buttons;
buttons.push_back(expandMenuInfo(L"sin", 3001));
buttons.push_back(expandMenuInfo(L"cos", 3002));
buttons.push_back(expandMenuInfo(L"tan", 3003));
expandMenu* menu = new expandMenu(this, wxGetMousePosition(), buttons);
menu->Popup();
}
}
Thank you in advance for any help, as I'm pretty new to this framework.
To get the buttons to layout in the popup window, in the constuctor for expandMenu I think you just need to change panel->SetSizer(sizer); to
SetSizerAndFit(sizer);
Layout();
From a UI perspective, I think a split button might be a better way to implement the functionality you are describing. wxWidgets doesn't currently have such a control, but this post shows 2 different ways you can create one.
I believe the issue is that you're specifying this for the parent of the buttons, i.e. the popup window. I think you wanted their parent to be the panel instead.

wxWidget mouse processing

I use wxWidget-3.1.4 under windows 10
I try to process mouse events from my class.
Header class:
#include "wx/wxprec.h"
#include <wx/wfstream.h>
class MainWindow : public wxFrame
{
public:
MainWindow(const wxString& title);
void OnQuit(wxCommandEvent& event);
void OnOpenImage(wxCommandEvent& WXUNUSED);
void OnEditImage(wxCommandEvent& WXUNUSED);
//will redirect mouse event to class ImageDrawing
void OnMouseWheel(wxMouseEvent& event);
void OnMouseMove(wxMouseEvent& event);
void OnMouseDown(wxMouseEvent& event);
void OnMouseUp(wxMouseEvent& event);
private:
//wx Panel for drawing image
wxPanel* m_background;
wxImage* m_Image;
...
};
Cpp file:
MainWindow::MainWindow(const wxString& title): wxFrame(NULL, wxID_ANY, title)
{
m_background = new wxPanel(this, wxID_ANY);
m_background->Bind(wxEVT_PAINT, &MainWindow::OnPaint, this);
}
void MainWindow::OnQuit(wxCommandEvent& WXUNUSED(event))
{
Close(true);
}
void MainWindow::OnOpenImage(wxCommandEvent& WXUNUSED(event))
{
wxString file = OpenFileDialog("Open Image file", "png files (*.png)|*.png");
openImage(file, true);
}
void MainWindow::OnEditImage(wxCommandEvent& WXUNUSED(event))
{
wxString file = OpenFileDialog("Open Image file", "png files (*.png)|*.png");
// some logic
}
void MainWindow::OnMouseWheel(wxMouseEvent& event)
{
std::cout<<"OnMouseWheel(event)";
}
void MainWindow::OnMouseMove(wxMouseEvent& event)
{
std::cout<<"OnMouseMove(event)";
}
void MainWindow::OnMouseDown(wxMouseEvent& event)
{
std::cout<<"OnMouseDown(event)";
}
void MainWindow::OnMouseUp(wxMouseEvent& event)
{
std::cout<<"OnMouseUp(event)";
}
wxBEGIN_EVENT_TABLE(MainWindow, wxFrame)
EVT_MENU(Minimal_Quit, MainWindow::OnQuit)
EVT_MENU(Open_Image, MainWindow::OnOpenImage)
EVT_MENU(Edit_Image, MainWindow::OnEditImage)
EVT_MOTION(MainWindow::OnMouseMove)
EVT_MOUSEWHEEL(MainWindow::OnMouseWheel)
EVT_LEFT_DOWN(MainWindow::OnMouseDown)
EVT_LEFT_UP(MainWindow::OnMouseUp)
EVT_RIGHT_DOWN(MainWindow::OnMouseDown)
EVT_RIGHT_UP(MainWindow::OnMouseUp)
wxEND_EVENT_TABLE()
the problem is that event menu works fine, also OnMouseWheel works, but all other mouse events are not working. Googling problem shows that a reason for this is because a parent component, in my case wxFrame is included some child, like wxPanel, and this child component could intercept events.
So why OnMouseWheel is working and all other is not. the same behaviour if I change wxFrame to wxPanel in event table and if I didn't use m_background.
I was able to solve this problem by applying the following changes in constructor:
MainWindow::MainWindow(const wxString& title): wxFrame(NULL, wxID_ANY, title)
{
m_background = new wxPanel(this, wxID_ANY);
m_background->Bind(wxEVT_PAINT, &MainWindow::OnPaint, this);
m_background->Connect(wxEVT_MOTION, wxMouseEventHandler(MainWindow::OnMouseMove));
m_background->Connect(wxEVT_LEFT_DOWN, wxMouseEventHandler(MainWindow::OnMouseDown));
m_background->Connect(wxEVT_LEFT_UP, wxMouseEventHandler(MainWindow::OnMouseUp));
}
in each function that if you want to access to some class member or parameter you need to do the following:
void MainWindow::OnMouseMove(wxMouseEvent& event)
{
MainWindow* pointer = dynamic_cast<MainWindow*>(this->GetParent());
if(pointer->m_path.size() > 0)
pointer->m_drawing->OnMove(event);
}
and instead of this, you need to use a pointer, the reason for this because this now point to m_background.
Last step is to delete this function from wx_EVENT_TABLE

Wxwidgets - How can I add resize event handler to make the "X" drawing to resize with the window?

I am trying to learn WxWidgets with C++ (I am very new at this), and I have created a window with a black background color and has a big red "X" on it. I have to edit the code so that the "X" changes its size with the window as I am resizing the window. How can I properly implement the resize event handler to this code?
Here's a screenshot of what my code produces: https://imgur.com/a/0I8EG5y
Here's what I have so far"
#include <wx/wx.h>
#include <wx/dcbuffer.h>
class MyCanvas : public wxWindow
{
public:
MyCanvas(wxWindow* parent)
: wxWindow(parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
{
SetBackgroundStyle(wxBG_STYLE_PAINT);
Bind(wxEVT_PAINT, &MyCanvas::OnPaint, this);
}
private:
void OnPaint(wxPaintEvent&)
{
wxAutoBufferedPaintDC dc(this);
dc.SetPen(*wxRED_PEN);
dc.SetBrush(*wxBLACK_BRUSH);
dc.DrawLine(0,0,485,485);
dc.DrawLine(0, 485, 485, 0);
}
};
class MyFrame : public wxFrame
{
public:
MyFrame()
: wxFrame(NULL, wxID_ANY, _("Resizable X"), wxDefaultPosition, wxSize(500, 525))
{
wxBoxSizer* bSizer = new wxBoxSizer(wxVERTICAL);
bSizer->Add(new MyCanvas(this), 1, wxEXPAND);
SetSizer(bSizer);
}
};
/**** MyApp ****/
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show();
return true;
}
};
IMPLEMENT_APP(MyApp)
The simplest way to implement your resize handler is to do the following in MyCanvas ctor:
Bind(wxEVT_SIZE, [this](wxSizeEvent& event) { Refresh(); event.Skip(); });
This will fully refresh your canvas every time it is resized, i.e. will generate a wxEVT_PAINT that will result in a call to your existing OnPaint() handler.
Of course, for this to be actually useful, your OnPaint() should take the current window size into account, i.e. use GetClientSize() instead of hardcoded 485.

Image corruption in wxScrolledWindow while scrolling

I want to view an image in one of wxSplitterWindow using wxScrolledWindow.
I derived class from wxPanel, which I add into wxScrolledWindow. But the result is not what i want.(
class ImagePanel : public wxPanel
{
public:
//ImagePanel(wxFrame* parent);
ImagePanel(wxWindow* parent);
void paintEvent(wxPaintEvent& event);
void paintNow();
void setBitmap(wxBitmap& imBitmap);
void setBitmap(wxImage& img);
int GetHeight();
int GetWidth();
void render(wxDC& dc);
DECLARE_EVENT_TABLE()
private:
wxBitmap curBitmap;
};
Paint event:
void ImagePanel::paintEvent(wxPaintEvent& event)
{
wxBufferedPaintDC dc(this);
render(dc);
}
This is how I have written the interface into main frame constructor:
topSizer = new wxBoxSizer(wxVERTICAL);
topSplitter = new wxSplitterWindow(this,ID_TOPSPLITTER,wxDefaultPosition,wxDefaultSize,wxSP_LIVE_UPDATE|wxSP_3D);
topSizer->Add(topSplitter,1,wxALL|wxEXPAND,0);
imagePanel = new wxPanel(topSplitter,ID_IMAGEPANEL);
imageSizer = new wxBoxSizer(wxVERTICAL);
imagePanel->SetSizer(imageSizer);
optionPanel = new wxPanel(topSplitter,ID_OPTIONPANEL);
optionSizer = new wxBoxSizer(wxVERTICAL);
optionPanel->SetSizer(optionSizer);
topSplitter->SplitVertically(imagePanel, optionPanel);
topSplitter->SetMinimumPaneSize(200);
//===========================================
//image panel interface
imageScWindow=new wxScrolledWindow(imagePanel,ID_IMAGESCWINDOW,wxDefaultPosition,wxDefaultSize,
wxVSCROLL|wxHSCROLL|wxALL|wxALWAYS_SHOW_SB|wxFULL_REPAINT_ON_RESIZE);
imgArea = new ImagePanel(imageScWindow);
imageSizer->Add(imageScWindow,1,wxEXPAND,0);
scrollSizer = new wxBoxSizer(wxVERTICAL);
imageScWindow->SetSizer(scrollSizer);
scrollSizer->Add(imgArea,1,wxEXPAND,0);
imageScWindow->SetTargetWindow(imgArea);
And this is the function that loads image:
void MainFrame::Setpic(wxCommandEvent& event)
{
wxString path = GetCurrentWorkingDir()+wxT("\\")+tmptxt1->GetValue();
wxImage img(path);
wxBitmap imBitmap(img,-1);
imgArea->setBitmap(imBitmap);
imageScWindow->SetScrollbars(1,1,imgArea->GetWidth(),imgArea->GetHeight());
imageScWindow->Refresh();
}
Thats what i get:
https://i.imgur.com/x1GIlrl.png
I have also tried to do like this in the constructor, without SetTargetArea:
//===========================================
//image panel interface
imageScWindow=new wxScrolledWindow(imagePanel,ID_IMAGESCWINDOW,wxDefaultPosition,wxDefaultSize,
wxVSCROLL|wxHSCROLL|wxALL|wxALWAYS_SHOW_SB|wxFULL_REPAINT_ON_RESIZE);
imgArea = new ImagePanel(imageScWindow);
imageSizer->Add(imageScWindow,1,wxEXPAND,0);
scrollSizer = new wxBoxSizer(wxVERTICAL);
imageScWindow->SetSizer(scrollSizer);
scrollSizer->Add(imgArea,1,wxEXPAND,0);
Then image shows properly, but if I change the frame size or move splitter, scrollbars disappear, and I have to set them again somwhere.
Then i tried to do like this:
int prvx,prvy;
void ImagePanel::paintEvent(wxPaintEvent& event)
{
reinterpret_cast<wxScrolledWindow*>(this->GetParent())->GetViewStart(&prvx,&prvy);
reinterpret_cast<wxScrolledWindow*>(this->GetParent())->SetScrollbars(1,1,curBitmap.GetWidth(),curBitmap.GetHeight());
reinterpret_cast<wxScrolledWindow*>(this->GetParent())->Scroll(wxPoint(prvx,prvy));
wxBufferedPaintDC dc(this);
render(dc);
}
It shows properly, but lags while scrolling, even without wxFULL_REPAINT_ON_RESIZE in wxScrolledWindow. The reason why it is in paint event: if I add this into OnSize event, program crashes. What to do?
I'm not sure where to begin answering your question. Here's a minimal sample of a frame that has a splitter with a bitmap on the left and a panel on the right. I hope you can adapt this sample to do what you need.
// For compilers that support precompilation, includes "wx/wx.h".
#include "wx/wxprec.h"
#ifdef __BORLANDC__
#pragma hdrstop
#endif
// for all others, include the necessary headers (this file is usually all you
// need because it includes almost all "standard" wxWidgets headers)
#ifndef WX_PRECOMP
#include "wx/wx.h"
#endif
#include <wx/scrolwin.h>
#include <wx/splitter.h>
#include <wx/dcbuffer.h>
class MyFrame : public wxFrame
{
public:
MyFrame( wxWindow* parent, int id = wxID_ANY, wxString title = "Demo",
wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize,
int style = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL );
private:
void OnScrollPaint( wxPaintEvent& event );
wxScrolledCanvas* m_canvas;
wxBitmap m_bitmap;
};
MyFrame::MyFrame( wxWindow* parent, int id, wxString title, wxPoint pos,
wxSize size, int style )
:wxFrame( parent, id, title, pos, size, style )
{
m_bitmap=wxBitmap ("test.png", wxBITMAP_TYPE_PNG );
wxSplitterWindow* m_splitter1
= new wxSplitterWindow( this, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxSP_LIVE_UPDATE );
m_canvas = new wxScrolledCanvas( m_splitter1, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxSTATIC_BORDER|wxHSCROLL|wxVSCROLL );
m_canvas->SetScrollRate( 5, 5 );
m_canvas->SetVirtualSize(m_bitmap.GetWidth(), m_bitmap.GetHeight());
m_canvas->SetBackgroundStyle(wxBG_STYLE_PAINT);
m_canvas->Bind( wxEVT_PAINT, &MyFrame::OnScrollPaint , this );
wxPanel* m_panel2 = new wxPanel( m_splitter1, wxID_ANY, wxDefaultPosition,
wxDefaultSize, wxSTATIC_BORDER|wxTAB_TRAVERSAL );
m_splitter1->SplitVertically( m_canvas, m_panel2, GetSize().x/2 );
}
void MyFrame::OnScrollPaint( wxPaintEvent& event )
{
wxAutoBufferedPaintDC dc(m_canvas);
m_canvas->DoPrepareDC(dc);
dc.Clear();
dc.DrawBitmap(m_bitmap,0,0);
}
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
wxInitAllImageHandlers();
MyFrame* frame = new MyFrame(NULL);
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
To run this, be sure to change "test.png" in line 35 to the name of an actual image file on your computer (and change wxBITMAP_TYPE_PNG if the image file isn't a png).
The important parts are:
Set the background style of the canvas to wxBG_STYLE_PAINT as shown in line 46.
In the paint handler, call the DoPrepareDC method on the paintdc.
By the way, the paint handler shown in my sample isn't very good. It draws the entire bitmap every time. A better approach would be to get the update region and only redraw the parts that are needed, but I wanted to keep this simple.