C++ wxWidgets绘制自定义控件

C++ wxWidgets绘制自定义控件,c++,wxwidgets,C++,Wxwidgets,我想写一个应用程序,它必须在任何平台上具有相同的外观。我决定使用wxWidgets (I never have a large amount of work with this library). 从滚动条开始我的定制。这是一个糟糕的决定 (inheritance from wxScrollBar = because all control badly flickering) 因为在操作系统Windows下,默认滚动条的定制能力非常差。我的下一步是做与中相同的工作,但使用wxWidgets

我想写一个应用程序,它必须在任何平台上具有相同的外观。我决定使用wxWidgets

 (I never have a large amount of work with this library).
从滚动条开始我的定制。这是一个糟糕的决定

(inheritance from wxScrollBar = because all control badly flickering)
因为在操作系统Windows下,默认滚动条的定制能力非常差。我的下一步是做与中相同的工作,但使用wxWidgets代替MFC

最好在一个窗口上绘制所有控件,或者用自己的窗口替换每个4部分:滚动背景、拇指滑块、2个滚动箭头?我需要建议。 我首先选择:

#include "wx/wx.h"
#include "wx/sizer.h"

#define ENABLE_ERASING 0
#define DISABLE_BORDER 1
#define UPDATE_ON_RESIZE 0

#if DISABLE_BORDER
long gBorder = wxBORDER_NONE;
#else
long gBorder = 0;
#endif

class ScrollBar : public wxControl
{
public:
    ScrollBar(wxWindow *parent, wxWindowID id);
protected:
    void on_paint(wxPaintEvent& ev);
#if ENABLE_ERASING
    void on_erace(wxEraseEvent& ev);
#endif
    void draw(wxDC& dc);
    wxSize DoGetBestSize() const override;
#if UPDATE_ON_RESIZE
    void on_size(wxSizeEvent& ev);
#endif
    wxDECLARE_EVENT_TABLE();
private:
};

wxBEGIN_EVENT_TABLE(ScrollBar, wxControl)
EVT_PAINT(ScrollBar::on_paint)
#if ENABLE_ERASING
EVT_ERASE_BACKGROUND(ScrollBar::on_erace)
#endif
#if UPDATE_ON_RESIZE
EVT_SIZE(ScrollBar::on_size)
#endif
wxEND_EVENT_TABLE()

ScrollBar::ScrollBar(wxWindow* parent, wxWindowID id)
    : wxControl(parent, id, wxDefaultPosition, wxDefaultSize, gBorder)
{}

void ScrollBar::on_paint(wxPaintEvent& ev)
{
    wxPaintDC dc(this);
    draw(dc);
    wxSize size = GetClientSize();
    wxString text = wxString::Format("%i; %i paint", size.GetWidth(),     
size.GetHeight());
    dc.DrawText(text, wxPoint(74, 8));
}

#if ENABLE_ERASING
void ScrollBar::on_erace(wxEraseEvent& ev)
{
    wxClientDC* clientDC = nullptr;
    if (!ev.GetDC())
        clientDC = new wxClientDC(this);
    wxDC* dc = clientDC ? clientDC : ev.GetDC() ;
    draw(*dc);
    wxSize size = GetClientSize();
    wxString text = wxString::Format("%i; %i erase", size.GetWidth(), 
size.GetHeight());
    dc->DrawText(text, wxPoint(74, 8));
    if (clientDC)
        delete clientDC;
}
#endif

void ScrollBar::draw(wxDC& dc)
{
    wxSize size = dc.GetSize();
    // draw scroll arrows
    dc.SetBrush(*wxBLUE_BRUSH);
    dc.SetPen( wxPen( wxColor(0,175,175), 2 ) );
    dc.DrawRectangle(0, 0, 20, size.GetHeight());
    dc.DrawRectangle(size.GetWidth() - 21, 0, 20, size.GetHeight());

    // draw background
    dc.SetBrush(*wxGREY_BRUSH);
    dc.SetPen( wxPen( wxColor(175,175, 0), 2 ) );
    dc.DrawRectangle(21, 0, size.GetWidth() - 43, size.GetHeight());

    // draw thumb slider
    dc.SetBrush(*wxCYAN_BRUSH);
    dc.SetPen( wxPen( wxColor(175, 0, 175), 2 ) );
    dc.DrawRectangle(70, 7, 150, 20);

    dc.SetPen(wxNullPen);
    dc.SetBrush(wxNullBrush);
}

wxSize ScrollBar::DoGetBestSize() const
{
    return wxSize(-1, 35);
}

#if UPDATE_ON_RESIZE
void ScrollBar::on_size(wxSizeEvent& ev)
{
    Refresh();
    ev.Skip();
}
#endif
// ********************************************** //

class MyApp: public wxApp
{
    wxFrame* frame_;
    ScrollBar* sc_bar_;
public:
    bool OnInit()
    {
        if ( !wxApp::OnInit() )
            return false;

        frame_ = new wxFrame(nullptr, wxID_ANY, "ScBar test App");
        sc_bar_ = new ScrollBar(frame_, wxID_ANY);

        wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
        sizer->Add(new wxButton(frame_, -1, "Tiny Button"), 0, 0, 0);
        sizer->Add(sc_bar_, 0, wxEXPAND, 0);

        frame_->SetSizer(sizer);
        frame_->Show();
        return true;
    }
};

IMPLEMENT_APP(MyApp)
为什么静态绘制不改变位置、大小和拇指滑块始终绘制正确,并且调整大小不会对该矩形产生任何影响? 然后,闪烁的战争开始了。擦除背景毫无帮助:

#define ENABLE_ERASING 1
我发现解决方案需要添加正确的处理程序来调整大小:

#define ENABLE_ERASING 0
#define UPDATE_ON_RESIZE 1
这是正确的解决方案吗? 我不明白为什么我必须做ev.Skip;在调整事件处理程序中? 为什么,使用wxPanel,wxWindow而不是wxControl?也许我也必须这样做?
操作系统窗口7 x64;MinGW 4.8.1;wxWidgets 3.1.0

wxWidgets显然不是编写在所有平台下都必须具有相同外观的应用程序的好选择,因为这与wxWidgets的设计目标背道而驰,wxWidgets的设计目标与每个平台下的本机外观和感觉无法区分


使用wxWidgets实现您想要的功能的唯一方法是使用它的wxUniversal端口,它在任何地方都实现相同的外观。但是,请注意,与通常的端口wxMSW、wxGTK、wxOSX相比,它有许多错误和缺少的功能,因此如果您决定使用它,您应该准备好处理这些问题。但是,这仍然比使用任何本机端口要好得多,因为您显然正在尝试这样做。

但是,如果我通过从wxControl继承实现所有控件。那么糟糕吗?我将有机会为我的应用程序创建皮肤。您应该使用wxWindow,而不是本机控件的基类wxControl。但是,试图重新实现wxUniversal是没有意义的,从存在的东西开始,并在必要时加以改进。你说“wxUniversal”有bug。我会创造我自己的;-。我是如此的年轻和愚蠢,但我试图获得知识,或者在科学的花岗岩上砸我的头。你能看看我的问题并指出与绘画有关的错误吗。和调整处理程序中的“event.Skip”。非常感谢。尝试创建一个像wxUniv这样的本地端口是没有希望的,您将经常遇到在它中完成事情的方式方面的问题。您必须要么使用wxUniv,要么完全使用wxWidgets以外的东西。我尝试使用wxUniversal端口。我有一个奇怪的印象,这个端口很长一段时间都不受支持。所有的样品都有巨大的闪烁问题,有时他们甚至停止绘画。我甚至还没有开始创建自己的应用程序和外观定制,已经有bug了。Thx的建议,但我会留在我的脑海中,并尝试创建自己的版本的端口。也许你喜欢一个更有经验的用户给我一些如何做的提示。