目录:
基于CWindow
0.Duilib之基于CWindowWnd的窗口.
1.无标题栏窗口拖动.
2.win32窗口源码.
3.自定义控件.<这里说一下,WindowImplBase比CWindow 简单>
基于WindowImplBase
1.MFC中嵌入Duilib窗口.
常用控件使用:
1.Richedit控件
基于CWindow
0.Duilib之基于CWindowWnd的窗口.
实际上是由入门程序进行逐渐封装的,思想在这一块,循序渐进的看入门程序章节哦.
源码:没必要细说,直接贴代码了,看懂就行.
参照duilib入门简明教程,这段实际上是存在误区的,已由源码表明,前言不在赘述.
.cpp文件
#pragma once
#include <UIlib.h>
#include <tchar.h>
using namespace DuiLib;
#ifdef _DEBUG
# ifdef _UNICODE
# pragma comment(lib, "DuiLib_d.lib")
# else
# pragma comment(lib, "DuiLibA_d.lib")
# endif
#else
# ifdef _UNICODE
# pragma comment(lib, "DuiLib.lib")
# else
# pragma comment(lib, "DuiLibA.lib")
# endif
#endif
class CDuiFrameWnd : public CWindowWnd, public INotifyUI
{
public:
virtual LPCTSTR GetWindowClassName() const { return _T("DUIMainFrame"); }
//这里进行notify消息响应:比如按钮点击
virtual void Notify(TNotifyUI& msg)
{
if (msg.sType == _T("click"))
{
if (msg.pSender->GetName() == _T("Button"))
{
::MessageBox(NULL, _T("我是按钮"), _T("点击了按钮"), NULL);
}
}
}
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lRes = 0;
//响应截获消息的相关操作:开始
//响应WM_CREATE消息
if( uMsg == WM_CREATE )
{
/*
//它提供的例子这块,这段源码直接注释了吧,实际上没什么用,本就有xml进行布局设置的.
CControlUI *pWnd = new CButtonUI;
pWnd->SetName(_T("Button"));
pWnd->SetText(_T("Hello World")); // 设置文字
pWnd->SetBkColor(0xFF00FF00); // 设置背景色
m_PaintManager.Init(m_hWnd);
*/
CDialogBuilder builder;
CControlUI* pRoot = builder.Create(_T("duilib.xml"), (UINT)0, NULL, &m_PaintManager); // duilib.xml需要放到exe目录下
ASSERT(pRoot && "Failed to parse XML");
m_PaintManager.AttachDialog(pRoot);
//通过FindControl获取button句柄,然后进行SetAttribute进行相关属性的设置.
CControlUI *pButton = m_PaintManager.FindControl("Button");
pButton->SetAttribute("pos", "0,0,100,100");
pButton->SetAttribute("bkcolor", "0xFFFF00FF");
/*响应按钮消息事件
1、调用AddNotifier函数将消息加入duilib的消息循环
2、给按钮设置一个唯一的控件ID(SetName函数)
3、在Notify函数里处理按钮点击消息。
*/
m_PaintManager.AddNotifier(this);
return lRes;
}//自行脑补if - else if结构:然后下面三个消息的筛选,duilib其实并没有区分标题栏和客户区,它的实现方法是屏蔽了系统自带的标题栏
else if (WM_NCACTIVATE == uMsg)
{
if (!::IsIconic(m_hWnd))
{
return (wParam == 0) ? TRUE : FALSE;
}
}
else if (WM_NCCALCSIZE == uMsg)
{
return 0;
}
else if (WM_NCPAINT == uMsg)
{
return 0;
}
if( m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes) )
{
return lRes;
}
//响应截获消息的相关操作:开始
//没哟处理:调用基类的消息响应函数进行处理.
return __super::HandleMessage(uMsg, wParam, lParam);
}
protected:
CPaintManagerUI m_PaintManager;
};
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
//类静态成员函数
CPaintManagerUI::SetInstance(hInstance);
CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath()); // 设置资源的默认路径(此处设置为和exe在同一目录)
CDuiFrameWnd duiFrame;
duiFrame.Create(NULL, _T("DUIWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
duiFrame.CenterWindow();
duiFrame.ShowModal();
return 0;
}
.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<Window size="800,600"> <!-- 窗口的初始尺寸 -->
<HorizontalLayout bkcolor="#FF00FF00"> <!-- 整个窗口的背景 -->
<Button name="Button" text="Hello World"/> <!-- 按钮的属性,如名称、文本 -->
</HorizontalLayout>
</Window>
设置按钮属性前样式:
设置按钮样式后:
1.无标题栏窗口拖动.
这里无标题 指的是: Window 标签属性 caption="0,0,0,0"
方法1://这种会对控件的消息照成消息无法响应,除非无控件,不然不推荐
方法二:<有控件的消息响应,包含标题栏拖动功能>
在使用过程中有特需需求添加移动功能. 具体代码:
这里实现类似标题栏的功能. 只有在 特定高度操作才生效:
2.win32窗口源码.
部分代码引用了1标题栏无窗口拖动,请注意甄别.
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
CPaintManagerUI::SetInstance(hInstance);
CDuiString strResourcePath = CPaintManagerUI::GetInstancePath();//获取EXE目录路劲
strResourcePath += _T("skin/");
CPaintManagerUI::SetResourcePath(strResourcePath); // 设置资源的默认路径(此处设置为和exe在同一目录)
CMainWind duiFrame;
duiFrame.Create(NULL, _T("WindowTitle"), UI_WNDSTYLE_CONTAINER, WS_EX_WINDOWEDGE);
duiFrame.ShowModal();
return 0;
}
#pragma once
#include "LoginWnd.h"
class CMainWind : public CWindowWnd, public INotifyUI
{
public:
CMainWind();
virtual ~CMainWind();
public:
virtual LPCTSTR GetWindowClassName() const { return _T("CMainWind"); }
virtual void Notify(TNotifyUI& msg)
{
if (msg.sType == _T("click"))
{
if (msg.pSender->GetName() == _T("HelloWorld"))
{
::MessageBox(NULL, _T("我是按钮"), _T("点击了按钮"), NULL);
}
}
}
//自己封装的函数
bool IsDragRect(POINT pt)
{
RECT rcWnd;
BOOL ret = ::GetWindowRect(m_hWnd, &rcWnd);
int captionHeight = 30; //标题栏高度为30
if (pt.y < captionHeight) {
return true;
}
return false;
}
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lRes = 0;
if (uMsg == WM_CREATE)
{
m_PaintManager.Init(m_hWnd);
CDialogBuilder builder;
CControlUI* pRoot = builder.Create(_T("Main.xml"), (UINT)0, NULL, &m_PaintManager); // duilib.xml需要放到exe目录下
ASSERT(pRoot && "Failed to parse XML");
m_PaintManager.AttachDialog(pRoot);
m_PaintManager.AddNotifier(this); // 添加控件等消息响应,这样消息就会传达到duilib的消息循环,我们可以在Notify函数里做消息处理
/*
//穿件登陆窗口
CLoginWnd LoginFrame;
LoginFrame.Create(NULL, _T("LoginTitle"), UI_WNDSTYLE_CONTAINER, WS_EX_WINDOWEDGE);
LoginFrame.CenterWindow();
LoginFrame.ShowModal();
*/
/*
CListTextElementUI* pItem = new CListTextElementUI;
CDuiString str1, str2, str3;
str1.Format(_T("%s, "),"loWorld");
*/
/*
CListUI* m_Tem = static_cast<CListUI*>(m_PaintManager.FindControl(_T("list_data"))); //Init进行初始化获取控件指针
CListTextElementUI* pItem = new CListTextElementUI();
pItem->SetFixedHeight(30);
m_Tem->Add(pItem);
pItem->SetText(0, _T("HrlloWorld"));
*/
CListUI* pList = static_cast<CListUI*>(m_PaintManager.FindControl(_T("list_data")));
CLabelUI *NewLable = new CLabelUI;
NewLable->SetText(_T("Hello"));
pList->Add(NewLable);
return lRes;
}else if (uMsg == WM_NCACTIVATE)
{
//修改了窗口样式
if (!::IsIconic(m_hWnd))
{
return (wParam == 0) ? TRUE : FALSE;
}
}else if (uMsg == WM_NCCALCSIZE)
{
return 0;
}else if (uMsg == WM_NCPAINT)
{
return 0;
}
static bool m_bStartMove = false;
static POINT m_StartPt;
if (uMsg == WM_MOUSEMOVE)
{
if (::GetAsyncKeyState(VK_LBUTTON) != 0 && m_bStartMove)
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
if (!IsDragRect(pt)) return 0;
int dx = pt.x - m_StartPt.x;
int dy = pt.y - m_StartPt.y;
RECT curRect;
::GetWindowRect(m_hWnd, &curRect);
int cx = curRect.left + dx;
int cy = curRect.top + dy;
int w = curRect.right - curRect.left;
int h = curRect.bottom - curRect.top;
SetWindowPos(m_hWnd, NULL, cx, cy, w, h, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
}
}else if (uMsg == WM_LBUTTONDOWN)
{
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
m_bStartMove = TRUE;
m_StartPt = pt;
}else if (uMsg == WM_LBUTTONUP)
{
m_bStartMove = FALSE;
}
if (m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes))
{
return lRes;
}
return __super::HandleMessage(uMsg, wParam, lParam);
}
protected:
CPaintManagerUI m_PaintManager;
};
3.自定义控件.<这里说一下,WindowImplBase比CWindow 简单>"WindowImplBase直接重写接口,但是CWindow 要继承和知名create"
步骤依次是:创建自定义控件类;添加窗口类的基类IDialogBuilderCallback并重写接口CreateControl;在CDialogBuilder 中Create第三个参数传this;XML中添加该控件.
类.h
#pragma once
const TCHAR kSkinPickerPictureItemClassName[] = _T("SkinPikerPictureItemUI");
const TCHAR kSkinPickerPictureItemInterface[] = _T("SkinPikerPictureItem");
//黑色的前景图的位置
const TCHAR kSkinPickerPictureItemForeImage[] = _T("file='UI\\LeftTab\\listitem\\ListBk.png' fade='150'");
//边框的颜色、图片名称的文字颜色、作者信息的文字颜色
const DWORD kBorderColor = 0xFF64B0FA;
const DWORD kBkNameColor = 0xFFFFFFFF;
const DWORD kAuthorColor = 0xFFAAAAAA;
class CSkinPikerPictureItemUI: public CButtonUI
{
public:
CSkinPikerPictureItemUI();
virtual ~CSkinPikerPictureItemUI();
public:
LPCTSTR GetClass() const;
LPVOID GetInterface(LPCTSTR pstrName);
void SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue);
void PaintStatusImage(HDC hDC);
private:
CDuiString m_BkName;
CDuiString m_Author;
};
类.cpp
#include "stdafx.h"
#include "SkinPikerPictureItemUI.h"
CSkinPikerPictureItemUI::CSkinPikerPictureItemUI()
{
m_Author = _T("作者:");
}
CSkinPikerPictureItemUI::~CSkinPikerPictureItemUI()
{
}
LPCTSTR CSkinPikerPictureItemUI::GetClass() const
{
return kSkinPickerPictureItemClassName;
}
LPVOID CSkinPikerPictureItemUI::GetInterface(LPCTSTR pstrName)
{
if( _tcscmp(pstrName, kSkinPickerPictureItemInterface) == 0 ) return static_cast<CSkinPikerPictureItemUI*>(this);
return CButtonUI::GetInterface(pstrName);
}
void CSkinPikerPictureItemUI::SetAttribute(LPCTSTR pstrName, LPCTSTR pstrValue)
{
if( _tcscmp(pstrName, _T("bkname")) == 0 ) m_BkName = pstrValue;
else if( _tcscmp(pstrName, _T("author")) == 0 ) m_Author += pstrValue;
CButtonUI::SetAttribute(pstrName, pstrValue);
}
void CSkinPikerPictureItemUI::PaintStatusImage(HDC hDC)
{
CButtonUI::PaintStatusImage(hDC);
if( IsFocused() ) m_uButtonState |= UISTATE_FOCUSED;
else m_uButtonState &= ~ UISTATE_FOCUSED;
if( !IsEnabled() ) m_uButtonState |= UISTATE_DISABLED;
else m_uButtonState &= ~ UISTATE_DISABLED;
if( (m_uButtonState & UISTATE_PUSHED) != 0 || (m_uButtonState & UISTATE_HOT) != 0) {
DrawImage(hDC, kSkinPickerPictureItemForeImage) ;
//计算作者信息文字和背景图片名字文字的显示位置,这里是用了硬编码,请使用者自己修改
RECT rcBkName = m_rcItem;
LONG nTextPadding = (m_rcItem.right - m_rcItem.left - CRenderEngine::GetTextSize(hDC, GetManager(),\
m_BkName.GetData(), m_iFont, m_uTextStyle).cx) / 2;
rcBkName.left += nTextPadding;
rcBkName.right -= nTextPadding;
rcBkName.top += 15;
rcBkName.bottom = rcBkName.top + 20;
RECT rcAuthor = m_rcItem;
nTextPadding = (m_rcItem.right - m_rcItem.left - CRenderEngine::GetTextSize(hDC, GetManager(),\
m_Author.GetData(), m_iFont, m_uTextStyle).cx) / 2;
rcAuthor.left += nTextPadding;
rcAuthor.right -= nTextPadding;
rcAuthor.top += 40;
rcAuthor.bottom = rcAuthor.top + 20;
CRenderEngine::DrawText(hDC, m_pManager, rcBkName, m_BkName, kBkNameColor, m_iFont, m_uTextStyle);
CRenderEngine::DrawText(hDC, m_pManager, rcAuthor, m_Author, kAuthorColor, m_iFont, m_uTextStyle);
CRenderEngine::DrawRect(hDC, m_rcItem, 2, kBorderColor);
}
}
派生:
class CMainWind : public CWindowWnd, public INotifyUI, public IDialogBuilderCallback
在HandleMessage处理Create消息.并:
if (uMsg == WM_CREATE)
{
m_PaintManager.Init(m_hWnd);
CDialogBuilder builder;
CControlUI* pRoot = builder.Create(_T("Main.xml"), (UINT)0,this, &m_PaintManager); //
// 这个函数 的第一个参数指定为xml文件的路径;第二个参数一般指定为NULL,我这里不详解了;第三个参数,就是识别自定义控件的关键了,这
//个参数要指定为继承了IDialogBuilderCallback接口的类对象的指针,比如窗体类继承IDialogBuilderCallback,这个参数就填写窗体类对象的指针。只有填写了这个参数,自定义控件才会被识别,经常有人问自己的自定义控件为什么无法被识别。多数情况就是这里没处理好;第四
//个参数指定CPaintManagerUI类对象指针,这个肯定会伴随着窗体类对象一起存在。最后一个参数一般为NULL。
重写:
CControlUI* CreateControl(LPCTSTR pstrClass)
{
if (_tcsicmp(pstrClass, kSkinPickerPictureItemInterface) == 0)
return new CSkinPikerPictureItemUI();
return NULL;
}
XML中进行描述:
<SkinPikerPictureItem name="" width="118" height="70" bkcolor="#FF7F0000" bkimage="BKImage\1small.png" bkname="测试" author="Redrain" />
/*备注*/
从Duilib的自带控件上可以看出,比如当前的自定义控件类名为CSkinPickerPictureItemUI,那么GetClass函数返回的字符串SkinPickerPictureItemUI。而GetInterface函数是根据传入的参数,是否与自身的字符串匹配,来决定能否把自己转换为需要的控件类型。GetInterface中用来匹配的字符串,应该与xml中的对应的控件的标签名称一直,这里应该是SkinPickerPictureItem。
感谢:http://www.bkjia.com/ASPjc/992050.html提供的相关讲解,感谢群duilib的小伙伴.
基于:WindowImplBase
1.MFC中嵌入Duilib窗口.
在MFC中使用duilib
只需要将前面教程的CDuiFrameWnd的父窗口指定为MFC的窗口就好啦,<理解,实际上,CDuiFrameWnd本身就是一个窗口,了解继承关系剖析WindowImplBase逐步上追>
建立 基于对话框的MFC应用程序,将CDuiFrameWnd m_duiFrame; 定义为MFC的成员变量,在MFC的初始化函数OnInitDialog里面创建duilib的窗口,代码如下:
CDuiFrameWnd的类申明文件:
#include <UIlib.h>
#include <tchar.h>
using namespace DuiLib;
#ifdef _DEBUG
# ifdef _UNICODE
# pragma comment(lib, "DuiLib_d.lib")
# else
# pragma comment(lib, "DuiLibA_d.lib")
# endif
#else
# ifdef _UNICODE
# pragma comment(lib, "DuiLib.lib")
# else
# pragma comment(lib, "DuiLibA.lib")
# endif
#endif
class CDuiFrameWnd : public WindowImplBase
{
public:
virtual LPCTSTR GetWindowClassName() const { return _T("DUIMainFrame"); }
virtual CDuiString GetSkinFile() { return _T("duilib.xml"); }
virtual CDuiString GetSkinFolder() { return _T(""); }
virtual void OnFinalMessage(HWND hwnd) {WindowImplBase::OnFinalMessage(hWnd);delete this;}//进行窗口清理和收尾工作.
};
/*
实际上,不管是基于对话框还是基于单文档的应用程序,都可以讲duilib窗口嵌入进去.理解思想,实际上CDuiFrameWnd本身就是窗口.
*/
// TODO: 在此添加额外的初始化代码
CPaintManagerUI::SetInstance(AfxGetInstanceHandle()); // 指定duilib的实例
CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath()); // 指定duilib资源的路径,这里指定为和exe同目录
::CoInitialize(NULL); // 记得释放::CoUninitialize();
m_duiFrame.Create(*this, _T("DUIWnd"), UI_WNDSTYLE_CHILD, 0, 0, 0, 800, 600);
m_duiFrame.ShowWindow(TRUE);
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
常用控件使用:
1.Richedit控件
在duilib开发中,小伙伴会因为CEditUI的各种问题,而转为使用CRichEditUI来代替。但是CRichEditUI控件却不支持文字变化通知事件,下面通过简单几行代码让RichEdit支持文字变化事件。
在UIRichEdith.cpp文件中搜索::OnTxNotify,然后使用以下代码替换此函数:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
好了,需要的小伙伴赶紧试试吧!
duilib开源项目圈QQ群:261675375
最新代码托管地址:https://github.com/qdtroy/DuiLib_Ultimate
实际上是由入门程序进行逐渐封装的,思想在这一块,循序渐进的看入门程序章节哦.
源码:没必要细说,直接贴代码了,看懂就行.
参照duilib入门简明教程,这段实际上是存在误区的,已由源码表明,前言不在赘述.
.cpp文件
#pragma once
#include <UIlib.h>
#include <tchar.h>
using namespace DuiLib;
#ifdef _DEBUG
# ifdef _UNICODE
# pragma comment(lib, "DuiLib_d.lib")
# else
# pragma comment(lib, "DuiLibA_d.lib")
# endif
#else
# ifdef _UNICODE
# pragma comment(lib, "DuiLib.lib")
# else
# pragma comment(lib, "DuiLibA.lib")
# endif
#endif
class CDuiFrameWnd : public CWindowWnd, public INotifyUI
{
public:
virtual LPCTSTR GetWindowClassName() const { return _T("DUIMainFrame"); }
//这里进行notify消息响应:比如按钮点击
virtual void Notify(TNotifyUI& msg)
{
if (msg.sType == _T("click"))
{
if (msg.pSender->GetName() == _T("Button"))
{
::MessageBox(NULL, _T("我是按钮"), _T("点击了按钮"), NULL);
}
}
}
virtual LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
LRESULT lRes = 0;
//响应截获消息的相关操作:开始
//响应WM_CREATE消息
if( uMsg == WM_CREATE )
{
/*
//它提供的例子这块,这段源码直接注释了吧,实际上没什么用,本就有xml进行布局设置的.
CControlUI *pWnd = new CButtonUI;
pWnd->SetName(_T("Button"));
pWnd->SetText(_T("Hello World")); // 设置文字
pWnd->SetBkColor(0xFF00FF00); // 设置背景色
m_PaintManager.Init(m_hWnd);
*/
CDialogBuilder builder;
CControlUI* pRoot = builder.Create(_T("duilib.xml"), (UINT)0, NULL, &m_PaintManager); // duilib.xml需要放到exe目录下
ASSERT(pRoot && "Failed to parse XML");
m_PaintManager.AttachDialog(pRoot);
//通过FindControl获取button句柄,然后进行SetAttribute进行相关属性的设置.
CControlUI *pButton = m_PaintManager.FindControl("Button");
pButton->SetAttribute("pos", "0,0,100,100");
pButton->SetAttribute("bkcolor", "0xFFFF00FF");
/*响应按钮消息事件
1、调用AddNotifier函数将消息加入duilib的消息循环
2、给按钮设置一个唯一的控件ID(SetName函数)
3、在Notify函数里处理按钮点击消息。
*/
m_PaintManager.AddNotifier(this);
return lRes;
}//自行脑补if - else if结构:然后下面三个消息的筛选,duilib其实并没有区分标题栏和客户区,它的实现方法是屏蔽了系统自带的标题栏
else if (WM_NCACTIVATE == uMsg)
{
if (!::IsIconic(m_hWnd))
{
return (wParam == 0) ? TRUE : FALSE;
}
}
else if (WM_NCCALCSIZE == uMsg)
{
return 0;
}
else if (WM_NCPAINT == uMsg)
{
return 0;
}
if( m_PaintManager.MessageHandler(uMsg, wParam, lParam, lRes) )
{
return lRes;
}
//响应截获消息的相关操作:开始
//没哟处理:调用基类的消息响应函数进行处理.
return __super::HandleMessage(uMsg, wParam, lParam);
}
protected:
CPaintManagerUI m_PaintManager;
};
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
//类静态成员函数
CPaintManagerUI::SetInstance(hInstance);
CPaintManagerUI::SetResourcePath(CPaintManagerUI::GetInstancePath()); // 设置资源的默认路径(此处设置为和exe在同一目录)
CDuiFrameWnd duiFrame;
duiFrame.Create(NULL, _T("DUIWnd"), UI_WNDSTYLE_FRAME, WS_EX_WINDOWEDGE);
duiFrame.CenterWindow();
duiFrame.ShowModal();
return 0;
}
.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<Window size="800,600"> <!-- 窗口的初始尺寸 -->
<HorizontalLayout bkcolor="#FF00FF00"> <!-- 整个窗口的背景 -->
<Button name="Button" text="Hello World"/> <!-- 按钮的属性,如名称、文本 -->
</HorizontalLayout>
</Window>
设置按钮属性前样式:
设置按钮样式后: