{A}简介
本文介绍了一套主题的所有者绘制放大器;第二套为主题的全定制按钮。在Vista中,他们提供视觉类似标准按钮的效果 - 褪色转换泛着微妙的默认状态的影响。
我为我自己的应用程序精制这一段时间,最近升级为Vista,并想出其他可能受益。 "按钮类Windows 98中,2000年,XP,和Vista兼容。他们正在实施的C / MFC控件。标准按钮,下拉按钮,和图像(位图或图标)按钮。
{S0}
背景
为主题的控件的Windows的支持是相当不错的。配套体系创建自定义控件口味很容易。 Vista中更进了一步动画一定的控制 - 尤其是按钮。 Vista的按钮国家之间的顺利过渡和微妙的发光默认状态按钮,以赶上眼。很酷的东西!
{S2}不发光的{S3}发光
不幸的是,主题API的不显然允许自定义控制,以配合这种新的行为。 "NM_CUSTOMDRAW技术的自定义绘制(见斯蒂芬钢的文章)不支持的发光效果,虽然其他过渡工作。然而,在Vista NM_CUSTOMDRAW技术需要简单的主题位图/图标按钮。他们都支持。自定义绘制
如果你确实需要雇主或自定义绘制然而,阅读。包括按钮类提供了一个跨平台的一致的绘制框架,并复制Vista的发光效果。例子是一个普通的推,菜单,和图像文本按钮。
只是要清楚,这些视觉效果似乎只在Vista上。在98/2k/XP像其他任何这些按钮。有没有业主的支持对于复选框或单选按钮(他们没有动画,所以没有点)。
因为我需要这些向后兼容与Win98的,我用VC6的(horrors!)。幸运的是,下运行的VirtualPC Win98的或VMWare使得这种支持微不足道。它还编译VS7/2003或VS8/2005罚款 - 只需打开社会福利署署长项目文件。 VS8用户必须删除事先从RT_MANIFEST资源类条目。
大卫赵的视觉风格类是用来避免DLL的问题Win98/2k。你还需要一个与XP平台SDK主题头文件。Alpha混合
AlphaBlend API是用来合并呈现按钮状态的位图。什么是阿尔法混合? "阿尔法"参考指两个图像合并的程度。以下方程可能有助于说明:
输出=(old_pixel *(255 -α)new_pixel *α)/ 255
零阿尔法意味着输出等于旧的像素。一个阿尔法255表示输出等于新的像素。其他值产生一个混合两者的组合。展开到RGB的概念颜色,添加图像缩放,和你有AlphaBlend API。Vista的按钮变
的主题API支持5个按钮的状态:disabled,正常,热,拖欠和推动。 Vista中执行转换这些国家以不同的速度。一些快速 - 就像当按下按钮。别人慢 - 像褪色热或停用状态。
也存在着差异取决于鼠标有多久上空盘旋按钮。生活就是这样。我不主张完美重现Vista中,疣和所有,但这些类非常接近。现在的代码... 首先,我们确定一个新的按钮状态优先检查:int old_stateid = m_stateid;
if (!button_enabled) m_stateid = PBS_DISABLED;
else if (button_pressed) m_stateid = PBS_PRESSED;
else if (button_hot) m_stateid = PBS_HOT;
else if (button_default) m_stateid = PBS_DEFAULTED;
else m_stateid = PBS_NORMAL;
如果状态变化,那么我们设置的过渡:{C}
tickcount变量拥有50ms的定时器必需的。 tickscale变量后的tickcount成倍增加,提供0到250的范围内"阿尔法"合并位图。我们也记录了老的快照按钮国家在这里。 CDCBitmap是一个辅助类借鉴成位图。
接下来,呈现新的按钮状态的背景是:// Draw themed button background...
if (g_xpStyle.IsThemeBackgroundPartiallyTransparent(hTheme, BP_PUSHBUTTON,
m_stateid))
{
g_xpStyle.DrawThemeParentBackground(m_hWnd, mDC.GetSafeHdc(), &rc);
}
g_xpStyle.DrawThemeBackground (
hTheme, mDC.GetSafeHdc(), BP_PUSHBUTTON,
m_stateid, &rc, NULL);
// Get content rectangle...
CRect border(rc);
g_xpStyle.GetThemeBackgroundContentRect (
hTheme, mDC.GetSafeHdc(), BP_PUSHBUTTON, m_stateid,
&border, &border);
标准的东西。没有什么新东西。
最后,AlphaBlend与新老:if (UseVistaEffects() && (m_transition_tickcount>0))
{
CDCBitmap tempDC(dc,m_oldstate_bitmap);
BLENDFUNCTION bf;
bf.BlendOp = AC_SRC_OVER;
bf.BlendFlags = 0;
bf.SourceConstantAlpha = m_transition_tickcount*m_transition_tickscale;
bf.AlphaFormat = 0; // AC_SRC_ALPHA;
AlphaBlend(mDC.GetSafeHdc(), rc.left, rc.top, rc.Width(), rc.Height(),
tempDC.GetSafeHdc(), rc.left, rc.top, rc.Width(), rc.Height(), bf);
}
田田!大多是其刚刚成立BLENDFUNCTION结构AlphaBlend。最初的Alpha附近100%,使老态主导视觉。作为定时器递减tickcount放大器;势力窗口刷新,阿尔法降低到0%,所以新的国家普遍存在。简单EH?
你可能会问我为什么不使用NM_CUSTOMDRAW。这是基于计时器的更新不兼容,因为Windows已经提供状态转换。方法的斗争 - 以喜悦脱俗。
注意:上面的代码只涉及按钮背景。中心内容是制定之后(文字,菜单箭头,位图等)Vista默认/聚焦按钮
发光/脉冲的默认按钮是最巧妙的效果。不幸的主题API不知道这一点。卫生署。
但是,它显然只是一个默认放大器相结合;热状态。
我们将重现效果如下: 默认状态的图像复制到一个临时位图。
按钮的边框加厚的温度从50%AlphaBlend位图。 临时位图渲染的热按钮状态。 AlphaBlend从临时位图的内容区域。
AlphaBlend步骤是α-比例超过两秒钟的时间间隔提供了脉冲效果。if (UseVistaEffects() && (m_transition_tickcount==0) &&
(m_stateid == PBS_DEFAULTED))
{
// Copy "default" button state...
CDCBitmap tempDC(dc,rc);
AlphaBlt (tempDC, rc, mDC, rc, 255); // lazy bitblt srccopy
// Compute "glow" alpha... 0->250->0 over 40 ticks.
int alpha = (int)(m_defaultbutton_tickcount*12.5);
if (m_defaultbutton_tickcount>=20) alpha = 500-alpha;
// Thicken content border...
CRect rect(border);
rect.InflateRect(1,1);
AlphaBlt (mDC, border, tempDC, rect, alpha/2);
// Render hot button state...
g_xpStyle.DrawThemeParentBackground(m_hWnd, tempDC.GetSafeHdc(), &rc);
g_xpStyle.DrawThemeBackground (
hTheme, tempDC.GetSafeHdc(), BP_PUSHBUTTON, PBS_HOT, &rc, NULL);
// Blend the hot-state content area (avoiding thick border)...
border.DeflateRect(1,1);
AlphaBlt (mDC, border, tempDC, border, alpha);
border.InflateRect(1,1);
}
这里的AlphaBlend函数被移动到一个独立的功能AlphaBlt清洁的东西。定时器周期tickcount从0到40不休,从我们计算的alpha。蜱成为ALPHA 0 0至19 23720日至40蜱成为阿尔法250至0。使用代码
评价Vista的效果,更换或继承任何按钮CButton的实例所有者绘制CButtonVE。添加源文件到您的项目,你是在业务。提供以下类: CButtonVE / CButtonVE2 - 业主绘制
放大器;全定制按钮
。 CButtonVE_Menu / CButtonVE2_Menu - 所有者绘制放大器;全自定义菜单按钮。 CButtonVE_Image / CButtonVE2_Image - 老板绘制放大器;全定制图像按钮(位图或图标)。
所有提供以下内容控制功能: SetOwner - 指定窗口接收按钮点击(AMP;菜单按钮命令)。默认父。 SetContentHorz - 指定水平对齐按钮图像/文本内容(ModifyStyle也可以使用)。 &
#160; SetContentVert - 指定垂直对齐按钮图像/文本的内容(也可用于ModifyStyle)。 SetContentMargin - 指定间距之间的按钮边境放大器;内容。 SetBackgroundColor - 强制按钮的背景颜色(默认投票WM_CTLCOLOR父)。
菜单按钮类添加以下的设置/通知功能: SetMenu - 预加载菜单资源ID或CMenu(以下功能显示前可以更改)。 AddMenuItem - 手动添加一个菜单项(可追加加载菜单资源)。 RemoveMenuItem - 删除一个菜单项(可以删除从加载菜单资源的项目)。 RemoveAllMenuItems - 删除所有菜单项。 NotifyMenuPopup - 菜单前出现动态更新(默认调查UI更新的所有者)。
这些图像按钮类提供: SetImagePosition - 指定图像的位置相文(左,右,上面或下面文本)。 SetImageSpacing - 指定图像和文字之间的间距。 SetImageShadow - 控制,如果高斯模糊阴影下显示图像。 SetTransparentColor - 指定位图背景色彩。默认为左上角像素。 SetHotImage - 指定位图或图标,以显示按钮时,热(盘旋在它的鼠标)。 SetDisabledImage - 指定位图或图标时显示该按钮被禁用。默认情况下,图像按钮创建一个源图像中的阴影的版本。 添加所有者描述的按钮
在对话框编辑器中添加一个标准的按钮,设置"所有者绘制"风格。添加一个控制类型的成员变量(子类),并取代"CButton的"您选择按钮类的头实例。
{S4}的添加完整的自定义按钮
在对话框编辑器中添加自定义控件,并指定所需的"类"在属性的名称。即:{五}
在你的WM_INITDIALOG处理程序,你需要配置字体放大器;全定制控制窗口中的文本(见演示代码)。自定义按钮内容
要自定义按钮绘制,派生一个新类放大器取代"DrawContent"。 CButtonVE框架处理背景放大器;过渡影响。
有几个参数提供有用的信息,包括绘图三种不同的矩形的坐标:按钮的轮廓,安全内容的边界,并建议文本矩形。如果hTheme是有效的,使用的主题的API尽可能建议。 uistate面具控制隐藏重点放大器;加速器标志着。virtual void DrawContent (
CDC &dc, // Drawing context
HTHEME hTheme, // Vista theme (if available).
// Use g_xpStyle global var for drawing.
int uistate, // Windows keyboard/mouse ui styles.
// If UISF_HIDEACCEL set, hide underscores.
CRect rclient, // Button outline rectangle.
CRect border, // Safe content rectangle.
CRect textrc, // Text rectangle.
int text_format, // DrawText API formatting.
BOOL enabled) // Set if button enabled.
图像按钮(例如)只是覆盖这一功能绘图"textrc"坐标内。兴趣点
每个按钮的两个版本。所有者绘制(VE)和一个完全自定义(VE2)实施按钮。
为什么你问?所有者绘制按钮都是伟大的!他们在简化生活方式(尽管后面提到的头痛)。然而,有一种奇异的显示瓶塞。为了画本身,子窗口取决于父合作放大器;反射回消息。因此,"主人"的一部分"自绘"。这也适用于NM_CUSTOMDRAW。
有些父窗口不反映,如通知消息,CFileDialog的(GetOpenFileName /则GetSaveFileName)。对于这些不合作父母,你不能使用所有者绘制或NM_CUSTOMDRAW的方法。衍生CButton和更换WM_PAINT处理函数不工作,因为视窗重绘按钮控制按钮以外的WM_PAINT消息点击。因此,全定制的推来自按钮CWnd的选项。
雇主抽奖头痛
所有者绘制按钮的一个刺激性的问题是值得一提。在对话框中,Windows的跟踪"默认"按钮 - 一个"点击"按Enter键。 "默认按钮绘制一个沉重的边境。
Windows的跟踪查询窗口的"默认"状态WM_GETDLGCODE。应该返回按钮DLGC_DEFPUSHBUTTON(如果默认)或DLGC_UNDEFPUSHBUTTON(如果不是)。如果不这样做意味着你不能成为"默认"作为Windows关注。行,够简单。
现在,刺激性的问题。返回值WM_GETDLGCODE有一个副作用 - 它们使Windows删除BS_OWNERDRAW按钮的风格!咦?视窗发送一个BS_SETSTYLE消息设置BS_DEFPUSHBUTTON风格默认按钮,和BS_DEFPUSHBUTTON是相互排斥的所有者绘制风格。卫生署。幸运的是,其可以覆盖BS_SETSTYLE和恢复所有者绘制。这样做虽然意味着失去默认状态。尼斯。
我的解决方法涉及到跟踪本地的默认状态。当Windows指定BS_DEFPUSHBUTTON一个BS_SETSTYLE处理程序的记录,然后力量所有者绘制。记录的状态是用于绘制并返回正确的值在WM_GETDLGCODE。工程同时键盘导航放大器鼠标点击;太棒了!随后的研究发现,保罗墨西拿文章解决了同样的问题。自定义文件对话框演示
CFileDialogVE演示类("正常开放推"按钮)使用自定义图像按钮:
{中六}
的OnInitDialog处理程序创建CButtonVE2_Image实例,并调整对话框大小,以便其可见:// Create & setup full-custom image button...
CButtonVE2_Image *btn = new CButtonVE2_Image(); // note: self deletes
btn->Create(_T("Custom Drawn Bitmap\n with Glow"),
WS_VISIBLE|WS_CHILD|WS_TABSTOP|BS_MULTILINE,
rve, CWnd::FromHandle(ofn_hWnd), CUSTOM_IMAGEBTN_ID);
HBITMAP hBitmap = (HBITMAP)(LoadImage(theApp.m_hInstance,
MAKEINTRESOURCE(IDB_BITMAP1), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR));
btn->SetBitmap(hBitmap);
// Update dialog size...
CRect rw;
::GetWindowRect(ofn_hWnd,&rw);
int adjust = rve.bottom-rcombo.bottom;
// tweak height if no places bar visible...
if (m_ofn.lStructSize == OPENFILENAME400SIZE) adjust -= rcombo.Height();
::SetWindowPos(ofn_hWnd, NULL, 0, 0, rw.Width(), rw.Height()+adjust,
SWP_NOMOVE|SWP_NOZORDER);
CRECT RVE持有按钮坐标,rcombo类型组合框的坐标,并ofn_hWnd文件对话框处理。如果对话框的大小调整取决于位置栏是可见的(结构的大小检测)。
Vista的当然,已提供有限的定制支持,在其新的文件对话框(见迈克尔邓恩的Vista文件对话框文章)
其他物品
的代码,包括各种有用的例程。支持Vista的9pt濑越UI字体,和8PT的MS Sans Serif为98/2k/XP处理由CFontOccManager(见ButtonVE_demo.cpp)。有人发布在MSDN论坛上,但其太好憔悴。如果运行Vista,它会查询SystemParametersInfo和初始化LOGFONT,然后计算为MFC CDialogTemplate正确缩放。
图像按钮在运行时计算禁用放大器;正常图像支持透明背景色。对于那些谁享受的BitBlt,看到DrawDisabledImageDrawTransparentImage,和DrawBluredShadowImage(中ButtonVE_Helper.h)。创建单色位图掩盖关闭背景,然后绘制图像输出上下文。
享受!版权和许可
这篇文章是版权??2007年伊恩发送戴维斯。演示代码和所附源代码本文现释放到公共领域。历史 2007年4月24日 - 首次发行。我的第一个代码项目文章! 2007年4月27日 - 破旧的问题,发现汉斯迪特里希(WINXP主题全定制按钮的默认状态),和杰里埃文斯(uistate不妥善处理)。还增加了更好地在全定制按钮输入处理。 5月4日,2007 - 新增支持BS_PUSHLIKE风格切换按钮(正常GetCheck查询),高斯模糊图像的阴影,和热像悬停的支持。还增加了背景颜色WM_CTLCOLOR投票。固定的罕见和难以重现的视觉闪烁动画的默认按钮。| IED