使用虚拟列表控件的 AppendCollumn() 崩溃

App crashes on AppendCollumn() with a Virtual List Control

我是一名业余程序员,仍在学习 C++ 和 wxWidgets。 我在 Windows 10 Pro 计算机上使用 Code::Blocks 20.3、wxWidgets 3.1.4 和 MinGW 17.1。

我正在尝试让虚拟 wxListCtrl 在 Code::Blocks 中工作, 我遵循了 ListCtrl 示例中的代码和 wxWidgets 论坛中的一个简单示例。 编译和工作的示例(1 个文件)代码。 当我在 Code::Blocks 中开始一个新项目时,GUI APP 和 MAIN 被编码在单独的文件中。 也许我需要做更多,但找不到它是什么。 该项目编译时出现零错误和警告,但在启动时显示以下错误。 我尝试将 SetItemCount() 移动到 AppendCollumn() 之上,但这没有帮助。 当我将两个 AppendColumn() 注释掉时没有错误,但是当然列表是空的。

有人可以帮我解决我的代码中缺少的内容吗?

谢谢你,路德

错误:

A debugging check in this application has failed
../../src/cpmmon.listctrlcmn.cpp(259):
 assert "Assert Failure" failed in OnGetItemText():
 wxListCtrl::OnGetItemText not supposed to be called

GUI 代码:

GUIFrame::GUIFrame( wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& pos, const wxSize& size, long style ) : wxFrame( parent, id, title, pos, size, style )
{
    this->SetSizeHints( wxDefaultSize, wxDefaultSize );

    wxBoxSizer* bSizer1;
    bSizer1 = new wxBoxSizer( wxVERTICAL );

    m_listCtrl1 = new wxListCtrl( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT|wxLC_VIRTUAL );
    bSizer1->Add( m_listCtrl1, 1, wxALL|wxEXPAND, 10 );

    this->SetSizer( bSizer1 );
    this->Layout();

    // Connect Events
    this->Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( GUIFrame::OnClose ) );
}

GUIFrame::~GUIFrame()
{
    // Disconnect Events
    this->Disconnect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( GUIFrame::OnClose ) );
}

主要代码:

#include "VlistMain.h"

const wxChar *SMALL_VIRTUAL_VIEW_ITEMS[][2] =
{
    { wxT("Cat"), wxT("meow") },
    { wxT("Sheep"), wxT("baaah") }
};

VlistFrame::VlistFrame(wxFrame *frame) : GUIFrame(frame)
{
    m_listCtrl1->AppendColumn("Animal");
    m_listCtrl1->AppendColumn("Sound");
    m_listCtrl1->SetItemCount(WXSIZEOF(SMALL_VIRTUAL_VIEW_ITEMS));
}

wxString VlistFrame::OnGetItemText(long item, long column) const
{
    return SMALL_VIRTUAL_VIEW_ITEMS[item][column];
}

事件table:

wxBEGIN_EVENT_TABLE(MyListCtrl, wxListCtrl)
    EVT_LIST_ITEM_SELECTED(wxAny, MyListCtrl::OnSelected)
wxEND_EVENT_TABLE()

要使用虚拟列表控件,您需要从 wxListCtrl 派生一个 class 并覆盖 OnGetItemText 方法。例如:

class MyListCtrl:public wxListCtrl
{
public:
    MyListCtrl(wxWindow *parent, wxWindowID id);
    virtual wxString OnGetItemText (long item, long column) const wxOVERRIDE;
};

MyListCtrl::MyListCtrl(wxWindow *parent, wxWindowID id)
           :wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize,
                       wxLC_VIRTUAL | wxLC_REPORT, wxDefaultValidator,
                       "MyListCtrl")
{

}

wxString MyListCtrl::OnGetItemText(long item, long column) const
{
    return SMALL_VIRTUAL_VIEW_ITEMS[item][column];
}

您还可以覆盖 OnGetItemColumnImage and OnGetItemAttr 以进一步自定义您的控件。


由于看起来您正在使用 wxFormbuilder,因此您可以使用自定义控件小部件来完成此操作。为此,首先您需要将一个新的 header 文件添加到名为 MyListCtrl.h 的项目(或任何您想要的名称)。新 header 的内容应如下所示:

#ifndef MYLISTCTRL_H_INCLUDED
#define MYLISTCTRL_H_INCLUDED

#include <wx/listctrl.h>

class MyListCtrl:public wxListCtrl
{
public:
    MyListCtrl(wxWindow *parent, wxWindowID id);
    virtual wxString OnGetItemText (long item, long column) const wxOVERRIDE;
};

#endif // MYLISTCTRL_H_INCLUDED

一旦您的控件具有 header,您就可以使用“CustomControl”小部件。这是“附加”选项卡上的倒数第二个项目:

在属性选项卡上,您需要设置以下项目

name:          m_listCtrl1
declaration:   MyListCtrl* m_listCtrl1;
construction:  m_listCtrl1 = new MyListCtrl(m_panel1, wxID_ANY);
include:       #include "MyListCtrl.h"

像这样:

您可以在与您开发的框架 class 相同的文件中提供 MyListCtrl::MyListCtrl 构造函数和 MyListCtrl::OnGetItemText 的代码。或者如果你想为每个文件维护一个 class,你也可以把它放在一个新的 cpp 文件中。显然,您需要在用于将这些方法的代码放入其中的任何文件的顶部添加“#include”MyListCtrl.h”。


要使用事件表处理事件,您需要在 class 声明中添加事件处理方法和 wxDECLARE_EVENT_TABLE 宏的声明。

class MyListCtrl:public wxListCtrl
{
public:
    MyListCtrl(wxWindow *parent, wxWindowID id);
    virtual wxString OnGetItemText (long item, long column) const wxOVERRIDE;

protected:
    void OnItemSelected(wxListEvent& event);
    // Add all other event handler method declarations here
private:
    wxDECLARE_EVENT_TABLE();
};

然后在包含代码的文件中,您将拥有如下内容:

wxBEGIN_EVENT_TABLE(MyListCtrl, wxListCtrl)
    EVT_LIST_ITEM_SELECTED(wxID_ANY, MyListCtrl::OnItemSelected)
    /* Add all other event macros here */
wxEND_EVENT_TABLE()


void MyListCtrl::OnItemSelected(wxListEvent& event)
{
    // do something here
}

// write all other event handlers here.

就个人而言,我真的不喜欢事件表,所以我会改用动态事件绑定。为此,class 的声明应如下所示:

class MyListCtrl:public wxListCtrl
{
public:
    MyListCtrl(wxWindow *parent, wxWindowID id);
    virtual wxString OnGetItemText (long item, long column) const wxOVERRIDE;

protected:
    void OnItemSelected(wxListEvent& event);
    // declare all other event handlers here.
};

那么构造函数的代码应该如下所示:

MyListCtrl::MyListCtrl(wxWindow *parent, wxWindowID id)
           :wxListCtrl(parent, id, wxDefaultPosition, wxDefaultSize,
                       wxLC_VIRTUAL | wxLC_REPORT, wxDefaultValidator,
                       "MyListCtrl")
{
    Bind(wxEVT_LIST_ITEM_SELECTED, &MyListCtrl::OnItemSelected, this);
    // Add any other needed Bind statements here
}

这是使用 wxFormbuilder 的 subclass 功能的第二个答案。这种方法的优点是它可以让您使用 wxFormbuilder 设置事件处理程序。首先,我们需要为派生的 class

的构造函数声明一个稍微不同的声明
class MyListCtrl:public wxListCtrl
{
public:
    MyListCtrl(wxWindow *parent,
               wxWindowID id,
               const wxPoint& pos = wxDefaultPosition,
               const wxSize& size = wxDefaultSize,
               long style = wxLC_VIRTUAL | wxLC_REPORT);
    virtual wxString OnGetItemText(long item, long column) const wxOVERRIDE;
};

这个构造函数增加了更多的参数,以便与wxFormbuilder生成的代码兼容。与另一个答案一样,这应该放在添加到项目的新头文件中。

接下来在代码文件中,添加构造函数的主体

MyListCtrl::MyListCtrl(wxWindow *parent, wxWindowID id, const wxPoint& pos,
                       const wxSize& size, long style)
           :wxListCtrl(parent, id, pos, size,
                       wxLC_VIRTUAL | wxLC_REPORT, wxDefaultValidator,
                       "MyListCtrl")
{

}

请注意,这会强制 subclass 具有样式 wxLC_VIRTUAL | wxLC_REPORT 并将忽略在 wxFormbuilder 中输入的任何样式。所以如果你想改变样式,你应该在上面的代码中改变它。

或者您可以将 wxLC_VIRTUAL | wxLC_REPORT 更改为 style。然后将使用在 wxFormbuilder 中输入的样式选项。但是,您需要确保这些选项始终包含 wxLC_REPORT 和 wxLC_VIRTUAL,并且不包含与这两个选项不兼容的任何其他选项。

接下来,在 wxFormbuilder 中,向您的表单添加一个列表控件,并在对象属性选项卡上,将子class 选项设置为

name: MyListCtrl
header: MyLstCtrl.h

(您可以选中或取消选中 forward_declare 选项 - 这没有任何区别)。

现在您可以像添加任何其他小部件一样为您的列表控件添加任何事件处理程序: