样式工具包

来源:互联网 发布:新浪域名国外的 编辑:程序博客网 时间:2024/05/22 11:33

翻译来源:https://www.codeproject.com/Articles/27819/Style-Toolkit-Use-advanced-graphics-techniques-to

简单点可以先从作者这篇看起:StyleDialog - 具有可选自定义框架的透明对话框类

样式工具包允许您使用渐变,透明度,PNG图像等更新程序的外观。
  • 下载演示源 - 491.04 KB
  • 下载演示 - 239.33 KB
  • 下载工具包源码 - 44.65 KB

P1_DemoProgram.png

介绍

如果上面的一些风格看起来很熟悉,他们应该。其中许多来自CodeProject网站上的其他文章。风格工具包不使用其他文章中的代码,只能定义颜色,效果,边框大小等。

Style Toolkit不会预先定义样式,C ++不会预先定义类; 因此,它可以创建的样式数量是无穷大的。当然,一旦创建了一个风格,类似于创建一个类,你可以重用它,修改它并分享它。

所有的样式组件都是用层创建的,好像你正在使用像Photoshop这样的程序; 然而,与Photoshop不同,所有图层都是以编程方式创建的。无论是背景,指示器控制还是水色按钮都无关紧要; 它们都是通过定义一系列图形操作来创建的。

背景

风格工具包使用GDI +; 然而,除了所需的初始化以及需要正确编译和链接的事实外,它对用户是透明的。本文将不介绍如何使用和并入GDI +; 如果您不熟悉,请阅读我的GdipButton [ ^ ]文章和其他资源。

使用预览

P2_TealPane.png

为了演示Style Toolkit的强大功能和简单性,我们来看看演示程序的Teal Pane。我删除了控件,因为它们有自己的样式对象。这种风格的动机来自Vista Info Bar [ ^ ]文章。

上面的风格可以用九行代码创建:

void CStyleDemoDlg::CreateTealPane(){    // create the stack    Stack TealPane(rect);    // set the OuterBorder params    TealPane.SetOuterBorder(2, Black);    // set the Inner Border params    TealPane.SetInnerBorder(2, RGB(135, 192, 185), RGB(109, 165, 168));    // 3 color gradient    TealPane.FillGrad3(HORIZ, RGB(9, 74, 116), RGB(32, 121, 140), RGB(5, 111, 90));     // add a top edge effect    TealPane.FillBar(rect, TOP_EDGE, 20, Clr(180,RGB(135, 192, 185)), Clr(0,White));    // add the shield image    TealPane.AddImage(CPoint(crnr.left, crnr.top),                       IDR_SHIELD, _T("PNG"), 255);    // create a display string    CString str1(_T("Progress Styles\t\t\t    Indicator Styles"));    // add the string     TealPane.AddString(str1, CPoint pt1(158, 35), RGB(148, 215, 255),                        14, FONT_REG, L"Swiss")    // add to style    m_Style.AddStack(TealPane);}

如果将其与Vista Info Bar源代码进行比较,可以看到它的紧凑程度。此外,Style Toolkit不使用任何外部库,它只需要gdiplus.dll

上面的代码实际上没有做任何图形操作,它只定义它们。随后的调用PaintStyle()将绘制图形对象。

样式,堆叠和图层

Style Toolkit使用的术语的定义如下:

  • 层 - 在概念上,Style Toolkit中的图层与Photoshop中的程序类似。然而,技术上,层是定义图形操作的结构。一个图层可以定义类似FillSolid,但它也可以定义喜欢的东西AddImageCreateRgn
  • 堆栈 - 堆栈是具有底部和顶部感觉的层的集合。一个堆栈也有一个由一个定义其剪切区域的矩形界定的框架。堆栈有三个边框,默认宽度为零,始终是最高的。Stack类是从派生Layers类,实际上只是一个包含所有用户的API函数的分区。
  • 风格 - 风格是堆叠的集合,也具有底部和顶部的感觉。堆叠可能重叠或不相交。Style类包含的主要用户油漆功能,PaintStyle

使用样式工具包

创建一个风格只是一个声明一个Stack有界矩形,添加一些图形操作,然后将其添加到一个Style对象进行绘画的问题。最简单的样式如下所示。

BOOL CStyleDemoDlg::OnEraseBkgnd(CDC* pDC){    CDialog::OnEraseBkgnd(pDevC);    CRect rect;    GetClientRect(rect);    Stack stack(rect);    Style style;    style.AddStack(stack);    stack.FillSolid(Green);    style.PaintStyle(pDC, rect);    return TRUE;}

实际上,您不会在擦除功能中声明堆栈和样式; 你可以在PaintStyle那里调用函数。

Stack功能添加图形操作到的作用就像一个FIFO的阵列。这些操作将按照它们被声明的顺序执行,所以始终从底层开始,朝着顶端方向工作。

背景和边界

P3_BorderPane.png

背景

我们已经看到如何创建一个背景,让我们再来看几个。左侧的背景是JPEG图像。您可以使用GDI +支持的任何格式的图像,也可以部分透明。右侧是一个SolidFill,您还可以使用两个或三个水平,垂直或对角色彩渐变。

国界

Stack有三个边框总是在顶端,无论你在哪里申报。它们可以有任何宽度,但默认情况下它们具有零宽度,因此除非指定,否则不使用它们。边框可以是RECTANGLEELLIPSEROUNDRECT,或TRANSITION

上图的左侧是过渡边框的示例。外边缘为矩形,内缘为圆弧形。预定义的边框只是为了方便,它们很容易声明,因为它们的相对位置是固定的,所以你只需要声明宽度。

您还可以创建边框,而不使用预定义的边框,因为图层也可以是上述任何形状。上图中右侧的边框是用层创建的,通过在中心声明剪切区域,并使用边缘的渐变填充。

风格控制

Style Toolkit的初始版本支持按钮,编辑和进度控件。更多的时间将被添加。

StyleProgress

StyleProgress控件至少包含两个堆栈:底部堆栈不改变,顶部堆栈在控制步骤时发生更改。底层堆叠可以只是一个清晰的层,但是由于连续性原因需要存在。

StyleProgress可以是一个进步的类型,或指示器的类型。不同的是,指示符类型表示多少东西(如温度计),预计不会像进度控制一样走到尽头。控件实际上并不知道它的类型,这一切取决于堆栈的定义。

P4_Progress.png

默认风格是Vista左边的一个。如果您不向控件添加任何堆栈,它将创建该样式。默认风格的定义来自Xasthom的VistaProgressBar [ ^ ]文章。您可以通过调用SetBackColor更改默认控件的颜色SetForeColor

第二个进度条仅使用底部的渐变层和顶部的椭圆梯度层。这种风格的动机来自Kochise的CSkinProgress [ ^ ]文章。他的控制实际上使用了一个PNG图像,并且比我的还有几百个功能。

顶部指示条不会着色层。它不能因为它永远是绿色到红色,不管位置。它实际上是在定义剪辑区域的彩色层顶部上的一个透明层。

底部的指示器是由Hans Dietrich的XGradientZoneBar [ ^ ]文章推动的,但是我添加了一个额外的包装梯度来给出它的驼峰效果。

箭头是StyleButton可以用来踩杆的。

StyleButton

StyleButton是我的GdipButton [ ^ ] 的扩展版本它具有所有的功能GdipButton(如按状态,热状态等),但它也具有使用Style对象而不是图像的功能。StyleButton可以使用不同的风格为它的每个状态。它们都含有类似的功能LoadStdStyle(style1)LoadHotStyle(style2)通常,您只需要一个堆栈来创建其中一个状态,但是这些函数采用样式参数,因此可以调用paint函数。

至少,您必须致电LoadStdStyle()LoadStdImage()未由加载功能设置的状态将自动从标准状态导出。

P5_VistaStyle1.png

VistaStyle1窗格显示按钮可以具有的大部分不同状态。这个按钮的定义大致来自Jose ManuelMenéndezPoó的文章WindowsVistaRenderer [ ^ ]。只有顶部的两个按钮(普通按钮和检查类型按钮)被完全实现,其他4个被强制为备用状态以进行说明。

实现按钮的样式

由于其复杂性,我为演示程序选择了这种按钮样式。创建这些按钮的方法是将它们分离成其独特的组件或工具包术语中的堆栈。例如,大多数状态共享相同的基础,或共享相同的光线等。创建样式状态时,我们只是将正确的部分组合在一起。Stack类重载运营商“=”,“+”和“+ =”中,将这些在更详细的API部分中描述。

在定义了创建每个堆栈所需的不同操作(这里未显示)之后,我们只是组合每个按钮状态所需的不同的部分。

// create standard groupVBStd = VB1Base;// create the hot groupVBHot = VB1Base2 + VB1Hover + VB1Glow;// create the pressed groupVBPress = VB1Base2 + VB1Pressed + VB1Glow;// create the alt groupVBAlt = VB1Base2 + VB1Checked + VB1Hover + VB1CheckedGlow;

VB1Base并且VB1Base2因为边界不同而不同。另请注意,在这个实现中,辉光不会流过边界。对我来说,如果有一些东西在一块玻璃上发光,那它也不能在它的前面。流血风格可以通过不同的方式来实现。

在创建不同的堆栈后,我们只需将它们添加到样式中,然后将其加载到StyleButton

更多按钮样式

P6_MoreButtons.png

按钮可以是矩形,椭圆形或圆形,如下所示。

Aquaizer Strikes Back [ ^ ]文章中提供了Aqua style按钮的定义

Vista Style 2按钮的定义来源于C# [ ^ ]文章中的 Xasthom的Vista Style Button

箭头按钮不使用样式; 他们使用PNG文件作为标准图像,并允许StyleButton该类生成其他状态。

StyleEdit

StyleEdit类源自CEdit,并具有添加的样式,能力。除了可能已经设置的水平居中点之外,它还可以垂直居中。演示程序显示了几个具有边框的渐变,我保持简单,所以文本是可读的。这个类需要更多的调整和功能,但是我耗尽了时间,这对于初始版本来说是足够好的。

样式工具包API

Style Toolkit API在Style.h文件中定义

所有API函数都将使用GDI或GDI +类型参数。例如,你可以传递COLORREFCRect以及CPoint与特定的功能,但你也可以传递ColorRectPoint相同的功能。究其原因是因为实际API论据是ClrSRectSPoint,这是转换类。在内部,所有内容都以GDI +格式存储。

重载运算符

Stack类重载运营商“=”,“+”和“+ =”。

stack1 = stack2;

=运算符是复制构造函数,并按照您期望的方式运行。堆栈中的层是STL向量,除了执行几个内务任务之外,复制构造函数还将复制所有向量。图像,字符串和区域是单独的向量,也被复制。

stack1 += stack2;

本例中的+ =操作自加一切stack2stack1,除了帧。由于框架包含边框和剪切区域,所以stack2边框将被忽略,其所有图层都将被stack1边框的边框和形状剪裁

stack1 = stack2 + stack3;

在这个例子中,stack1将包含stack2来自两个堆栈的框架和层。stack2对象将不会被修改。

模板

模板在技术上不是Style Toolkit的一部分,因为您不需要它们来创建样式。模板只是一种可用于定义,维护和共享样式的机制。

Style Toolkit中的模板与C ++模板不同。C ++模板用于将一组行为应用于许多不同的东西; Style Toolkit模板更类似于木工作模板,用于制作相同内容的许多副本。例如,如果程序有十个按钮,则需要一个机制来制作相同样式的多个副本,而无需定义十次。

由于风格只是一个数据集,您可以通过多种方式创建模板。可以将样式存储到ASCII文件或XML文件中; 然而,我选择使用类,因为它是一个相当简单的方法。

在类方法中,您只需定义一个类,添加一些堆栈或样式作为成员变量,并定义构造函数中的所有层。有关示例请参阅Templates.hTemplates.cpp文件。

范围

您可能会注意到,当查看代码时,栈不会与new操作员声明原因是因为堆栈是临时构造对象。堆栈的数据永久性是什么使得它被添加到样式中。AddStack函数将所有数据复制到样式对象,并且构造堆栈超出范围时将被破坏。样式对象通常被声明为一个类的成员函数,这赋予了它们的永久性。

即使将堆栈声明为模板类的成员变量,模板类本身也是临时构造对象,当它们超出作用域时被破坏。

透明度

演示程序的底部窗格演示了透明度的使用。风格的所有组成部分都可以从完全不透明到完全透明。您可以使用Color类或Clr来定义颜色的透明度如果您不声明透明度,则将完全不透明。例如,要定义半透明蓝色,您将使用Clr semiblue(128,RGB(0,0,255))

图形效率

堆叠实际上只是“画”一次 - 这是第一次被使用。一旦堆栈已经绘制了所有层,它就会创建一个由其框架界定的位图。对堆栈的任何后续绘制调用只将位图复制到设备上下文。这个例外Regenerate是使用堆栈的功能。Regenerate函数将堆栈标记为“脏”,并在下次需要时重新绘制。

PaintStyle函数被调用时,它将所有的“干净”堆栈位图复制到设备上下文中。它单独复制的原因是因为风格不知道是否有任何堆栈“脏”,直到它遍历它们。

对于您可以拥有的层数和堆叠数量没有实际限制。此外,使用Style Toolkit不比直接使用GDI +慢。原因是因为堆栈和层层实际上只是Style定义一系列图形操作对象的嵌套循环计数器定义数组中的操作,而不是使用后面的代码行调用它们并不会改变所需的图形操作数。风格工具包将其所有的绘画在内存中,然后将最终结果复制到屏幕。

如何有效地使用样式工具包

为了最小化绘图操作,为动态对静态创建不同的堆栈。例如,进度控制的背景永远不会改变,所以它有自己的堆栈,只需要画一次。每次发生一个步骤时,需要重新生成前台,所以它在一个单独的堆栈中。

已经标记为再生的堆栈顶部的任何堆叠也需要被标记以供用户再生。即使上层堆栈本身可能没有改变,它的背景也是如此,因此它的位图是不正确的。该工具包不会自动执行此操作,因为它不会跟踪或关心堆栈的相对位置。

不用说,如果一层完全被不是至少部分透明的另一层或堆叠覆盖,那么它应该被去除。堆栈代码不会尝试识别和/或删除任何冗余的涂漆操作。

问题和解决方法

GDI +有一些问题会产生意想不到的结果。

  • 不对称 - GDI +在创建圆角矩形时具有对称性问题。有关详细信息,请参阅我的RoundRect [ ^ ]。我已经为课程中的一些(如果不是全部)问题实施了解决Border方法。Border班是的扩展版本RoundRect类。
  • 工件 - GDI +有时会在使用a时创建工件(不应该存在的额外的行)GradientBrush如果发生这种情况,请尝试以下操作之一:
    • 使用工件更改对象的大小。这是最简单的方法,它通常可以工作,但可能不是可以接受的解决方案。
    • 尝试不同的平滑和插值模式。这有时候起作用,但它也有一个倾向来解决问题。
    • 如果在使用该AddEdgeEffect功能时发生工件,请尝试使用配置文件版本,然后调整配置文件参数,直到工件消失。
  • 延迟绘画 - 控件将不会被画,直到他们得到Windows消息告诉他们这样做,这引起了一些延迟。如果演示程序中有许多控件,则在背景画面之后,它们将倾向于弹出。这是一个非常不利的影响,可以通过将控件预画到背景来规避。控制器后来还是自己画,但你不能说是因为它是画同样的事情。有关如何执行此操作的示例,请参阅Vista Style 2或Aqua Style按钮。

最终评论

演示程序使用VC6和VS2005编译,并在Windows XP上进行测试。但是,Style Toolkit应该与支持GDI +的任何编译器和平台配合使用。

随着时间的推移,会增加更多的控制和功能。如果您想添加特定控件,请在消息部分中请求。


原创粉丝点击