天天看点

duilib开发(六):基本控件介绍

代码仓库:https://github.com/yangpan4485/duilib/tree/develop/MyDemo

一、基本控件介绍

duilib 里面没有提供默认的控件样式,所以我们就要使用自己的控件样式了

1、Button,Label,Edit,这三个控件应该是我们平时使用最多的控件了,现在我们使用这 3 个控件做一个简单的登录界面,这里我们使用系统自带的标题栏按钮,所以在这里就不使用 WindowImplBase 了,还是使用最原始的方法

login.xml 内容如下

<?xml version="1.0" encoding="UTF-8"?>
<Window size="960,540" mininfo="600,400" caption="0,0,0,32" sizebox="4,4,4,4">
  <Font id="0" name="宋体" size="18" bold="false" underline="false" italic="false" />
  <VerticalLayout bkcolor="#FFDFFDF0" width="300" height="318">  <!-- 整个窗口使用 VerticalLayout 布局 -->
  	<VerticalLayout>
        <Label name= "userIdLabel" text="用户名:" float="true" pos="300,80,370,120" normaltextcolor="white" font="0"/>
        <Label name= "passwordLabel" text="密  码:" float="true" pos="300,140,370,180" normaltextcolor="white" font="0"/>

        <Edit name="userIdEdit" text="" float="true" pos="370,85,0,0" width="250" height="30" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="0"/>
        <Edit name="passwordEdit" text="" float="true" pos="370,146,0,0" width="250" height="30" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="0"/>

        <Button name="btnLogin" text="登录" float="true" pos="300,220,400,250" normalimage=" file='common/button_normal.bmp' " hotimage=" file='common/button_over.bmp' " pushedimage=" file='common/button_down.bmp' " font="0"/>
        <Button name="btnQuit" text="退出" float="true" pos="500,220,600,250" normalimage=" file='common/button_normal.bmp' " hotimage=" file='common/button_over.bmp' " pushedimage=" file='common/button_down.bmp' " font="0"/>
  	</VerticalLayout>
  </VerticalLayout>
</Window>           

login.h

#pragma once

#include "UIlib.h"

class Login : public DuiLib::CWindowWnd, public DuiLib::INotifyUI 
{
public:
    Login();
    ~Login();

    void Init();
    bool CreateDUIWindow();
    void ShowWindow();

    LPCTSTR GetWindowClassName() const override;
    void Notify(DuiLib::TNotifyUI& msg) override;
    LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) override;

    LRESULT OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam);
    LRESULT OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam);

    void OnClick(DuiLib::TNotifyUI& msg);

private:
    HINSTANCE _hInstance;
    DuiLib::CPaintManagerUI _paintManager{};
    HWND _ownerWnd{};
};           

login.cpp

#include "login.h"

#include <iostream>
#include <string>

Login::Login()
{

}
Login::~Login()
{

}

void Login::Init()
{
    SetProcessDPIAware();
    _hInstance = GetModuleHandle(0);
    DuiLib::CPaintManagerUI::SetInstance(_hInstance);
    DuiLib::CPaintManagerUI::SetResourcePath(DuiLib::CPaintManagerUI::GetInstancePath() + +_T("resources"));
}
bool Login::CreateDUIWindow()
{
    _ownerWnd = Create(NULL, _T("Login"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
    if (!_ownerWnd)
    {
        std::cout << "create dui window failed" << std::endl;
        return false;
    }
    return true;
}
void Login::ShowWindow()
{
    ShowModal();
}

LPCTSTR Login::GetWindowClassName() const
{
    return _T("DUILOGINFrame");
}
void Login::Notify(DuiLib::TNotifyUI& msg)
{
    if (msg.sType == _T("click"))
    {
        OnClick(msg);
    }
}
void Login::OnClick(DuiLib::TNotifyUI& msg)
{
    if (msg.pSender->GetName() == _T("btnLogin")) 
    {
        auto editUserId = (DuiLib::CEditUI*)_paintManager.FindControl(_T("userIdEdit"));
        auto editPassword = (DuiLib::CEditUI*)_paintManager.FindControl(_T("passwordEdit"));
        std::string userId = editUserId->GetText();
        std::string password = editPassword->GetText();
        std::cout << "userId:" << userId << std::endl;
        std::cout << "password:" << password << std::endl;
    }
    else if (msg.pSender->GetName() == _T("btnQuit"))
    {
        Close();
    }
}
LRESULT Login::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) 
{
    LRESULT lRes = 0;
    switch (uMsg) {
    case WM_CREATE:
        lRes = OnCreate(uMsg, wParam, lParam);
        break;
    case WM_CLOSE:
        lRes = OnClose(uMsg, wParam, lParam);
        break;
    default:
        break;
    }
    if (_paintManager.MessageHandler(uMsg, wParam, lParam, lRes))
    {
        return lRes;
    }

    return __super::HandleMessage(uMsg, wParam, lParam);
}

LRESULT Login::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    _paintManager.Init(m_hWnd);
    DuiLib::CDialogBuilder builder;
    DuiLib::CControlUI* pRoot = builder.Create(_T("login.xml"), (UINT)0, NULL, &_paintManager);
    _paintManager.AttachDialog(pRoot);
    _paintManager.AddNotifier(this);
    return 0;
}
LRESULT Login::OnClose(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    return 0;
}           

运行结果如下所示

duilib开发(六):基本控件介绍

这个时候会发现密码框的数字可以看到,这里要对 CEdit 加上 password="true" 这个属性,这样再运行就可以了

改完之后我们还会发现一个问题就是可以输入中文,我们一般是不允许密码是中文的,我们需要对输入的字符做一些处理,当用户输入时,编辑框的内容被用户改变了,会触发EN_CHANGE事件,查看CEdit源码,我们发现有这样一个函数

LRESULT CEditWnd::OnEditChanged(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
{
	if( !m_bInit ) return 0;
	if( m_pOwner == NULL ) return 0;
	// Copy text back
	int cchLen = ::GetWindowTextLength(m_hWnd) + 1;
	LPTSTR pstr = static_cast<LPTSTR>(_alloca(cchLen * sizeof(TCHAR)));
	ASSERT(pstr);
	if( pstr == NULL ) return 0;
	::GetWindowText(m_hWnd, pstr, cchLen);
	m_pOwner->m_sText = pstr;
	m_pOwner->GetManager()->SendNotify(m_pOwner, DUI_MSGTYPE_TEXTCHANGED);
	if( m_pOwner->GetManager()->IsLayered() ) m_pOwner->Invalidate();
	return 0;
}           

会发送一个 DUI_MSGTYPE_TEXTCHANGED 的消息,在UIDefine.h里面有这样一个定义

#define DUI_MSGTYPE_TEXTCHANGED            (_T("textchanged"))           

接下来我们就可以在 Notify 里面处理一下 textchanged 这个消息了

加入我们的 Button 的样式都是一样的,所以我们可以为 Button 指定一个默认样式,这样之后改动就只用改一次属性就可以了。给<Window>节点添加一个Default节点即可,其中name属性填写控件的名字,value属性添加控件的属性的值,不过需要将双引号【"】换成【&quot;】,单引号【'】换成【&apos;】,单引号也可以不转换。接下来我们的 xml 会变成下面这样

<?xml version="1.0" encoding="UTF-8"?>
<Window size="960,540" mininfo="600,400" caption="0,0,0,32" sizebox="4,4,4,4">
  <Font id="0" name="宋体" size="18" bold="false" underline="false" italic="false" />
  <Default name="Button" value=" height=&quot;30&quot; width=&quot;100&quot; normalimage=&quot;file=&apos;common/button_normal.bmp&apos;&quot; hotimage=&quot;file=&apos;common/button_over.bmp&apos;&quot; pushedimage=&quot;file=&apos;common/button_down.bmp&apos;&quot; font=&quot;0&quot;" />

  <VerticalLayout bkcolor="#FFDFFDF0" width="300" height="318">  <!-- 整个窗口使用 VerticalLayout 布局 -->
  	<VerticalLayout>
        <Label name= "userIdLabel" text="用户名:" float="true" pos="300,80,370,120" normaltextcolor="white" font="0"/>
        <Label name= "passwordLabel" text="密  码:" float="true" pos="300,140,370,180" normaltextcolor="white" font="0"/>

        <Edit name="userIdEdit" text="" float="true" pos="370,85,0,0" width="250" height="30" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="0"/>
        <Edit name="passwordEdit" password="true" text="" float="true" pos="370,146,0,0" width="250" height="30" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="0"/>

        <Button name="btnLogin" text="登录" float="true" pos="300,220,400,250"/>
        <Button name="btnQuit" text="退出" float="true" pos="500,220,600,250"/>
  	</VerticalLayout>
  </VerticalLayout>
</Window>           

2、单选框、多选框

duilib 中单选框和多选框都可以用 Option 表示,多选框也可以用 CheckBox 表示

xml 文件内容如下

<?xml version="1.0" encoding="UTF-8"?>
<Window size="960,540" mininfo="600,400" caption="0,0,0,32" sizebox="4,4,4,4">
  <Font id="0" shared="true" name="宋体" size="18" bold="false" underline="false" italic="false" />
  <Font id="1" shared="true" name="宋体" size="20" bold="false" underline="false" italic="false" />
  <Default shared="true" name="Button" value=" height=&quot;30&quot; width=&quot;100&quot; normalimage=&quot;file=&apos;common/button_normal.bmp&apos;&quot; hotimage=&quot;file=&apos;common/button_over.bmp&apos;&quot; pushedimage=&quot;file=&apos;common/button_down.bmp&apos;&quot; font=&quot;0&quot;" />
  <Default shared="true" name="Option" value="textcolor=&quot;#FFbac0c5&quot; hottextcolor=&quot;#FF386382&quot; selectedtextcolor=&quot;#FF386382&quot; disabledtextcolor=&quot;#FFbac0c5&quot; textpadding=&quot;18,2,0,0&quot; align=&quot;left&quot; selectedimage=&quot;file='common/RadioBtnSel.png' source='0,0,13,13' dest='0,9,14,23'&quot; normalimage=&quot;file='common/RadioBtnNon.png' source='0,0,13,13' dest='0,9,14,23'&quot;" />
  <Default shared="true" name="CheckBox" value="textcolor=&quot;#FFbac0c5&quot; hottextcolor=&quot;#FF386382&quot; selectedtextcolor=&quot;#FF386382&quot; disabledtextcolor=&quot;#FFbac0c5&quot; textpadding=&quot;20,2,0,0&quot; align=&quot;left&quot; selectedimage=&quot;file='common/checked.png' dest='0,8,16,24'&quot; normalimage=&quot;file='common/unchecked.png' dest='0,8,16,24'&quot;" />

  <VerticalLayout bkcolor="#FFDFFDF0" width="300" height="318">  <!-- 整个窗口使用 VerticalLayout 布局 -->
  	<VerticalLayout>
        <Label name= "userIdLabel" text="用户名:" float="true" pos="300,80,370,120" normaltextcolor="white" font="0"/>
        <Label name= "passwordLabel" text="密  码:" float="true" pos="300,140,370,180" normaltextcolor="white" font="0"/>

        <Edit name="userIdEdit" text="" float="true" pos="375,85,0,0" width="250" height="30" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="0"/>
        <Edit name="passwordEdit" password="true" text="" float="true" pos="375,146,0,0" width="250" height="30" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="0"/>

        <Button name="btnLogin" text="登录" float="true" pos="300,300,400,330"/>
        <Button name="btnQuit" text="退出" float="true" pos="500,300,600,330"/>

        <Option name="radioTeacher" group="radio" text="老师" pos="370,200,450,230" float="true" font="0" />
        <Option name="radioStudent" group="radio" text="学生" pos="500,200,580,230" float="true" font="0" />

        <CheckBox name="CheckBox1" pos="370,250,450,280" float="true" text="选择1" font="0" />
        <CheckBox name="CheckBox2" pos="500,250,580,280" float="true" text="选择2" font="0" />


  	</VerticalLayout>
  </VerticalLayout>
</Window>           

在上面代码中 Option,CheckBox 都是做了默认样式的处理,当然自己也可以做修改,其中有几个新的属性在这里介绍一下

textcolor:默认文本颜色
hottextcolor:鼠标移动上去的文本颜色
selectedtextcolor:选中的时候文本颜色
disabledtextcolor:失效的时候文本颜色,也就是选中了其它的 Option
textpadding:该控件的文本内容的显示和该控件的内边距属性的内容
align:对齐方式
selectedimage:被选中的时候的图片,也就是带点的图片
source:图片的位置。想在控件上画出来图片的哪一块。这一个也可随便坐标
dest:你要放在这个控件的哪里。如果不设就是整个控件           

在代码中判断是否选中,可以做下面的处理

void Login::OnClick(DuiLib::TNotifyUI& msg)
{
    if (msg.pSender->GetName() == _T("btnLogin")) 
    {
        auto editUserId = (DuiLib::CEditUI*)_paintManager.FindControl(_T("userIdEdit"));
        auto editPassword = (DuiLib::CEditUI*)_paintManager.FindControl(_T("passwordEdit"));
        std::string userId = editUserId->GetText();
        std::string password = editPassword->GetText();
        std::cout << "userId:" << userId << std::endl;
        std::cout << "password:" << password << std::endl;

        auto option = dynamic_cast<DuiLib::COptionUI*>(_paintManager.FindControl(_T("radioTeacher")));
        std::cout << "radioTeacher:" << option->IsSelected() << std::endl;
        option = dynamic_cast<DuiLib::COptionUI*>(_paintManager.FindControl(_T("radioStudent")));
        std::cout << "radioStudent:" << option->IsSelected() << std::endl;

        auto checkBox = dynamic_cast<DuiLib::CCheckBoxUI*>(_paintManager.FindControl(_T("CheckBox1")));
        std::cout << "CheckBox1:" << checkBox->GetCheck() << std::endl;
        checkBox = dynamic_cast<DuiLib::CCheckBoxUI*>(_paintManager.FindControl(_T("CheckBox2")));
        std::cout << "CheckBox2:" << checkBox->GetCheck() << std::endl;
        
    }
    else if (msg.pSender->GetName() == _T("btnQuit"))
    {
        Close();
    }
    else if (msg.pSender->GetName() == _T("radioTeacher"))
    {
        std::cout << "radioTeacher selected" << std::endl;
    }
    else if (msg.pSender->GetName() == _T("radioStudent"))
    {
        std::cout << "radioStudent selected" << std::endl;
    }
    else if (msg.pSender->GetName() == _T("CheckBox1"))
    {
        std::cout << "CheckBox1 click" << std::endl;
        
    }
    else if (msg.pSender->GetName() == _T("CheckBox2"))
    {
        std::cout << "CheckBox2 click" << std::endl;
    }
}
void Login::OnEditTextChange(DuiLib::TNotifyUI& msg)
{
    if (msg.pSender->GetName() == _T("passwordEdit")) {
        
    }
}           

其中多选框也可以使用 IsSelected() 判断是不是被选中了,因为 GetCheck() 函数内部调用的就是 IsSelected() 函数

运行代码,结果如下所示

duilib开发(六):基本控件介绍

3、组合框也叫做下拉框 

xml 代码如下

<?xml version="1.0" encoding="UTF-8"?>
<Window size="960,540" mininfo="600,400" caption="0,0,0,32" sizebox="4,4,4,4">
  <Font id="0" shared="true" name="宋体" size="18" bold="false" underline="false" italic="false" />
  <Font id="1" shared="true" name="宋体" size="20" bold="false" underline="false" italic="false" />
  <Font id="2" shared="true" name="宋体" size="16" bold="false" underline="false" italic="false" />
  <Default shared="true" name="Button" value=" height=&quot;30&quot; width=&quot;100&quot; normalimage=&quot;file=&apos;common/button_normal.bmp&apos;&quot; hotimage=&quot;file=&apos;common/button_over.bmp&apos;&quot; pushedimage=&quot;file=&apos;common/button_down.bmp&apos;&quot; font=&quot;0&quot;" />
  <Default shared="true" name="Option" value="textcolor=&quot;#FFbac0c5&quot; hottextcolor=&quot;#FF386382&quot; selectedtextcolor=&quot;#FF386382&quot; disabledtextcolor=&quot;#FFbac0c5&quot; textpadding=&quot;18,2,0,0&quot; align=&quot;left&quot; selectedimage=&quot;file='common/RadioBtnSel.png' source='0,0,13,13' dest='0,9,14,23'&quot; normalimage=&quot;file='common/RadioBtnNon.png' source='0,0,13,13' dest='0,9,14,23'&quot;" />
  <Default shared="true" name="CheckBox" value="textcolor=&quot;#FFbac0c5&quot; hottextcolor=&quot;#FF386382&quot; selectedtextcolor=&quot;#FF386382&quot; disabledtextcolor=&quot;#FFbac0c5&quot; textpadding=&quot;20,2,0,0&quot; align=&quot;left&quot; selectedimage=&quot;file='common/checked.png' dest='0,8,16,24'&quot; normalimage=&quot;file='common/unchecked.png' dest='0,8,16,24'&quot;" />

  <VerticalLayout bkcolor="#FFDFFDF0" width="300" height="318">  <!-- 整个窗口使用 VerticalLayout 布局 -->
  	<VerticalLayout>
        <Label name= "userIdLabel" text="用户名:" float="true" pos="300,80,370,120" normaltextcolor="white" font="0"/>
        <Label name= "passwordLabel" text="密  码:" float="true" pos="300,140,370,180" normaltextcolor="white" font="0"/>

        <Edit name="userIdEdit" text="" float="true" pos="375,85,0,0" width="250" height="30" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="0"/>
        <Edit name="passwordEdit" password="true" text="" float="true" pos="375,146,0,0" width="250" height="30" bkcolor="#FFFFFFFF" textpadding="4,3,4,3" textcolor="#FF000000" disabledtextcolor="#FFA7A6AA" font="0"/>

        <Label name= "serverLabel" text="服务器:" float="true" pos="300,200,370,230" normaltextcolor="white" font="0"/>
        <Combo name="env_type" pos="375,200,520,230" width="250" float="true" itemtextcolor="#FF000000" font="3" normalimage=" file='common/normal.png' " hotimage=" file='common/normal.png' " pushedimage=" file='common/normal.png' " itemfont="2" >
          <ListLabelElement text=" 线上环境" selected="true" height="30"/>
          <ListLabelElement text=" 测试环境" height="30"/>
        </Combo>

        <Option name="radioTeacher" group="radio" text="老师" pos="370,260,450,290" float="true" font="0" />
        <Option name="radioStudent" group="radio" text="学生" pos="500,260,580,290" float="true" font="0" />

        <CheckBox name="CheckBox1" pos="370,320,450,350" float="true" text="选择1" font="0" />
        <CheckBox name="CheckBox2" pos="500,320,580,350" float="true" text="选择2" font="0" />

        <Button name="btnLogin" text="登录" float="true" pos="300,390,400,420"/>
        <Button name="btnQuit" text="退出" float="true" pos="500,390,600,420"/>


  	</VerticalLayout>
  </VerticalLayout>
</Window>           

其中 itemfont 表示子列表的字体大小

在代码中识别选中了哪个

void Login::OnClick(DuiLib::TNotifyUI& msg)
{
    if (msg.pSender->GetName() == _T("btnLogin")) 
    {
        auto editUserId = (DuiLib::CEditUI*)_paintManager.FindControl(_T("userIdEdit"));
        auto editPassword = (DuiLib::CEditUI*)_paintManager.FindControl(_T("passwordEdit"));
        std::string userId = editUserId->GetText();
        std::string password = editPassword->GetText();
        std::cout << "userId:" << userId << std::endl;
        std::cout << "password:" << password << std::endl;

        auto option = dynamic_cast<DuiLib::COptionUI*>(_paintManager.FindControl(_T("radioTeacher")));
        std::cout << "radioTeacher:" << option->IsSelected() << std::endl;
        option = dynamic_cast<DuiLib::COptionUI*>(_paintManager.FindControl(_T("radioStudent")));
        std::cout << "radioStudent:" << option->IsSelected() << std::endl;

        auto checkBox = dynamic_cast<DuiLib::CCheckBoxUI*>(_paintManager.FindControl(_T("CheckBox1")));
        std::cout << "CheckBox1:" << checkBox->GetCheck() << std::endl;
        checkBox = dynamic_cast<DuiLib::CCheckBoxUI*>(_paintManager.FindControl(_T("CheckBox2")));
        std::cout << "CheckBox2:" << checkBox->GetCheck() << std::endl;

        auto combo = dynamic_cast<DuiLib::CComboUI*>(_paintManager.FindControl(_T("env_type")));
        std::cout << "env_type:" << combo->GetText() << std::endl;
        std::cout << "env_type:" << combo->GetCurSel() << std::endl;  // 第一个输出0
        
    }
    else if (msg.pSender->GetName() == _T("btnQuit"))
    {
        Close();
    }
    else if (msg.pSender->GetName() == _T("radioTeacher"))
    {
        std::cout << "radioTeacher selected" << std::endl;
    }
    else if (msg.pSender->GetName() == _T("radioStudent"))
    {
        std::cout << "radioStudent selected" << std::endl;
    }
    else if (msg.pSender->GetName() == _T("CheckBox1"))
    {
        std::cout << "CheckBox2 click" << std::endl;
        
    }
    else if (msg.pSender->GetName() == _T("CheckBox2"))
    {
        std::cout << "CheckBox2 click" << std::endl;
    }
}           
duilib开发(六):基本控件介绍

到这里一些基本的控件也都介绍的差不多了

二、参考文章

1、DuiLib xml 配置项:https://alenstar.github.io/post/duilib_xml/

2、Duilib CEdit 禁止输入中文的办法:https://www.bbsmax.com/A/amd012XDzg/