天天看點

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/