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

VCF中的操作支持

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.63/5 (12投票s)

2006年4月25日

8分钟阅读

viewsIcon

39509

downloadIcon

233

一篇关于为您的 VCF 应用程序添加 Action 支持的文章。

Actions screenshot

引言

您是否曾经有一个单一的功能或函数,有多个 UI 元素可以触发这个函数?是否曾编写代码来更新所有这些元素以保持同步?是否曾希望能够轻松地将更新和函数集中在一个不与窗口类绑定的地方?Visual Component Framework (VCF) 通过使用其 Action 类来解决这个问题,这有点像 MFC 的 ON_COMMAND/ON_UPDATE_COMMAND_UI 宏的增强版。

Action 可以轻松地拥有多个“目标”,所有这些目标都链接到一个对象,该对象可以确定它们的状态并响应“执行”操作的请求。对于只有有限功能的小型应用程序来说,这可能有点过度,但对于大型应用程序来说,它可以更轻松地将控制此行为的代码集中在一个地方,并且可能更容易维护。

Action 的作用

Action 做了两件事:它提供了一种确定一个或多个“目标”的更新状态的方法,并提供了一种让目标“触发”操作的方法,从而导致操作执行或执行某些特定功能。这可以包括打开文件对话框、打印页面,或者您能想到的任何其他适当的功能。

更新

Action 链接到一个或多个“目标”。目标,简单来说,是任何直接或间接派生自 VCF::Component 的对象。每个组件反过来都可以有一个分配给它的 ActionAction 提供了一个 委托,当链接到该 Action 的组件需要更新时,就会触发该委托。然后,连接到此委托的事件处理程序会收到通知,并可以采取适当的操作并修改动作事件,进而由组件用于更新其状态。

更新由 UIToolkit 中的主 UI 事件循环驱动。在运行循环的空闲时间,工具包会检查其要更新的组件列表。在设置组件的 Action 时,会修改此列表。如果 ActionNULL,则组件会将自身添加到工具包的要更新的组件列表中。这确保了我们只更新可能对更新事件做出反应的组件,而不是所有可能的组件。MFC 的处理方式不同:在 MFC 中,更新也由空闲时间处理程序驱动,但它会向主窗口的**所有**子窗口发送消息,对于复杂的 UI,这会浪费大量时间做无用功。

如果工具包发现有组件需要更新,它会遍历并向组件发送一个更新事件。然后,组件会通知其关联的 Action 更新自身。Action 然后在其 Update 委托上触发一个 ActionEvent,通知事件处理程序。处理程序随后可以修改 ActionEvent 上的许多不同值。可以设置文本状态值,还可以切换诸如事件是否“选中”、“启用”等项。一旦委托触发了事件,Action 就会遍历其目标列表,并调用目标的 Component::handleEvent() 函数,并将 ActionEvent 实例传递给它。这允许组件处理动作事件并利用其设置。例如,菜单项可能会根据操作事件中的值更改其文本,或启用/禁用自身。大多数常用类,如菜单项、按钮、工具栏项等,已经内置了支持以这种方式处理更新。以下是一些更新代码的示例:

void onUpdateViewSidebar( ActionEvent* e ) {
    if ( sideBar_->getVisible() ) {
        e->setState( e->getState() | ToolbarItem::tisPressed );
    }
    else {
        e->setState( e->getState() & ~ToolbarItem::tisPressed );
    }
    e->setChecked( sideBar_->getVisible() );
}

此代码检查侧边栏面板 (sideBar_) 是否可见,然后设置 ActionEvent 变量的状态和选中值。

执行操作

除了提供处理更新的框架外,Action 实例还“执行”一些“操作”。当调用 Actionperform() 函数并传入某个事件实例时,就会发生这种情况,它会反过来将该事件传递给 ActionPerformed 委托。附加到 Action 的 Performed 委托的事件处理程序可以相应地响应。这是一个执行操作的示例:

void onViewSidebar( Event* e ) {
    sideBar_->setVisible( !sideBar_->getVisible() );
}

当操作触发时,将调用此函数,该函数反过来会切换侧边栏面板的可见性。更改面板的可见性将反过来更改工具栏项(以及任何其他需要更新的目标)的状态。

以下屏幕截图展示了这一点:

此截图显示侧边栏不可见,菜单项和工具栏项未选中。

一旦单击工具栏项(或菜单项),就会触发上述操作,从而产生以下更新更改:

请注意,现在工具栏项和菜单项的状态反映了状态更改!

几个常见的 UI 元素,如命令按钮、工具栏项、单选和复选按钮以及菜单项,已经内置了功能,可以在必要时检查其关联的 Action 实例并调用 perform() 函数,例如在按钮单击时。这意味着按钮、菜单项以及可能的工具栏按钮都可以是同一个 Action 的目标。单击其中任何一个项目都会导致它检查组件是否有 Action,如果有,则调用 Actionperform() 函数。然后,可以使用单个事件处理程序来响应并执行所需的功能,例如打开新文件。

如何连接 Action 和 Target

基本步骤是创建一个 Action,向 ActionPerformed 委托添加一个事件处理程序来处理您要完成的特定任务,并向 ActionUpdated 委托添加一个事件处理程序来管理更新通知的适当状态。然后,创建 UI 元素(如按钮、菜单项等),并将每个元素添加为相应 Action 的目标。框架将处理其余部分,您需要做的就是填写事件处理程序的代码。如果您有自定义组件或控件,那么您可能需要为它们添加支持以正确处理更新并调用 Actionperform() 函数。我们稍后将介绍这一点。让我们看一个简单的例子:

Action* fileOpenAction = new Action();

//menu
MenuBar* menuBar = new MenuBar();
MenuItem* root = menuBar->getRootMenuItem();
MenuItem* fileMnu = 
    DefaultMenuItem( "&File", root, menuBar );
MenuItem* fileOpenMnu = 
    new DefaultMenuItem( "&Open", fileMnu, menuBar );

//toolbar
Toolbar* toolbar = new Toolbar();
ToolbarItem* openTBitem = 
    toolbar->addToolBarButton( "Open" );
openTBitem->setTooltip( "Open from file" );

//wire up handlers
fileOpenAction->Performed += //some function to perform
                             //the action of opening a file...
fileOpenAction->Update += //some function to perform
                          //the updates...

//add targets
fileOpenAction->addTarget(fileOpenMnu);
fileOpenAction->addTarget(openTBitem);

这会创建一个菜单栏、一个“文件”菜单项,以及一个下面的“打开”菜单项。下一段代码创建了一个工具栏并添加了一个文件打开项。然后,将事件处理程序附加到 ActionPerformedUpdate 委托。最后,将菜单项和工具栏项作为目标添加到 Action。框架将处理其余部分,我们只需要编写事件处理程序。

更新处理程序

我们的更新处理程序的代码很简单(请注意,在本例中,它被声明为静态函数,但它很容易成为类成员函数):

void SomeUpdateFunction( ActionEvent* e )
{
       if ( SomeConditionIsTrue ) {
               e->setEnabled( true );
       }
       else {
               e->setEnabled( false );
       }
}

我们可以做更多的事情,比如更改文本 (ActionEvent::setText()),或者设置选中状态,等等。

操作执行处理程序

最后,我们来实现实际执行代码的部分!在本例中,我们将只打开一个文件打开对话框。

void SomeActionPerformedFunction( Event* e )
{
       CommonFileOpenDialog dlg;
       if ( dlg.execute() ) {
               String fileName = dlg.getFileName();
       }
}

编写自定义事件处理程序以进行更新

完全有可能编写一个需要支持更新事件的自定义组件或控件。这样做相当直接,我们只需要覆盖 Component::handleEvent() 函数。您应该首先调用超类(父类)的 handleEvent()。然后,检查事件类型并确保它是 Action::UpdateEvent 类型。如果是,则读取事件的各种状态设置,例如,您可能需要检查事件的文本。如果您有一个控件,则无需检查启用状态,Control 类已经为您处理了这一点,并将相应地自动设置控件的启用状态。一个简单的例子:

class MyCoolLabel: public Label {
public:
       virtual void handleEvent( Event* e ) {
               Label::handleEvent( e );

               switch ( e->getType() ) {
                       case Action::UpdateEvent : {
                               ActionEvent* ae = (ActionEvent*)e;

                               setCaption( ae->getText() );
                       }
                       break;
               }
       }
};

就是这样!Label 将自动启用或禁用(因为 Control 类实现了 handleEvent()),并且我们将根据操作事件的文本设置来设置标题。

何时调用 Action 的 perform() 函数

当您有一个应该触发此类行为的事件时,您可以随时调用 Actionperform() 函数。例如,CommandButton 在框架通知其发生单击事件时会调用其 Actionperform() 函数。工具栏项在检测到它被单击时也会这样做。

关于构建示例的说明

您需要安装最新版本的 VCF(至少 0-9-0 或更高版本),并且需要确保您已构建 VCF 的静态库(而不是 DLL 版本)。示例已配置为静态链接到 VCF。有关构建 VCF 的更多文档,请参阅 VCF 在线文档中的 Building the VCF

结论

我们已经涵盖了使用 Action 的基本知识以及如何将多个目标链接到它们。我们还涵盖了如何响应框架由于拥有 ActionAction 目标而生成的各种事件。希望这能向您展示如何在下一个基于 VCF 的应用程序中充分利用 Action

欢迎就该框架提出问题,您可以直接在此处或在我们的 论坛中提问。如果您对如何改进这些方面有任何建议,我们很乐意听取!

VCF 中的 Action 支持 - CodeProject - 代码之家
© . All rights reserved.