【wxWidgets教程】控件基础知识

控件是构成界面的基本元素,也是wxWidgets的主体。优秀的工程师不仅要像神农遍尝百草了解药性那样熟悉各类控件的特性,还要像医生给病人开处方那样针对不同的应用特点合理搭配各类控件以满足多样化的客户需求,"譬之若良医疗病,病万变药亦万变。"

wxWidgets控件包括了标签、按纽、文本框、滑块、树、列表框、组合框等基本控件;标签页、面板等容器控件,网格、属性管理、数据视图等数据控件,网页与浏览器等Web控件;如果现有的控件无法满足应用需求,您也可以自定义控件。

与MFC、FLTK和Qt相比,wxWidgets更丰富,不仅包括最基础的常规控件,也包含常用的复合控件,在很大程度上提高了程序开发的便利。

每一类型的控件既有共同的属性和使用方式,又有一套相对独立、自成体系的应用场景、交互方式和使用方法。类似官方手册细致入微的介绍和面面俱到的讲解将会使读者陷入细节的泥潭而迷失在千姿百态的控件组成的丛林中。本章将放弃控件的细节部分,从一个宏观的角度对控件进行基础性、全局性和整体性的描述,旨在为读者提供一种用于把握这些控件所必需的思维方法和认知框架。

正如面向对象设计所要求的那样,每一个控件都被精心地设计成类,是对状态、操作、方法、事件、外观等要素的封装。

针对不同的控件,除个别参数的数量会有增减、定义略有不同外,其创建方式基本上保持了一致的风格,几乎所有wxWidgets控件创建都需要父窗口、ID、位置、大小、样式、验证器和名称等参数。这种一以贯之的控件创建方式,与微软MFC难以捉摸且复杂多变的创建风格相比,更能被开发者轻松掌握和熟练运用。

一、创建控件

正如面向对象设计所要求的那样,每一个控件都被精心地设计成类,是对状态、操作、方法、事件、外观等要素的封装。

针对不同的控件,除个别参数的数量会有增减、定义略有不同外,其创建方式基本上保持了一致的风格,几乎所有wxWidgets控件创建都需要父窗口、ID、位置、大小、样式、验证器和名称等参数。这种一以贯之的控件创建方式,与微软MFC难以捉摸且复杂多变的创建风格相比,更能被开发者轻松掌握和熟练运用。

创建控件的一般方式是通过构造函数或Create()函数实现。以在wxFrame中创建一个文本输入框为例:

cpp 复制代码
class MyFrame:public wxFrame{
    //定义一个接受输入值的变量
    wxString m_string;
    //在堆上定义文本输入框指针变量
    //该变量应该在OnClose()函数中释放,此处略去相关代码
    wxTextCtrl* m_text;
    //......
}
cpp 复制代码
MyFrame::MyFrame():wxFrame(){
    //使用Create()方法创建控件
    m_text= new wxTextCtrl();
    m_text->Create( frame,   //父窗口指针
                wxID_ANY,   //控件ID
                "",          //初始化字符串[这个位置的参数因控件而异]
                wxDefaultPosition, wxDefaultSize,//默认位置与大小
                wxTE_MULTILINE|wxHSCROLL|wxTE_CAPITALIZE,//控件样式
                wxTextValidator(wxFILTER_ALPHA, &m_string),//验证器
                wxTextCtrlNameStr);  //控件名称
                //......
}

cpp 复制代码
MyFrame::MyFrame():wxFrame(){
    //使用构造函数创建控件
    m_text = new wxTextCtrl(frame, //父窗口指针
        wxID_ANY, //控件ID
        "",//初始化字符串
        wxDefaultPosition, wxDefaultSize,//默认位置与大小
        wxTE_MULTILINE|wxHSCROLL|wxTE_CAPITALIZE,//控件样式
        wxTextValidator(wxFILTER_ALPHA, &m_string),//验证器
        wxTextCtrlNameStr);//控件名称
        //......
}

通常,构造函数或Create()函数的第一参数是指向包含此控件的父窗口的指针;第二个参数为可区分的控件ID;倒数第五和第四个参数分别为控件位置与大小;倒数第三个参数为控件样式;倒数第二个为验证器对象;最后的参数为控件名称。第二个参数之后、倒数第五个参数之前的中间参数的个数(有的控件可能为0)和类型因控件而异。

所有控件的构造函数或Create()函数都保持了这种参数布局,了解这一点,非常重要,这有助于您正确地把握wxWidgets纷繁芜杂的对象世界,当您面对一个从末遇到过的新控件时,能够帮助您缓解因陌生而产生的距离感和畏惧感。

对于同一个控件,其构建函数的参数个数、类型和顺序与Create()方法完全相同。以此两种创建控件的方式是等效的,您可自由选择。

1 、父窗口

与Qt不同,wxWidgets控件不能独立存在,需要依托于一个特定的窗体wxWindows,这个窗体可以是wxFrame或wxDialog的派生类,也可以是具有容器功能的控件(如wxPanel、wxBoxSizer等)。所有控件Create()函数中的第1个参数用来指定父窗口并建立控件与父窗口的联系。

2 、控件 ID

在一个特定的窗体中,其控件都有一个唯一ID。用于事件处理中识别事件源或目标。控件ID是一个int类型的变量。如果并不需要关心某个控件的ID,可以将其设置为wxID_ANY,wxWidgets会为其自动分配一个小于0的随机整数;如果需要为这个控件添加事件处理等功能,则需要指定一个特定的整数值。为了增强程序的可读性,通常的做法是在代码中采用enum定义控件ID,下面是wxWidget源码中定义的标准ID示例:

cpp 复制代码
enum wxStandardID{  
    //......
    /* all predefined ids are between wxID_LOWEST and wxID_HIGHEST */
    wxID_LOWEST = 4999, 
    wxID_OPEN,
    wxID_CLOSE,
    //...... 
    wxID_EDIT = 5030,
    wxID_CUT,
    wxID_COPY,
    //......

    wxID_VIEW_DETAILS,
    wxID_VIEW_LARGEICONS,
    wxID_VIEW_SMALLICONS, 
    //...... 
    wxID_FILE = 5050,
    wxID_FILE1,
    //...... 
};

所有控件Create()函数中的第2个参数用来指定控件ID。

3 、位置和大小

控件放置于父窗口的位置由参数pos指定,pos是一个wxPoint类型的变量,提供了标识控件位置的坐标值(x,y);控件大小由参数size指定,size是wxSize类型的变量,提供了标识控件大小的长度和宽度。如果使用了布局管理器(参考Error! Bookmark not defined.),那么开发者可能并不需要关心控件的位置与大小,可以分别将其设置为wxDefaultPostion和wxDefaultSize。

4 、控件样式

控件有不同的显示样式,样式影响控件的外观和功能。参数style指定了控件样式。style是一个long型变量。其取值范围为集合{2n|0≤n≤(sizeof(long)×8)},控件在创建时可以指定多个样式,各样式通过"位或(|)"操作连接。

除在创建时设置控件样式外,也可以在程序运行过程中通过控件对象的void wxWindow::SetWindowStyle(long style)函数进行动态更改。

5 、验证器

验证器是一个wxValidator类型的变量,其作用是为了方便对话框应用程序的开发。作为植入控件的一个外部插件,它有三项职能:

一是实现C++变量与控件之间的双向数据输。在对话框应用程序中,当调用wxDialog::Show()或wxDialog::ShowModal()初始化对话框时,会调用对话框的TransferDataToWindow()函数,该函数会遍历每一个控件的验证器并调用其上的wxValidator::TransferToWindow()方法,用C++变量初始化控件。当用户点击对话框上的"确认"按纽时,会调用wxWindow::Validate()函数,该函数会遍历每一个控件的验证器并调用其上的wxValidator::Validate()函数,如果验证失败,将会直接返回,否则继续调用wxWindow::TransferDataFromWindow(),该函数会依次调用控件上的wxValidator::TransferFromWindow()方法,将控件值赋给对应的C++变量。

二是用于验证控件数据的合法性,并在出错时弹出错误消息提示。当控件输入数据发生变化时,调用验证器的Validate()函数对控件数据(不是C++变量)进行合法性检测,如果合法,则返回true,操作继续,如果不符合要求,则拒绝数据,并弹出消息提示。

三是拦截控件事件,提供过滤行为,而无需派生新的控件类。

并不是所有的控件都有验证器参数。如果控件不支持或不需要使用验证器,可以将该参数设置为wxDefaultValidator

wxWidgets提供了有限的几个验证器对象,如wxTextValidator、wxGenericValidator、wxNumValidator<T>等。上面的实例在wxTextCtrl控件中使用了wxTextValiator验证器,输入的验证和过滤通过两种方式完成。输入字符时,验证器根据允许的过滤器标志(在本例中为wxFILTER_ALPHA)检查字符。如果字符不合适,它将被拒绝显示并发出警告蜂鸣音(除非调用了wxValidator::SetBellOnError(false))。第二种类型的验证是在对话框即将关闭时执行的,因此,如果默认字符串已经包含无效字符,则会显示一个对话框,给出错误信息,并且不会自动关闭对话框。

6 、名称

名称是一个wxString类型参数,wxWindow::FindWindowByName()可以通过该参数获取控件的指针。如果不需要指定特殊的值,可以使用默认值。如实例中的wxTextCtrlNameStr="text"。

二、事件处理

几乎所有的控件都会触发一类或多类事件。wxWidgets为每一类事件定义了一个唯一的事件标识符,如当wxTextCtrl控件中的输入发生变化时,控件会自动触发wxEVT_TEXT事件,该事件所期望的处理函数原型为:

void handlerFuncName (wxCommandEvent& event )

事实上,所有的事件处理函数都具有上述形式:返回类型为void,参数只有一个,类型为wxEvent派生类。

开发者可以在任意位置定义一个返回类型为void,输入参数为wxCommandEvent&的全局或类成员函数,然后将事件与该函数进行动态或静态绑定。

cpp 复制代码
class MyFrame:public wxFrame{ 
//......
protected:
    //声明文本输入变化事件处理函数
    void OnTextChange(wxCommandEvent& event);
    //静态绑定,声明静态事件表
    wxDECLARE_EVENT_TABLE();
}
//静态态绑定,添加事件与事件处理函数的关联
wxBEGIN_EVENT_TABLE(MyFrame, wxFrame)
    EVT_TEXT(wxID_COPY, MyFrame::OnTextChange) 
wxEND_EVENT_TABLE()
void MyFrame:OnTextChange(wxCommandEvent& event){
    //处理与输入变化相关的代码
}
//动态绑定,添加事件与事件处理函数的关联
MyFrame::MyFrame():wxFrame(){
    //......
    m_text->Bind(wxEVT_TEXT,&MyFrame:OnTextChange,this)
}

动态绑定通过函数bind()建立事件ID(如wxEVT_TEXT)与处理函数的关联;静态绑定利用事件宏(如EVT_TEXT(id,func))建立事件(如wxEVT_TEXT)与处理函数的关联。

细心的您可能发现,将事件ID名称去除"wx"字符后便是对应事件宏的名称。了解这个规律,也是非常有用的。

完成绑定后的函数会在事件触发时由wxWidgets负责调用。通常,事件处理函数在控件的顶层窗体中定义,这样做会给开发带来许多便利,例如可以方便地获得各个成员变量和控件引用等,但这不是一成不变的铁律。

一个控件会触发多个类型事件,大多数情况下,对于同一个控件,这些不同类型的事件往往有着相同类型的处理函数。以wxTextCtrl为例,该控件有wxEVT_TEXT、wxEVT_TEXT_ENTER、wxEVT_TEXT_URL、wxEVT_TEXT_MAXLEN等四种类型的事件,但每一类事件的处理函数类型都是void handlerFuncName(wxCommandEvent& event),开发者可以为每一类事件分别定义一个事件处理函数,也可以共用同一个。

三、XRC定义

同JavaFX中的fxml、Qt中的QXML一样,wxWidgets提供了XRC以支持代码与界面的分离,除了使用C++代码创建控件外,还可以在XRC文件中定义控件及其初始属性值,如下所示:

XML 复制代码
<?xml version="1.0" ?>
<resource version="2.3.0.1">
<object class="wxDialog" name="SimpleDialog">
//......
<object class="wxTextCtrl" name="Hello,World" 
hint="Please Input Something" 
maxlength=20 forceupper=true focused=true/>
//......
  </object>
</resource>

文件定义了一个wxTextCtrl控件,初始值为"Hello,World",允许最大输入长度为20,并且自动转换为大写,当控件输入值为空时,会在控件内显示"Please Input Something"提示性字样。

相关推荐
图形学爱好者_Wu2 小时前
每日一个C++知识点|模板
c++
xiaolang_8616_wjl3 小时前
c++超级细致的基本框架
开发语言·数据结构·c++·算法
Joy-鬼魅3 小时前
Win10x64系统VS2022使用CreateFileMapping返回无效句柄
c++·createfilemap·vc·getlasterror
冷崖3 小时前
排序--基数排序
c++·算法
淼淼7633 小时前
Qt调度 程序
开发语言·c++·windows·qt
暗然而日章4 小时前
C++基础:Stanford CS106L学习笔记 13 特殊成员函数(SMFs)
c++·笔记·学习
云泽8084 小时前
STL容器性能探秘:stack、queue、deque的实现与CPU缓存命中率优化
java·c++·缓存
J ..5 小时前
C++ Lambda 表达式完全指南
c++
Qt程序员5 小时前
从 C++11 到 C++23:枚举的原理升级与实践
c++·c++23