65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 C++/CLI 的 AnimatedRollupControl

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.89/5 (5投票s)

2008年8月6日

CPOL

23分钟阅读

viewsIcon

34125

downloadIcon

1304

3dsMax 风格的卷展栏控件,使用 C++/CLI 实现动画效果

AnimatedRollupControl1.JPG

引言

本文适合哪些读者?通常,它适用于熟悉 Visual Studio C++ 2008 中 C++/CLI 的中级程序员。为了充分利用本文,这里的中级水平意味着如果您具备以下条件,会觉得它更适合:

  1. 知道如何在项目中添加新项,例如窗体或用户控件。
  2. 能够使用 Visual Studio 设计器创建用户界面。
  3. 能够轻松熟悉 Visual Studio 设计器在创建基于窗体/控件的用户界面时生成的代码(以便您自己可以编写/修改一些代码)。
  4. 对模型-视图-控制器 (MVC) 设计模式术语有所了解。注意:本文中,MVC 术语是作为讨论辅助工具使用/滥用的,而不是作为严格遵守的架构(是否遵守该模式由您选择)。

至少,如果您只是想在另一个项目中使用示例代码,您应该能够遵循在项目中包含新类(对象)、实例化对象并使用它的典型过程(本文摘要中对此进行了概述)。好的……现在开始表演!

我的主要目标是在基于 Windows 窗体的应用程序中使用卷展栏控件。次要兴趣是“看看我们走了多远”,朝着编程涅槃的进程(什么?);也就是说,看看编写一个基于 .NET 版本的卷展栏控件,或者更严格地说,使用 C++ 和公共语言基础设施 (CLI) 会变得多么容易。因此,本文提供了一个我如何实现 C++/CLI 版本卷展栏控件的示例。提供的示例中有两个卷展栏控件;一个是标准卷展栏(即瞬间展开/收缩),另一个包含动画卷展栏(即更慢、更平滑的展开/收缩),并带有卷展栏速度控制。下载的源代码是使用 Visual C++ 2008 Express Edition 创建的(并在专业版中稍作修改)。

对于那些希望直接使用控件而不想阅读本文的人,您可以下载压缩的解决方案,然后转到页面底部的摘要部分,获取有关如何在您自己的应用程序中包含卷展栏控件的基本说明。如果您注意力不集中,开始对阅读本文感到厌倦,这也适用。

背景

卷展栏控件也称为展开控件。卷起或卷入,以及卷下或卷出:这些术语在本文中可以互换使用。卷展栏控件在 CodeProject 上有一段历史。我知道还有两篇较早的文章涉及卷展栏;一篇使用 MFC,另一篇使用 WTL。所以这是关于卷展栏的第三篇文章,这次使用 C++/CLI Windows 窗体。对于不熟悉卷展栏控件或 3dsMax 等复杂程序的人来说,卷展栏控件是一个有用的工具,可以以非常紧凑的方式为您的程序用户提供附加功能,随着应用程序复杂性的增加,它可以腾出屏幕空间,避免可能出现的工具栏、工具面板和“浮动窗口”的海洋。卷展栏控件有两个主要功能:

  1. 卷展页。卷展栏控件通常包含多个可以折叠和展开(或卷起和卷下)的面板或页面。
  2. 滚动条。当您的页面占用的屏幕空间超出您分配的允许查看区域时,会出现滚动条,允许您滚动到当前不在您的查看窗格中的任何感兴趣的页面。

这两个功能使得呈现原本会占用更多屏幕空间的功能成为可能,而这正是您可能不希望允许的。

Using the Code

以下是我们将卷展栏控件放入应用程序的步骤概述:

  1. RollupCtrl.h, RollupCtrl.cpp, RollupCtrlPage.h, RollupCtrlPage.cpp, DialogPage.hDialogPage.cpp 添加到您的项目中。
  2. 声明并实例化一个 RollupCtrl 对象。设置其一些属性:主要属性是 Location 和 Size。
  3. 在 Windows 窗体设计器中的对话框面板上设计您的卷展页。
  4. 声明并创建对话框面板的实例。设置它们的一些属性。
  5. 通过 CreatePage() 方法将每个对话框面板传递给卷展栏控件:以便它创建一个包含您的页面设计的卷展页,并将其添加到其查看面板中。
  6. 将卷展栏控件添加到其宿主窗体或控件中。

您可以将控件放置在窗体中或任何合适的控件中。在示例项目中,一个卷展栏控件被添加到窗体中,而另一个则放置在选项卡控件中。现在让我们看一下这个卷展栏控件的详细信息。

面板,面板,更多面板!

你猜对了……,卷展栏控件是基于 System::Windows::Forms::Panel 的,除了使用了一个 UserControlPanel 对我们来说是理想的,因为它被设计用于分组控件,而 UserControl 为我们提供了一个合适的视觉表面,以便在 Windows 窗体设计器中创建卷展页的内容(例如,通常通过将标签、文本框和其他控件从工具箱拖到表面上来创建页面设计)。在本文中,这个 UserControl 也被称为对话框面板。在我们继续之前,让我们看看构成卷展栏控件的类的定义。有三个类:

RollupCtrl:派生自 Panel。这是包含可扩展页面的宿主控件。我喜欢把它想象成一个进入我们卷展页列表的视口或窗口,因为它差不多就是:一个带边框的矩形。

RollupCtrlPage:派生自 Panel。这是可折叠/可扩展的页面。它只包含一个按钮(用于折叠/展开页面)、一个分组框和一个对话框面板。

DialogPanel:派生自 System::Windows::Forms::UserControl。这是我们创建卷展页内容的面板(我们通常在窗体设计器中将控件拖到面板上)。

使用示例:以下代码片段将创建一个卷展栏控件,其中包含一个类型为 RollupPageDialog 的可扩展/可折叠页面。然后它将卷展栏控件添加到选项卡式页面。您可能还会注意到一些次要的附加功能;您可以:

  • 将控件的 AnimatedRoll 属性设置为 true,以便在页面打开/关闭时获得平滑的动画滚动效果。
  • 设置动画页面关闭/打开的速度,并且您可以:
  • 当用户选择“全部关闭”或“全部打开”选项时,设置动画页面关闭/打开的速度。

有关更全面的示例,请参阅示例项目 Form.h 中的 InitializeRollupCtrls() 方法。

#include "RollupCtrl.h"
#include "RollupPageDialog.h" // include the header file of the dialog panel we
                              // created in the Designer
...
private: RollupCtrl^ rollupCtrl2; 
... 
this->rollupCtrl2 = gcnew RollupCtrl();
// 
// rollupCtrl2 on Tab page
// 
this->rollupCtrl2->Location = System::Drawing::Point(10, 10);
this->rollupCtrl2->Text = L"rollupCtrl2";
this->rollupCtrl2->Size = rollupCtrlSize;
this->rollupCtrl2->TabIndex = 2; 
...
...
/// The DialogPanel RollupPageDialog (derived from DialogPanel) is created
/// in the Designer before the next line can happen
RollupPageDialog^ rollupPageSample = gcnew RollupPageDialog();
rollupPageSample->Text = L"rollupPageSample";
rollupPageSample->Name = L"rollupPageSample";

//give the page a reference to this form
rollupPageSample->ClientForm = this;  

this->rollupCtrl2->CreatePage(rollupPageSample);
//
// rollupCtrl2 Added to Tab Page
//
this->tabPage1->Controls->Add(this->rollupCtrl2);

// Animated Rollup Page Properties.
// Set Page Rollup Speed: the bigger the number the faster the rollup speed
// Note: some values may not roll up neatly. There are no limits! So try a value
// of 200 or more at your peril?!
//
this->rollupCtrl2->RollupSpeed = 2; 
//
// also you can rollup/down faster when user selects "Close All" or "Open All" from the 
// context menu. The default value for this is value of the RollupSpeed property 
//
this->rollupCtrl2->RollupSpeedAll = 100; 
//
// turn on animated rolling
// 
this->rollupCtrl2->AnimatedRoll = true; 
this->rollupCtrl2->ResumeLayout(false);

现在我们所要做的,以实际在控件中看到一些东西,就是创建并添加包含卷展页数据的 rollupSamplePage 类。这是使用卷展栏控件过程中要介绍的最后一个基本步骤(或两个步骤)。

如何在设计器中创建卷展页的内容

有多种方法可以创建卷展页内容。以下是示例解决方案中提供的一种方法:

要在 Visual C++ 2008 Express Edition 中设计卷展页,请执行以下操作:

  1. 向项目添加一个新的窗体项。
  2. 将窗体从 DialogPanel 类派生。例如,在新窗体的头文件中包含 DialogPage.h,并将新窗体类定义中的“public System::Windows::Forms::Form”替换为“public DialogPanel”。

现在,当您在设计器中查看 DialogPanel 窗体时,它应该看起来像一个典型的空白 UserControl 对话框窗格或表面,并且已准备好让您在其上创建新页面。只需拖动调整大小,然后创建您的新页面。

卷展栏控件的基本信息到此结束。下一节包含一些在创建卷展栏控件时出现的可能引起兴趣的额外问题。

关注点

与开发过程中出现的问题相比,使用刚刚描述的卷展栏控件的说明要简单得多。下面描述了其中一些问题以及其他一些有趣的地方。

卷展页内容创建替代方案

DialogPanel 的使用是可选的。它是用户界面开发中两个典型功能的简单、方便的示例。根据您对单一职责原则的执着程度,DialogPanel 可以被视为两个 UI 功能的“快速而粗糙”的示例。两种通常以某种形式需要的 UI 功能是:

  1. 一个用于设计对话页面的可视化表面。
  2. 与模型交互的接口。这里的术语属于模型-视图-控制器 (MVC) 架构模式类型。模型是存储数据的实际位置,这些数据在视图中可视化呈现(视图由文本框、按钮和在卷展页中呈现给用户的其他控件组成)。

一些可视表面选项

  • 在 Visual C++ 2008 中,我们可以为我们创建的每个页面添加一个新的 UserControl 项作为设计表面。您可以将所有对 DialogPanel 对象的引用替换为对 UserControl 对象的引用,并完全删除 DialogPanel。只需要更改 2 或 3 个引用,并且可以使用 IDE 中的“编辑”->“查找和替换”菜单选项成功修改。当您使用此选项时,您将放弃继承可能提供的代码重用好处。

  • 创建您自己的继承自 UserControlDialogPanel 类型类。

希望从这些选项中获得的主要一点是,您可以轻松地以最适合您的方式修改提供的演示源代码。

一些模型接口选项

首先,我们简要了解一下 DialogPanel 在这方面有什么。示例项目中的每个页面都包含一个 DialogPanel 实例作为成员。它为我们提供了设计表面来创建卷展页,并且它包含一个页面和模型之间接口的示例。在这种情况下,模型仅仅是 Form1,或者更具体地说,是应用程序主窗体中的几个文本框,它们所做的只是显示用户与视图交互的一些结果(即,当用户与页面上的控件交互时,会显示卷展页名称、控件名称和状态(如果适用)等信息)。DialogPanel 将对 Form1 的引用存储在 ClientForm 属性中,接口是属于该窗体的所有公共方法。您可以从上面的代码片段中看到设置客户端窗体属性的示例:

 //give the page a reference to this form
 rollupPageSample->ClientForm = this;

在演示代码中,我们的卷展页只对一个方法感兴趣:

 void UpdateInfo(String^ msg1, String^ msg2)

它是一个(糟糕?)的通用函数名,但它是一个通用示例(因此就这样)。我希望您在方法名称选择上更有意义。如果页面的 ClientForm 属性设置为 Form1,那么 UpdateInfo() 用于向窗体传递两个字符串消息:当用户点击页面中的控件时,以及当鼠标离开页面中的控件时。可以向 Form1 添加其他公共访问方法,以根据需要向其传递附加数据。

其他选项

  • 如果您还没有一个成熟的应用程序以及管理用户交互的系统,那么研究模型-视图-控制器/演示者 (MVC/MVP) 和模型-视图-适配器 (MVA) 设计模式可能会激发您的思考。
  • 随着卷展栏控件中添加的卷展页越来越多,视图和模型之间的通信通常会增加或变得更加复杂,这是显而易见的。您可能会觉得更舒适地创建一个 interface 作为……好吧,作为每个模型和视图之间的接口。提出这种技术是为了让您研究(并/与朋友讨论)其作为替代方案的优点,本文将不再进一步讨论。

如果我必须声明我对设计模式(例如上面提到的 MVC/MVP/MVA)的总体方法,那最好用“平等主义”来形容。平等主义?:例如,像 MVC 这样的设计模式是很棒的工具,因为它们为软件设计中如此通用的问题提供了解决方案,以至于一个通用模式确实非常简洁地阐明了相关的开发问题(也许这句话可以更简洁!)。设计模式应该按照它的本来面目使用,它是一个通用工具。*它是一个通用工具,可以根据您认为合适的特定设计需求进行应用。*所以,当我看到有人提到滥用设计模式时,我忍不住认为这样的讨论更多地与讨论参与者的个人需求有关,而不是实际的设计模式主题。如果你观察一个开发团队中的群体动态,这种现象甚至更加明显。

ContextMenu

RollupCtrl 中右键单击,可以看到一个上下文菜单,其中包含关闭和打开页面的选项。我正在考虑是否应该添加一个选项,让用户能够更改动画卷展速度(什么?……更多的附加功能!)。

滚动条

RollupCtrl 使用的滚动条是 Windows 提供的默认滚动条,而不是我通常在卷展栏控件中看到的更小、更紧凑、“更好”的、由所有者绘制的滚动条。也许应该更改?但我不会更改它。

.NET 中的鼠标光标

.NET 框架对自定义鼠标光标的支持如何?根据这个旧链接(大约 2005 年),它显然处理得很差。似乎有一些建议的解决方案,其中大多数在一般情况下似乎都不起作用,除了链接中的最后一个建议。我选择了最简单的解决方案,把它放在某个文件夹中,每次需要时在运行时加载。例如,下面的代码显示,当鼠标进入查看区域时,它会变成 Pan.Cur(手的图片)光标,表示通过拖动进行滚动处于“活动”状态。请注意,似乎 thePan.Cur 文件在每个实例中都会加载!这当然不是一个理想的情况。

void RollupCtrlPage::RollupCtrlPage_MouseEnter(Object^ sender, System::EventArgs^ /*e*/)
{

 if(bHasFocus == false)
  bHasFocus = ((RollupCtrl^)(this->Parent))->ContainsMouse();
 if(bHasFocus) 
 { 
 this->Cursor = gcnew System::Windows::Forms::Cursor("Pan.Cur"); 
 this->Focus(); 
 }
}

尽管这似乎并没有导致应用程序性能下降,但有一天可能会。因此,如果任何人有更最新的信息,或者您认为对我们处理自定义光标更有效的方法有所帮助,请告诉我们(或者如果我自己找到了更好的方法并使用了它,我会给您更新)。

页面平滑无闪烁动画卷展(消除造成大问题的次要烦恼)

.NET 之前的 MFC 给我留下的持久印象之一是它的行为可能反复无常、特立独行,甚至彻头彻尾的古怪。.NET 和 Forms 控件也有其相当多的特立独行之处,但程度不如 MFC(或者到目前为止看起来是这样)。当你开始创建超出标准、普通控件使用范围的元素或功能时,这种古怪或意想不到的行为会迅速出现。对于初学者来说,这种古怪或意想不到的行为通常也表明是时候加入中级程序员的行列了:也许会导致自己绘制控件,或者至少,找到其他 API 调用,这些调用将为您提供对程序行为更大的控制,以实现您的目标。

出现闪烁控制问题。在某些圈子中,闪烁的控件似乎是一个热门话题,这可能是因为它通常是初学者尝试非常规操作时出现的第一个意外行为之一。它最大的影响是它无可挑剔的能力,使最好的应用程序看起来和感觉都很业余。如果您对这个主题进行足够多的研究,您会发现闪烁的主要原因是两次绘制了相同的像素。解决方案也同样简单:确保不要两次绘制相同的像素。因此,问题和解决方案似乎都很明确。然而,解决问题的方法并不那么明确。好处是您可以自由使用任何您喜欢的方法来消除闪烁。但这种自由需要智慧。如果没有明智的选择,您可能会不必要地编写大量代码:有许多非常著名的策略,从简单的单行标志/属性设置双缓冲到更复杂的 GDI+ 离屏设备上下文使用,以及管理所有矩形、剪裁和我们自己绘制。有些策略可以在 .NET 级别使用,而另一些则需要访问底层的 Win32 API。

你可能已经猜到了,我第一次尝试实现像平滑动画卷展页这样不寻常的东西时,有时会出现闪烁(惊喜,惊喜……不)。所以这里简要介绍一下如何消除闪烁:但首先,我是一个懒惰的程序员:我避免编写单调、重复和冗余的代码。其次,我也很:我甚至不假装知道任何事情(好吧……那不是真的,我有时会假装);我总是在寻找更好的做事方法。因此,我以这种态度尝试消除卷展栏控件中的闪烁。我最不想做的就是编写大量的冗余绘图代码,或者在我的应用程序中塞入一些 Win32 代码只是为了消除闪烁。我尝试了双缓冲属性和标志等常见嫌疑犯,但都无济于事。所以,我只是想在认真地自己绘制控件之前尝试一些不同的东西。我知道在卷展动画期间我两次绘制了相同的像素,但它在哪里?如果我将动画速度足够慢,我实际上可以看到控件被绘制的两个不同位置。所以我开始寻找任何可能在同一方法中多次更新控件位置的代码。它在这里,在 RollupCtrlPage.cpp 中:

/// Moves the page relative to the Rollup Control 
/// Called on all pages at a time to simulate scrolling 
/// of the canvas panel 
/// 
void Scroll(int yDelta) 
{ 
 this->Location = System::Drawing::Point(0, y-yDelta); 
 Update(); 
} 
/// 
/// recalculate the absolute position of the page on the canvas panel 
/// 
void ReCalcLayout(int yAdjustment)
{ 
 y += yAdjustment;
 this->Location = System::Drawing::Point(0, y); //this line is omitted to remove
                                                //flickering! 
}

这些是唯一修改页面位置的方法(除了初始化代码),它们最终在 RollupCtrl::AdjustLayout() 方法中被调用。我看到的闪烁似乎确实与 Location 属性上的两次不同调用相符。“也许只设置一次 Location 会有效,”我心想。删除行:

 this->Location = System::Drawing::Point(0, y);

ReCalcLayout() 中确实消除了闪烁(y 变量存储页面位置,但直到设置 Control::Location 属性才实际设置)。那么我学到了什么?可能没学到多少。我必须假设控件在每次设置 Location 属性后都会被绘制,*至少在这种情况下*,并且这应该是将来需要注意的事情。然而,对于页面内的控件来说,情况并非如此。例如,数据面板的 Location 属性在 RollupCtrlPage::Resize() 方法中设置,但直到上面 Scroll() 方法中调用 Update() 后才正确绘制。

与我们自己完成所有绘制相比,这是一种更好的消除闪烁的方法吗?这取决于你。我自认为很幸运(因为懒惰)能够消除闪烁,而无需更深入地自己绘制控件,任何严肃的程序员都会证实我应该这样做(我希望当你运行示例时它不会闪烁,因为如果我没有自己绘制控件,我将得到所有应得的批评:我充满了信心,不是吗?)。所以,作为警告:“孩子们,不要在家尝试”,这是一个不良编程实践的好例子。做正确的事情:当你开始做一些不寻常的事情,比如动画卷展栏时(如果你想避免“严肃程序员”的鄙视),请自己绘制控件。

UserControl 继承与 /clr 异常

在您的项目中使用托管和非托管代码(选中 /clr 属性)可能会出现问题,如果您从 UserControl 继承的话。结果:在设计器中,您可能会看到类似下图的内容,而不是一个空白的表面来创建您的卷展页对话框:

AnimatedRollupControl2.JPG

“鉴于已知的 CLR 限制,这是预期行为。用户控件和继承的窗体也会出现同样的问题。解决方法是将混合模式代码编译为 DLL 而不是 EXE。” WenYuan Wang [MSFT] - 2007年6月18日 04:35 GMT,微软在线社区支持,参考此链接。所以,如果您研究一下,这不是一个异常,而是预期行为。

我曾使用过其他两种解决方法:

  1. 将公共语言运行时支持属性更改为 /clr:pure 来设计页面。然后将其改回 /clr 来运行程序。请确保在用 clr:pure 重新编译之前关闭您想在设计器中看到的文件,以便它刷新并正确加载,然后您应该会看到预期的空白表面来创建您的页面。这是一个冒险的尝试,因为根据您的应用程序,编译器可能会对存在非托管代码进行强烈抱怨。
  2. 在除 Express Edition 之外的 Visual Studio 版本中:不要从 UserControl 继承。只需为每个要创建的卷展页设计向项目添加一个新的 UserControl 项。您将获得从 UserControl 继承的所有内容,再加上一个设计器表面,并且在使用 /clr 时不会出现错误框。然而,缺点是我们放弃了继承可能提供的代码重用好处。总有替代方案:例如,一个更明智的设计可能是赋予 UserControl 一个目的(提供设计器表面),并实现一个 interface 作为视图和模型之间的接口。但是,设计超出了本文的范围。我们主要关注卷展栏控件。因此,坚持示例项目中的简单示例,其中 UserControl 提供两件事:设计表面和接口方法,您可以将所有对 DialogPanel 对象的引用替换为 UserControl 对象,并删除 DialogPanel 类。如果您愿意,可以添加 ClientForm 类型方法以与模型交互,或者(我期望您最终)实现您自己首选的视图-模型交互策略。

因此,有 3 个选项可供选择,作为解决“用户控件继承和 /clr 异常”所产生行为的替代方案。

您会在示例源代码中找到其他感兴趣的地方,以注释的形式存在。许多注释直接针对本文的读者,因此,通常情况下它们不会存在。因此,我希望这些注释更多的是帮助而不是阻碍。

摘要

我希望每篇文章都有这一部分;总结;当你只想了解基本信息时;跳过文章,尽快让代码运行起来。

以下是我们将卷展栏控件放入应用程序的步骤概述:

  1. RollupCtrl.h, RollupCtrl.cpp, RollupCtrlPage.h, RollupCtrlPage.cppDialogPage.h 添加到您的项目中。
  2. 声明并实例化一个 RollupCtrl 对象。设置其一些属性:主要属性是 Location 和 Size。
  3. 在 Windows 窗体设计器中的对话框面板上设计您的卷展页。
    • 向项目添加一个新的窗体项。
    • 将窗体从 DialogPanel 类派生。例如,在新窗体的头文件中包含 DialogPanel.h,并将新窗体类定义中的“public System::Windows::Forms::Form”替换为“public DialogPanel”。
    • 现在,当您在设计器中查看 DialogPanel 窗体时,它应该看起来像一个典型的空白 UserControl 对话框窗格或表面,并且已准备好让您在其上创建新页面。只需拖动调整大小,然后创建您的新页面。
  4. 声明并创建对话框面板的实例。设置它们的一些属性。
  5. 将每个对话框面板传递给卷展栏控件:以便它可以创建一个包含您的页面设计的卷展页,并将其添加到其查看面板中。
  6. 将卷展栏控件添加到其宿主窗体或控件中。

您可以将卷展栏控件放置在窗体中或任何其他合适的控件中。在示例项目中,一个卷展栏控件被添加到窗体中,而另一个则放置在选项卡控件中。要查看一些示例代码,请向上滚动到文章中的第一个代码块(包含步骤 2、4、5 和 6 的示例),或者要查看更全面的示例,请参阅示例项目 Form.h 中的 InitializeRollupCtrls() 方法。如果您遇到困难或有问题,查看上面的兴趣点部分可能会有所帮助。

最后……

最后,您可能还记得我在文章中的次要兴趣是想知道与旧技术(例如 .NET 之前的 MFC)相比,使用 C++/CLI 实现卷展栏控件会容易多少(即,回想一下:“看看我们离编程涅槃还有多远”)。嗯,不计算行数,代码量*似乎*少了很多(这并非总是好事,例如,那些通过故意编写简洁晦涩的代码来给人留下深刻印象的人可能会让人难以合作)。如果我们忽略大量标准初始化代码(自动生成和手写),代码量*确实*少了很多。代码量少意味着实现应该更快,而且你编写的代码越少,产生错误的机会就越小(我们的编程理论就是这样运作的,对吧?)。但主要优点是我们需要编写的代码易于理解(这对于维护非常重要,希望对像你我这样的文章读者程序员也有益)。所以,不用说,我印象深刻。

附言:关于平滑动画控件的几句话

在开发卷展栏控件的过程中,对平滑动画页面的持续测试和使用对我产生了令人惊讶的深刻影响(当一个 UI 控件能对一个人产生这种影响时,真是令人悲伤!)。它们使用起来感觉比标准即时卷展栏控件好得多。如果您试用示例,有时您可能会注意到动画页面在卷起时会有轻微的抖动或颤抖/波动效果(似乎只在关闭页面且滚动条按钮位于滚动条控件底部(即最大值)时才明显)。我甚至喜欢这种效果,足以保留它[作为一个功能!?](当然,如果您不喜欢,可以尝试消除它)。我希望您在使用卷展栏控件时能像我在制作它时一样享受到乐趣。

我不记得我是否真的在视频游戏之外见过或使用过这种平滑动画类型的控件(所以,如果您知道任何拥有它们的 Windows 应用程序,请告诉我们)。未来我将很难不使用这些动画类型的控件。话又说回来,我怀疑它可能只是一种最终会消退的新奇事物。

© . All rights reserved.