一般来说,对于mStatic
,mImage
这样的控件,只要设置了透明属性(transparent=true
),就可以实现背景透明,但对于mButton
按钮却不行,即使设置了透明属性,也不能实现透明背景。
miniStudio中对按钮设置透明属性示例:
这是为什么呢?通过跟踪minigui/mgncs
的代码发现了原因:
以下是libmgncs-1.2.0/src/renderer/flat/flat_boxpieces.c
中,按钮类(mButtonBoxPiece
)渲染器的paint
处理函数(flat
风格)的代码:
// button box
static void flat_buttonbox_paint(mButtonBoxPiece *self, HDC hdc, mWidget * owner, DWORD add_data)
{
RECT rc;
if(!_c(self)->getRect(self, &rc)) return ; flat_draw_3dbox(hdc, &rc, NCSRF_FILL, add_data&NCS_PIECE_PAINT_STATE_MASK, NCS_PIECE_PAINT_GET_CHECK(add_data), owner); }
从这个代码可以看出,每次重绘窗口区域时,不论是否有设置透明属性,都会先调用flat_draw_3dbox
函数填充背景。
其他风格的渲染器处理mButtonBoxPiece
的逻辑也是一样的(classic
,fashion
,skin
,flat
)
知道了原因,解决问题的方案就有了
修改libmgncs
源码:
还以上面的flat_buttonbox_paint
函数为例,在函数开始添加一行代码判断透明属性是否设置就可以了。
////////////////////////////////////////////
// button box
static void flat_buttonbox_paint(mButtonBoxPiece *self, HDC hdc, mWidget * owner, DWORD add_data)
{
/* 如果有透明直接返回 */
if(GetWindowExStyle(owner->hwnd) & WS_EX_TRANSPARENT)return;
RECT rc;
if(!_c(self)->getRect(self, &rc))
return ;
flat_draw_3dbox(hdc, &rc, NCSRF_FILL, add_data&NCS_PIECE_PAINT_STATE_MASK,
NCS_PIECE_PAINT_GET_CHECK(add_data), owner);
}
上面这样修改带来的效果就是按钮边框也没有了。如果你要背景透明但还希望画上按钮边框,修改如下:
static void flat_buttonbox_paint(mButtonBoxPiece *self, HDC hdc, mWidget * owner, DWORD add_data)
{
// 根据透明背景属性来决定flat_draw_3dbox函数的flag参数
int flag = GetWindowExStyle(owner->hwnd) & WS_EX_TRANSPARENT ? 0 : NCSRF_FILL;
RECT rc;
if(!_c(self)->getRect(self, &rc))
return ;
flat_draw_3dbox(hdc, &rc, flag, add_data&NCS_PIECE_PAINT_STATE_MASK,
NCS_PIECE_PAINT_GET_CHECK(add_data), owner);
}
如果你不想修改源码,可以在外部自己为mButtonBoxPiece
写个渲染器函数,然后调用ncsRegisterCtrlRDRs
替换掉flat_buttonbox_paint
函数就可以了:
#include
#include
#include
#include
#include
#include
// 自定义的mButtonBoxPiece 渲染器函数,do nothing
static void transparent_buttonbox_paint(mButtonBoxPiece *self, HDC hdc, mWidget * owner, DWORD add_data)
{
}
//init boxpiece
void transparent_init_boxpiece_renderer(void)
{
NCS_RDR_ENTRY entries [] = {
{Class(mButtonBoxPiece).typeName, (mWidgetRenderer*)(void*)transparent_buttonbox_paint},
};
// 注册控件的渲染器函数,修改flat风格的button渲染器函数,
// 可以用这个函数同时替换其他风格(`classic`,`fashion`,`skin`,`flat`)的对应函数
ncsRegisterCtrlRDRs("flat",
entries,
sizeof(entries)/sizeof(NCS_RDR_ENTRY));
ncsRegisterCtrlRDRs("classic",
entries,
sizeof(entries)/sizeof(NCS_RDR_ENTRY));
ncsRegisterCtrlRDRs("fashion",
entries,
sizeof(entries)/sizeof(NCS_RDR_ENTRY));
ncsRegisterCtrlRDRs("skin",
entries,
sizeof(entries)/sizeof(NCS_RDR_ENTRY));
}
注意要在应用程序启动前调用transparent_init_boxpiece_renderer
函数完成transparent_buttonbox_paint
的注册。
同样,如果你希望背景透明的同时保留按钮边框。上面transparent_buttonbox_paint
要这样写:
// 声明外部调用函数
void flat_draw_3dbox(HDC hdc, const RECT *rc, int flag ,int state, int check_state, mWidget *owner);
static void transparent_buttonbox_paint(mButtonBoxPiece *self, HDC hdc, mWidget * owner, DWORD add_data)
{
int flag = GetWindowExStyle(owner->hwnd) & WS_EX_TRANSPARENT ? 0 : NCSRF_FILL;
RECT rc;
if(!_c(self)->getRect(self, &rc))
return ;
flat_draw_3dbox(hdc, &rc, flag, add_data&NCS_PIECE_PAINT_STATE_MASK,
NCS_PIECE_PAINT_GET_CHECK(add_data), owner);
}
注意:修改渲染器(renderer)对所有同类型的控件都有效