Duilib库中的点九图使用(解决控件贴图拉伸圆角变形问题等)

Duilib库支持类似安卓系统中所用的点九图技术。参考网络上其他点九图说明资料,在此详述其在Duilib库中的具体应用。

点九图在Duilib库中的使用,有其自身特点。无须像安卓中要在图中绘制标记,Duilib库中所用点九图,就是普通的图像,其拉伸区域的定位标记是在xml的属性参数中体现。

1.点九图原理

如上图,井字红线将原图像瓜分为九宫格。

  • 上、下两区域,在贴图显示时,此区域图像会根据显示尺寸,仅长度方向进行相应拉伸;
  • 左、右两区域,在贴图显示时,此区域图像会根据显示尺寸,仅高度方向进行相应拉伸;
  • 中区域,在贴图显示时,此区域图像会根据显示尺寸,长度、高度方向均进行相应拉伸;
  • 1、2、3、4区域,在贴图显示时,此区域内图像固定不变,即不拉伸;
  • (x1,y1),(x2,y2)两点,确定了井字线的位置,其值为离相应左上,右下边的距离像素数值。

这样的规则,就可利用小像素的图像,通过九宫格方式拉伸,就可形成不同大小的,而四角不变的图像。比较常用于框格的贴图应用。无须再对不同大小的框格而裁切不同大小的图像,避免徒增资源的体积。

2.点九图在Duilib中应用

通过查看Duilib源码项目目录中的“属性列表.xml”文件,xml图像属性中支持点九图功能。

<Attribute comment="背景图片,如(bk.bmp或file='aaa.jpg' res='' restype='0' dest='0,0,0,0' source='0,0,0,0' corner='0,0,0,0' mask='#FF0000' fade='255' hole='false' xtiled='false' ytiled='false')" type="STRING" default="" name="bkimage"/>


应用实例:

以上图为例,像素120x100,圆角半径为10个像素(为显示细节在此已放大,图中“中”字是为说明应用效果而设)。

利用点九图技术在xml中进行添加此贴图,并设置点九图属性参数corner:

<Button name="bt1"  width="240" height="150" bkimage="file='中.png' corner='10,10,10,10'"  />


如此设置,边框四个圆角仍为10像素,未变形,而其余部分则进行相应拉伸。


若如下代码,加入hole参数,表示中间部位透明。

<Button name="bt2"  width="240" height="150" bkimage="file='中.png' corner='10,10,10,10' hole='true' "  />




实际应用中,图框是没有示例中那样大,也没有“中”字图,中间多为单一色或渐变色。故源图像可裁切更小,比如3x3像素。








<think>根据用户需求,用户想在标准Duilib中嵌入MFC控件。结合引用资料,特别是引用[3]和引用[4]中提到的在duilib使用MFC控件的方法,以及引用[2]中提到的集成Duilib到MFC项目,我们可以将方法反过来应用:在Duilib中嵌入MFC控件。 关键: 1. 在Duilib窗口中创建MFC控件需要用到窗口句柄(HWND)。 2. 使用MFC控件时,需要确保MFC环境已经初始化(如使用AfxWinInit初始化)。 3. 在Duilib的窗口类中,可以通过重写CreateControl方法来创建MFC控件,或者使用现有的容器控件(如Container控件)来承载MFC控件。 步骤: 1. 确保项目已经正确集成了MFC和Duilib。 2. 在Duilib的窗口类中,获取窗口句柄(通常在创建窗口后)。 3. 使用MFC的控件创建函数(如Create)在指定的句柄上创建控件。 4. 处理消息映射,确保MFC控件的消息能够被正确处理。 注意:由于Duilib本身有自己的消息循环,需要将消息传递给MFC控件处理。 参考引用[3]中的示例,他们使用了win32按钮,并提到也可以使用MFC按钮(注释部分)。因此,我们可以类似地创建MFC控件。 具体步骤: 1. 在Duilib窗口的初始化函数中(如InitWindow),创建MFC控件。 2. 使用MFC控件类(如CButton)的Create方法,指定父窗口为Duilib窗口的句柄。 3. 调整MFC控件的位置和大小,可以通过Duilib的布局来控制,但需要将Duilib的坐标转换为屏幕坐标,或者使用相对位置。 4. 如果需要处理MFC控件的消息,可以在Duilib窗口类中添加消息处理函数(使用MFC的消息映射)。 但是,由于Duilib窗口不是MFC窗口,所以需要特殊处理消息。一种方法是在Duilib的消息循环中,将消息转发给MFC控件的父窗口(即Duilib窗口)的MFC消息泵。或者,我们可以使用一个MFC的容器窗口来承载这些控件,然后将这个容器嵌入到Duilib窗口中。 引用[4]中提到的博客文章(https://blog.youkuaiyun.com/byxdaz/article/details/72862013)提供了更详细的方法。 根据以上分析,我们可以给出以下步骤: 步骤1:在Duilib窗口中创建MFC控件 - 在Duilib窗口类中,重写`CreateControl`方法,或者在一个容器控件中创建MFC控件。 - 使用`GetHWND()`获取Duilib窗口的句柄。 步骤2:创建MFC控件 - 使用MFC控件类的Create函数,例如: CButton* pBtn = new CButton(); pBtn->Create(_T("按钮标题"), WS_CHILD|WS_VISIBLE, CRect(10,10,100,30), this, 1234); 注意:这里的this不能直接使用,因为Duilib窗口不是CWnd的派生类。因此,我们需要一个CWnd对象来作为父窗口。我们可以使用一个技巧:将Duilib窗口的句柄附加到一个CWnd对象上。 步骤3:使用CWnd::Attach将Duilib窗口句柄附加到一个CWnd对象(或者使用CWnd::FromHandle)来临时创建一个CWnd对象。 但是,更常见的方法是创建一个MFC的容器窗口(如CDialog)作为子窗口,然后将这个容器窗口嵌入到Duilib窗口中。然后在容器窗口中创建MFC控件。 然而,引用[3]中提到了可以在duilib中添加MFC控件,并且给出了一个示例。根据引用[3]的代码片段,他们直接在duilib的窗口上创建了MFC按钮。 因此,我们可以这样实现: 1. 在Duilib窗口类中,声明一个MFC控件的成员变量(如`CButton m_btn;`),注意要包含头文件。 2. 在InitWindow函数中创建按钮: m_btn.Create(_T("MFC按钮"), WS_CHILD|WS_VISIBLE|BS_PUSHBUTTON, CRect(0,0,100,30), CWnd::FromHandle(GetHWND()), 1234); 3. 调整位置:由于Duilib使用的是相对布局,我们需要在调整大小时重新设置MFC控件的位置。可以在OnSize函数中处理。 但是,这样创建的按钮可能无法接收消息,因为Duilib的消息循环没有将消息传递给MFC。所以我们需要在Duilib的消息循环中增加对MFC消息的处理。 4. 修改消息循环:在Duilib的消息循环中,使用`TranslateMessage`和`DispatchMessage`之前,先让MFC的消息泵处理。或者,我们可以使用`PreTranslateMessage`来处理。 实际上,我们可以使用MFC的`PeekMessage`和`PumpMessage`来处理。但是,由于我们的程序可能是基于Win32的,并没有启动MFC的消息泵,所以需要手动处理。 另一种方法是重载Duilib窗口的`MessageHandler`函数,将消息传递给MFC控件。但是,MFC控件需要其父窗口是一个MFC窗口(即从CWnd派生)。因此,我们可能需要一个中间容器。 考虑到复杂性,更推荐的方法是使用一个MFC的容器窗口(比如一个CDialog)来承载MFC控件,然后将这个容器窗口嵌入到Duilib窗口中。这样,这个容器窗口就可以处理所有MFC控件的消息。 具体步骤: 1. 创建一个CDialog派生类(例如CMFCContainer),并在上面放置需要的MFC控件。 2. 在Duilib窗口中,创建一个Container控件(或者一个自定义的容器控件),并获取其位置和大小。 3. 在Duilib窗口的InitWindow中,创建CMFCContainer实例,并创建为子窗口,父窗口为Duilib窗口的句柄。 m_container.Create(CMFCContainer::IDD, CWnd::FromHandle(GetHWND())); m_container.ShowWindow(SW_SHOW); 4. 调整容器窗口的位置和大小,使其与Duilib的容器控件位置一致(或者直接使用Container控件的区域)。 这样,容器窗口内的MFC控件就可以正常工作,因为容器窗口是MFC窗口,能够处理消息。 但是,用户要求的是在标准Duilib中嵌入MFC控件,而不是嵌入一个容器窗口再放控件。不过,从实际效果看,嵌入一个容器窗口是更可靠的方式。 参考引用[2]中提到的“控件通信机制”,我们可以实现MFC控件Duilib控件之间的通信。 因此,我们提供两种方法: 方法一:直接在Duilib窗口上创建MFC控件(适用于单个控件,需要处理消息转发)。 方法二:使用MFC容器窗口嵌入多个MFC控件(推荐,因为消息处理由MFC框架完成)。 由于方法一较为复杂,且需要处理消息映射,这里我们以方法二为例。 详细步骤(方法二): 1. 创建一个MFC对话框资源(IDD_DIALOG1),并创建一个对应的类CMFCContainer。 2. 在Duilib窗口类中添加成员变量:`CMFCContainer m_container;` 3. 在Duilib窗口的InitWindow函数中: // 获取Duilib中用于放置MFC容器的控件(比如一个Container控件)的位置 CControlUI* pContainer = m_PaintManager.FindControl(_T("mfc_container")); if (pContainer) { RECT rc = pContainer->GetPos(); // 创建MFC容器窗口 m_container.Create(IDD_DIALOG1, CWnd::FromHandle(m_hWnd)); // 调整MFC容器窗口的位置和大小 ::SetWindowPos(m_container.m_hWnd, NULL, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, SWP_NOZORDER | SWP_SHOWWINDOW); } 4. 在MFC容器对话框(CMFCContainer)中,可以添加各种MFC控件。 5. 消息处理:在MFC容器对话框中,可以像普通MFC程序一样添加消息处理。 6. 通信: - Duilib调用MFC控件:可以通过获取容器对话框中的控件对象,然后调用其方法。 - MFC控件事件通知Duilib:可以在容器对话框中发送自定义消息到Duilib窗口,或者通过回调函数。 7. 注意:在Duilib窗口关闭时,需要销毁MFC容器窗口。 另外,引用[1]和[2]中提到的资源文件详细说明了集成步骤,因此用户可以参考这些资源。 由于用户要求的是在标准Duilib中嵌入MFC控件,我们提供了上述方法。下面将用代码示例说明。 注意:以下代码示例需要项目已经正确设置MFC和Duilib。 示例代码: 在Duilib窗口类中(假设为CMainWnd): ```cpp #include "MFCContainer.h" // CMFCContainer类的头文件 class CMainWnd : public CWindowWnd, public INotifyUI, ... { ... private: CMFCContainer m_container; // MFC容器 ... }; // 在InitWindow函数中创建容器 void CMainWnd::InitWindow() { // 假设在DuilibXML布局中有一个名为"mfc_container"的容器控件 CContainerUI* pContainerUI = static_cast<CContainerUI*>(m_PaintManager.FindControl(_T("mfc_container"))); if (pContainerUI) { RECT rc = pContainerUI->GetPos(); // 创建MFC容器窗口,父窗口为当前Duilib窗口 if (m_container.Create(IDD_DIALOG1, CWnd::FromHandle(m_hWnd))) { // 调整位置和大小 ::SetWindowPos(m_container.m_hWnd, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_SHOWWINDOW); } } } // 在关闭窗口时销毁 void CMainWnd::OnDestroy() { if (::IsWindow(m_container.m_hWnd)) m_container.DestroyWindow(); ... } ``` 在MFC容器对话框(CMFCContainer)中,我们可以设计对话框资源,并添加按钮控件,然后为按钮添加事件处理。 这样,MFC控件就成功嵌入到Duilib窗口中了。 相关问题: 1. 如何实现Duilib控件与嵌入的MFC控件之间的通信? 2. 在嵌入MFC控件时,如何处理焦切换(Duilib和MFC之间的焦问题)? 3. 嵌入MFC控件后,如何调整MFC控件的大小随Duilib窗口变化? 注意:以上方法在MFC和Duilib混合编程中需要谨慎处理资源释放和消息循环。</think>### 在标准Duilib中嵌入MFC控件的实现方法 在Duilib中集成MFC控件需要解决窗口句柄传递、消息路由和坐标转换三大核心问题。以下是具体实现步骤(参考引用[1][2][3]): #### 一、核心步骤 1. **环境初始化** ```cpp // 在应用入口初始化MFC环境 BOOL bInit = AfxWinInit(GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0); ``` 2. **创建MFC控件容器** ```cpp // 在Duilib窗口类中创建MFC容器 class CDuiContainer : public CWnd { public: CDuiContainer(HWND hParent) { Create(NULL, NULL, WS_CHILD|WS_VISIBLE, CRect(0,0,0,0), CWnd::FromHandle(hParent), 1234); // 1234为控件ID } }; ``` 3. **控件集成(关键代码)** ```cpp // 在Duilib窗口的InitWindow()中 void CMainDlg::InitWindow() { // 获取Duilib容器控件 CControlUI* pContainer = m_PaintManager.FindControl(_T("mfc_ctrl_holder")); // 创建MFC控件容器 m_pMFCContainer = new CDuiContainer(GetHWND()); // 创建具体MFC控件(如按钮) m_pMfcButton = new CButton(); m_pMfcButton->Create(_T("MFC按钮"), WS_CHILD|WS_VISIBLE, CRect(10,10,100,30), m_pMFCContainer, 1001); } ``` 4. **坐标同步(重载Duilib方法)** ```cpp void CMainDlg::DoPaint(HDC hDC, const RECT& rcPaint) { __super::DoPaint(hDC, rcPaint); // 同步MFC控件位置 CControlUI* pHolder = m_PaintManager.FindControl(_T("mfc_ctrl_holder")); if(pHolder) { RECT rc = pHolder->GetPos(); ::SetWindowPos(m_pMFCContainer->m_hWnd, NULL, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, SWP_NOZORDER); } } ``` 5. **消息路由处理** ```cpp LRESULT CMainDlg::HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam) { if(uMsg == WM_COMMAND) { // 处理MFC控件消息 if(LOWORD(wParam) == 1001) { // 按钮击处理 } } return __super::HandleMessage(uMsg, wParam, lParam); } ``` #### 二、关键技术要 1. **窗口层级关系** ``` Duilib主窗口 (HWND) └── MFC容器窗口 (CDuiContainer) └── MFC控件 (如CButton) ``` 2. **焦管理** - 重载`KillFocus`事件传递焦 ```cpp void CMainDlg::KillFocus() { if(m_pMFCContainer) ::SetFocus(m_pMFCContainer->m_hWnd); } ``` 3. **内存管理** ```cpp virtual void OnDestroy() { if(m_pMfcButton) delete m_pMfcButton; if(m_pMFCContainer) delete m_pMFCContainer; } ``` #### 三、XML布局配置 ```xml <HorizontalLayout name="mfc_ctrl_holder" width="200" height="50"> <!-- 此处区域将显示MFC控件 --> </HorizontalLayout> ``` > **注意事项**: > 1. 需在项目设置中启用MFC支持(属性页→常规→使用MFC) > 2. 混合使用时需处理`WM_ERASEBKGND`消息避免闪烁 > 3. 高DPI场景需额外处理坐标缩放(参考`GetDpiForWindow`)[^3][^4]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值