wxListCtrl和wxListBox

wxListCtrl和wxListBox都是用于显示列表数据的控件,但它们之间有一些区别。

wxListCtrl通常用于显示复杂的表格数据,可以通过设置列来显示多个属性,还支持对列表项进行排序、编辑和选择。
wxListBox通常用于显示简单的单列文本数据,只能显示一个属性,不支持对列表项进行排序和编辑,但支持选择。
因此,如果你需要显示复杂的表格数据并对其进行排序、编辑和选择,应该使用wxListCtrl。如果你只需要显示简单的单列文本数据并支持选择,则可以使用wxListBox。

wxListCtrl 或 wxDataViewCtrl

wxListCtrl 和 wxDataViewCtrl 都是 wxWidgets 框架中的列表控件,它们的主要区别在于它们的数据模型和显示方式不同。

wxListCtrl 是一个基于 Win32 ListView 控件的封装,它使用简单的行列格式来显示数据,每个项目通常只有一列数据。它可以使用 wxLC_REPORT 样式来显示多列数据,但这些列通常只是简单的文本或图标。它的数据模型是基于一个二维数组,每个项目对应一个数组元素,这个数组称为“列表视图”。

相比之下,wxDataViewCtrl 更加灵活,它支持多列数据的显示和自定义单元格渲染方式。它的数据模型是基于 MVC(Model-View-Controller)模式,即将数据模型、视图和控制器分离,使得开发人员可以更加方便地管理和显示复杂的数据结构。在 wxDataViewCtrl 中,每个项目都可以包含多列数据,并且每个单元格可以使用自定义的渲染器来显示不同的内容。

总之,wxListCtrl 适用于简单的列表显示,而 wxDataViewCtrl 适用于更加复杂的数据结构和自定义渲染方式的显示。

Style

此类支持以下样式:

事件

wxEVT_LIST_ITEM_SELECTED事件

wxEVT_LIST_ITEM_SELECTED 是 wxWidgets 框架中的一个事件,它表示用户在列表控件(如 wxListCtrl 或 wxDataViewCtrl)中选择了一个或多个项目。当用户选择一个或多个项目时,该事件会被触发,并将相关信息(如选择的项目的索引)传递给事件处理程序。开发人员可以通过绑定 wxEVT_LIST_ITEM_SELECTED 事件处理程序来响应用户选择项目的操作,以便在用户选择项目时执行一些操作,例如更新相关的控件或执行一些计算。

 Bind( wxEVT_LIST_ITEM_SELECTED, &PANE_LISTVIEW::OnItemSelected, this );


wxEVT_LIST_ITEM_FOCUSED事件

wxEVT_LIST_ITEM_FOCUSED 是 wxWidgets 框架中的一个事件,它表示用户在列表控件(如 wxListCtrl 或 wxDataViewCtrl)中焦点变化了,即用户将焦点从一个项目移动到另一个项目。当用户移动焦点时,该事件会被触发,并将相关信息(如获得焦点的项目的索引)传递给事件处理程序。开发人员可以通过绑定 wxEVT_LIST_ITEM_FOCUSED 事件处理程序来响应用户焦点变化的操作,以便在用户移动焦点时执行一些操作,例如更新相关的控件或执行一些计算。

 Bind( wxEVT_LIST_ITEM_FOCUSED, &PANE_LISTVIEW::OnItemSelected, this );

wxEVT_LIST_ITEM_DESELECTED事件

wxEVT_LIST_ITEM_DESELECTED 是 wxWidgets 框架中的一个事件,它表示用户在列表控件(如 wxListCtrl 或 wxDataViewCtrl)中取消选择了一个或多个项目。当用户取消选择一个或多个项目时,该事件会被触发,并将相关信息(如取消选择的项目的索引)传递给事件处理程序。开发人员可以通过绑定 wxEVT_LIST_ITEM_DESELECTED 事件处理程序来响应用户取消选择项目的操作,以便在用户取消选择项目时执行一些操作,例如更新相关的控件或执行一些计算。

 Bind( wxEVT_LIST_ITEM_DESELECTED, &PANE_LISTVIEW::OnItemDeselected, this );
void SEARCH_PANE_LISTVIEW::OnItemSelected( wxListEvent& aEvent )
{
    CallAfter(
            [&]()
            {
                std::vector<long> list;
                GetSelectRowsList( list );
                m_handler->SelectItems( list );
            } );

    aEvent.Skip();
}

void SEARCH_PANE_LISTVIEW::OnItemDeselected( wxListEvent& aEvent )
{
    CallAfter(
            [&]()
            {
                std::vector<long> list;
                GetSelectRowsList( list );
                m_handler->SelectItems( list );
            } );

    aEvent.Skip();
}

这段代码使用了一个函数对象 CallAfter ,该函数接受一个 lambda 表达式作为参数,该 lambda 表达式捕获了当前作用域中的所有变量并在后台线程中执行。 lambda 表达式中的代码首先创建一个 std::vector 类型的变量 list,然后调用 GetSelectRowsList 函数将选定行的列表存储到该变量中。最后,它调用 m_handler->SelectItems 函数,将选定行的列表作为参数传递给该函数。这段代码的目的是在后台线程中选择选定行的项目。

CallAfter()函数

CallAfter 是 wxWidgets 框架中的一个函数,而不是 C++ 本身的关键字。它的作用是将指定的函数或 lambda 表达式加入到消息队列中,以便在稍后的时间点在主线程中执行。这个函数通常用于在后台线程中执行某些操作后,更新用户界面。由于用户界面只能在主线程中更新,因此 CallAfter 函数允许我们在主线程中异步执行需要更新界面的操作。

在计算机编程中,异步执行指的是程序中的某个操作不会立即返回结果,而是在后台继续执行,同时程序可以继续执行其他操作。当操作完成后,程序会通知相关的回调函数或事件处理程序。

与之相反,同步执行是指程序中的某个操作会一直阻塞当前线程,直到该操作完成并返回结果,才会继续执行后面的代码。

在使用 CallAfter 函数时,异步执行的意思是将指定的函数或 lambda 表达式加入到消息队列中,以便在稍后的时间点在主线程中执行,而不会阻塞当前线程。

void _LISTVIEW::GetSelectRowsList( std::vector<long>& aSelectedList )
{
    long idx = GetFirstSelected();

    if( idx < 0 )   // Nothing selected
        return;

    aSelectedList.emplace_back( idx );

    idx = GetNextSelected( idx );

    while( idx >= 0 )
    {
        aSelectedList.emplace_back( idx );
        idx = GetNextSelected( idx );
    }
}

wxWidgets中的GetFirstSelected()函数什么意思

GetFirstSelected() 是 wxWidgets 框架中 wxListCtrl 类的一个成员函数,它用于获取列表控件中第一个被选中的项目的索引。如果列表控件中没有选中任何项目,则返回值为 -1。

使用 GetFirstSelected() 函数可以方便地获取列表控件中选中的项目,并且可以与其他成员函数(如 GetNextSelected())一起使用,遍历所有选中的项目。例如,可以使用以下代码来遍历列表控件中所有选中的项目:

c
Copy
int index = listCtrl->GetFirstSelected();
while (index != -1)
{
// 处理选中的项目
// …

index = listCtrl->GetNextSelected(index);

}
该代码首先获取第一个选中的项目的索引,然后在循环中处理该项目,并调用 GetNextSelected() 函数来获取下一个选中的项目的索引。当 GetNextSelected() 函数返回 -1 时,表示已经遍历完所有选中的项目。

wxEVT_LIST_ITEM_RIGHT_CLICK 事件

要在 wxListCtrl 控件中右键单击某个项目时触发事件,可以使用以下步骤:

绑定 wxEVT_LIST_ITEM_RIGHT_CLICK 事件处理程序到 wxListCtrl 控件上。例如:

listCtrl->Bind(wxEVT_LIST_ITEM_RIGHT_CLICK, &MyFrame::OnListItemRightClick, this);

在 wxEVT_LIST_ITEM_RIGHT_CLICK 事件处理程序中,获取右键单击的项目的索引。例如:

void MyFrame::OnListItemRightClick(wxListEvent& event)
{
    int index = event.GetIndex();
    // ...
}

根据项目的索引执行相应的操作。例如,可以显示上下文菜单或弹出窗口。

完整的代码示例如下:

void MyFrame::OnListItemRightClick(wxListEvent& event)
{
    int index = event.GetIndex();
    if (index != wxNOT_FOUND)
    {
        // 显示上下文菜单或弹出窗口
        wxMenu menu;
        menu.Append(wxID_COPY, "Copy");
        menu.Append(wxID_CUT, "Cut");
        menu.Append(wxID_PASTE, "Paste");
        PopupMenu(&menu);
    }
}

MyFrame::MyFrame()
{
    // 创建 wxListCtrl 控件
    listCtrl = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT);

    // 绑定右键单击事件处理程序
    listCtrl->Bind(wxEVT_LIST_ITEM_RIGHT_CLICK, &MyFrame::OnListItemRightClick, this);
}

使用案例

wxListCtrl中放21个item,每一行放两个item,每个item显示图标和名字,点击某个图标,该图标呈现响应状态并响应对应的事件

以下是使用wxListCtrl实现放置21个item,每一行放两个item,每个item显示图标和名字,点击某个图标,该图标呈现响应状态并响应对应的事件的代码:

cpp
Copy

// 定义图标的状态
enum IconState {
    Normal,
    Selected,
    Focused
};
// 定义每个项目的数据结构
struct ItemData {
    wxString name;
    wxBitmap normalIcon;
    wxBitmap selectedIcon;
    IconState state; // 图标状态
};
// 创建列表控件
wxListCtrl* ComponentList = new wxListCtrl(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
ComponentList->InsertColumn(0, wxT("Name"));
ComponentList->InsertColumn(1, wxT("Name"));
// 创建项目数据
std::vector<ItemData> itemData = {
    { wxT("Item 1"), wxBitmap(wxT("icon1.png"), wxBITMAP_TYPE_PNG), wxBitmap(wxT("icon1_selected.png"), wxBITMAP_TYPE_PNG), Normal },
    { wxT("Item 2"), wxBitmap(wxT("icon2.png"), wxBITMAP_TYPE_PNG), wxBitmap(wxT("icon2_selected.png"), wxBITMAP_TYPE_PNG), Normal },
    // ...
};
// 插入项目
for (int i = 0; i < itemData.size(); i += 2) {
    int row = ComponentList->GetItemCount();
    ComponentList->InsertItem(row, itemData[i].name, 0);
    if ((i + 1) < itemData.size()) {
        ComponentList->InsertItem(row, itemData[i + 1].name, 1);
    }
}
// 设置图标
for (int i = 0; i < itemData.size(); i++) {
    wxListItem item;
    item.SetMask(wxLIST_MASK_IMAGE);
    item.SetColumn(i % 2);
    item.SetId(i / 2);
    item.SetImage(itemData[i].normalIcon);
    ComponentList->SetItem(item);
}
// 绑定事件处理函数
ComponentList->Bind(wxEVT_LIST_ITEM_SELECTED, [=](wxListEvent& event) {
    int index = event.GetIndex();
    int column = event.GetColumn();
    if (column >= 0 && index >= 0) {
        // 获取项目数据
        ItemData& data = itemData[index * 2 + column];

        // 更新图标状态
        data.state = Selected;
        wxListItem item;
        item.SetMask(wxLIST_MASK_IMAGE);
        item.SetColumn(column);
        item.SetId(index);
        item.SetImage(data.selectedIcon);
        ComponentList->SetItem(item);

        // 处理事件
        wxLogMessage(wxT("Selected: %s"), data.name);
    }
});
ComponentList->Bind(wxEVT_LIST_ITEM_DESELECTED, [=](wxListEvent& event) {
    int index = event.GetIndex();
    int column = event.GetColumn();
    if (column >= 0 && index >= 0) {
        // 获取项目数据
        ItemData& data = itemData[index * 2 + column];

        // 更新图标状态
        data.state = Normal;
        wxListItem item;
        item.SetMask(wxLIST_MASK_IMAGE);
        item.SetColumn(column);
        item.SetId(index);
        item.SetImage(data.normalIcon);
        ComponentList->SetItem(item);

        // 处理事件
        wxLogMessage(wxT("Deselected: %s"), data.name);
    }
});
// 创建组合框架并添加控件
wxStaticBoxSizer* CompGroupLayout = new wxStaticBoxSizer(wxVERTICAL, this, wxT("Components"));
CompGroupLayout->Add(ComponentList, 1, wxEXPAND | wxALL, 5);

SetSizerAndFit(CompGroupLayout);

在这个例子中,我们定义了一个IconState枚举用于表示图标的状态,以及一个ItemData结构体用于存储每个项目的数据。然后我们创建了一个列表控件,并插入了两列。接着,我们创建了一个包含21个项目数据的数组,并使用循环插入了这些项目到列表中。同时,我们也使用循环设置了每个项目的图标。接下来,我们绑定了wxEVT_LIST_ITEM_SELECTED和wxEVT_LIST_ITEM_DESELECTED事件,并在事件处理函数中更新了所选项目的图标状态,并输出对应的事件信息。

值得注意的是,在本例中我们使用了wxLC_SINGLE_SEL样式来设置列表控件的选择模式为单选模式。这意味着用户只能选择一个项目。如果你希望用户可以选择多个项目,请使用wxLC_MULTIPLE_SEL样式。

08-12 21:22