【问题标题】:Creating Ribbon Styled Applications创建功能区样式的应用程序
【发布时间】:2011-06-13 07:49:26
【问题描述】:

使用标准的 Delphi TRibbon 组件,我发现它们并不是那么出色。

  • 首先,它们看起来不如 Microsoft 的漂亮,例如 TRibbon 中的发光效果和颜色不像 Windows 7 中的写字板或画图中使用的那样令人印象深刻。

  • 其次,如果您想创建 Ribbon Styled 界面,我注意到没有独立于 TRibbon 的 Ribbon 样式菜单或弹出菜单。对于实际的功能区有,但如果说出于连续性目的,您希望将功能区样式弹出菜单分配给 TListbox 或 TListView,例如,似乎没有。

  • 第三,有时当功能区动作被禁用时,即使它被禁用,它仍会显示热光效果,就像将鼠标悬停在动作上一样。

  • 最后,我发现尝试将容器组件(例如 TCombobox)放在一个组中非常繁琐。调整控件和位置等的大小真的很尴尬。

我想我的观点是,使用标准的 Delphi TRibbon 组件在视觉和使用上似乎都不是最好的方法。如何使功能区样式的应用程序看起来和工作起来像 Microsoft 的一样整洁,就像我之前所说的 Wordpad 和 Paint 在 Windows 7 中所做的那样?

查看此比较屏幕截图以获得更好的想法:

德尔福功能区似乎不完整,除非我期待太多。我相信功能区组件旨在为您的应用程序提供更好的视觉体验和更好的工作空间等最终用户体验。

您可以提出哪些建议来增强或使 TRibbon 工作并看起来像 Microsoft 的?

我并不一直使用功能区样式界面,所以购买 3rd 方组件并不是我真正想做的事情。我看过 TMS 和 DevExpress 的,但就价格而言,它们看起来也不那么好。 TMS 看起来比标准的 Delphi TRibbon 差。

【问题讨论】:

  • 你考虑过微软提供的COM版本吗?我认为它们很难使用,但我相信最终结果会是高质量的。
  • 据我所知,Delphi 功能区组件是 DevExpress“ExpressBars”的“轻量级”版本。
  • 您可以尝试 TMS 组件 (tmssoftware.com)。他们有自己的原生 Delphi Ribbon 组件,并且他们的组件在视觉上非常吸引人。此外,只要您从他们提供的特定表单下降主表单,您就可以拥有 App 按钮效果(应用程序按钮在标题上方升起)。查看演示以获取更多信息。
  • @Andreas:,它们不是 DevExpress 控件的精简版。 DevExpress ExpressBars 具有完全不同的代码和组件界面。
  • @Andreas:我将确认我们在 ExpressBars 中的功能区尚未授权给 Embarcadero。 Delphi 功能区组件绝不是 DevExpress 功能区的“轻量级”版本。

标签: delphi ribbon


【解决方案1】:

要获得原生外观,请查看Windows Ribbon Framework for Delphi

这是一个围绕 the Windows Ribbon Framework 的开源包装器,自 Windows 7 起可用(以及安装了一些官方更新后的 Vista)。这是 Windows 7 Word Pad 使用的 API。

另请注意,您有两种布局:Office 2007 和 Office 2010。Delphi VCL 功能区实现 Office 2007 样式,而 Windows 七写字板使用 Office 2010 样式。

在我们为一些客户提供的一些项目中,我们使用了TMS software Ribbon components。代码有点过大(有很多重复或写得不好的东西,比如组件持久性),但它工作和渲染都很好,支持 2007 和 2010 Ribbon 样式。对于我们的客户来说,渲染是最重要的。对于我们的开源框架we published a dual solution for building a Ribbon-like GUI,由代码生成:它将使用标准 VCL 组件进行基本布局,或者使用 TMS 组件进行完整的 Office 2007/2010 渲染。我们刚刚定义了一些类,由任一库实现。如果您在自己的代码中使用SQLite3ToolBar(即TSynForm, TSynToolBar, TSynToolButton, TSynPopupMenu, TSynPage, TSynPager, TSynBodyPagerTSynBodyPage 类)和SynTaskDialog(对于TSynButton)中定义的通用组件,则USETMSPACK 条件语句将为您完成所有魔法。

我们还没有使用 Delphi 2009 中引入的 Ribbon 组件。它的动作驱动设计无法轻松与我们的用户界面处理的事件驱动设计进行交互,我们必须承认这个组件的名声很差(至少在 Delphi 2009 版本中)。

用于 Delphi 的出色 Windows 功能区框架无法满足我们从代码中即时生成功能区的需求。它的设计,来自 Microsoft 实现本身,是从 XML 资源创建 UI,在编译时链接......所以它不适合我们的需求,但它可能适合您的需求,用于更“静态”的应用程序 UI 设计。

如果您在应用程序中使用类似 Office 的功能区,请注意 Office UI Licensing

【讨论】:

  • "源代码使用了 Delphi 2010 中引入的一些功能,例如新的 RTTI 引擎。因此您需要 Delphi 2010 或更高版本才能使用此库。"该死的。
  • @A.Bouchez 我只是在仔细阅读代码,看看我能挽救多少——自从我使用的 Delphi 版本以来,Object Pascal 中添加了很多结构。
  • @Ian 你的 Delphi 版本发布时不是克林顿总统吗?!
  • 看起来很有趣,我肯定会更仔细地研究这个答案。
  • 我以前从未听说过这个。谢谢你的独家新闻。我想我的问题是我仍然有很多 Windows XP 用户,所以除非我放弃他们,或者直到他们离开,否则我将无法使用 Windows Ribbon Framework。
【解决方案2】:

务实的答案是使用另一个组件集。 TMS Software version appears good,但我使用的是 DevExpress ExpressBars,它非常适合我。

【讨论】:

  • DevExpress expressBars +1。如果你用 DevEx 做不到,它可能做不到。如果您想要丝带,请使用 DevEx。
  • 也许可以,但正如我所说,我宁愿不必购买第 3 方组件套件。 TMS 功能区看起来也比标准的 TRibbon 差。
  • @Craig:如果您的时间不值钱,那么您可以随时尝试修改 VCL 功能区以使其看起来像您想要的样子。我不相信有任何开源功能区,并且使用 MS 东西的包装器意味着您依赖于 MS .net 控件,这不是一个明智的长期举措。当你在系统中构建这样的依赖时,你会后悔的。
  • @Warren 这不是“MS .net 控件”,不是 DotNet 运行时的一部分,而是操作系统的一部分。从我的小实验来看,依靠 OS/API 控件是一种非常好的方法:您可以确保屏幕上的渲染与 OS 版本相匹配。例如,它不会帮助您的应用程序在 Windows 2000 上大放异彩,但您可以确保它具有原生外观并填充下一个即将推出的 Windows 版本。 Delphi 6 TButton 在 Vista/Seven 下渲染得很好(带有渐变+发光效果)。所以恕我直言,依赖 MS Ribbon 框架比任何其他第三方组件更智能。
【解决方案3】:

我使用 Windows Ribbon Framework - Windows (7) 附带的本机组件。

这是来自 Delphi 的关于 Windows 功能区框架的超短入门;复制粘贴代码的重要部分,不做太多解释:

procedure TfrmTicketDetail.ShowScenicRibbon;
begin
    try
        Fframework := UIRibbon.CoUIRibbonFramework.Create;
        Fframework.Initialize(Self.Handle, Self); //Give the ribbon the hwnd, and our implementation of uiapplication for callbacks
        OleCheck(Fframework.LoadUI(hInstance, 'APPLICATION_RIBBON'));
    except
        on e:Exception do
        begin
            if DebugHook > 0 then
                raise;
            Exit;
        end;
    end;
end;

但它开始变得棘手,因为您必须遵循 Microsoft 的 API。

{IUIApplication}
function  OnViewChanged(viewId: SYSUINT; typeID: UI_VIEWTYPE; const view: IUnknown;
      verb: UI_VIEWVERB; uReasonCode: SYSINT): HResult; stdcall;
function  OnCreateUICommand(commandId: SYSUINT; typeID: UI_COMMANDTYPE;
      out commandHandler: IUICommandHandler): HResult; stdcall;
function  OnDestroyUICommand(commandId: SYSUINT; typeID: UI_COMMANDTYPE;
      const commandHandler: IUICommandHandler): HResult; stdcall;

然后你必须实现它们:

function TfrmTicketDetail.OnViewChanged(viewId: SYSUINT;
     typeID: UI_VIEWTYPE; const view: IUnknown; verb: UI_VIEWVERB;
     uReasonCode: SYSINT): HResult;
var
    cy: integer;
begin
    Result := S_OK;

    //viewID: The ID for the view. Only a value of zero is valid.
    if viewID <> 0 then
       Exit;

    //typeID: The only declared typeID is UI_VIEWTYPE_RIBBON
    if typeID <> UI_VIEWTYPE_RIBBON then
       Exit;

    case verb of //there are only 4 verbs: create, destroy, size, error
    UI_VIEWVERB_CREATE:
        begin
            {   The view was resized.
                In the case of the Ribbon view, the application should call
                GetHeight() to determine the height of the Ribbon.}
            (view as IUIRibbon).GetHeight(cy);
            bvTopSpacer.Height := cy;
        end;
    UI_VIEWVERB_SIZE:
        begin
            {   The view was resized.
                In the case of the Ribbon view, the application should call
                GetHeight() to determine the height of the Ribbon.}
            (view as IUIRibbon).GetHeight(cy);
            bvTopSpacer.Height := cy;
        end;
    UI_VIEWVERB_DESTROY: {nop};
    UI_VIEWVERB_ERROR: {nop};
    end;

    Result := S_OK;
end;

function TfrmTicketDetail.OnCreateUICommand(commandId: SYSUINT;
  typeID: UI_COMMANDTYPE; out commandHandler: IUICommandHandler): HResult;
begin
    commandHandler := Self; //this form will handle all commands on the ribbon;
    Result := S_OK;
end;

function TfrmTicketDetail.OnDestroyUICommand(commandId: SYSUINT; typeID: UI_COMMANDTYPE;
      const commandHandler: IUICommandHandler): HResult;
begin
   Result := E_NOTIMPL;
end;

然后你也必须

  • 实现IUICommandHandler
  • 创作功能区 XML 文件
  • 使用功能区编译器编译功能区 XML 文件
  • 将编译后的功能区作为资源包含:

    {$RESOURCE '..\Resource\UIRibbon\Ribbon_frmTicketDetails.res'}

这是我的应用程序的功能区 xml 转储:

<?xml version="1.0" encoding="utf-8"?>
<Application xmlns="http://schemas.microsoft.com/windows/2009/Ribbon">

    <!-- Commands are like actions, with a name, a numeric ID, caption (LabelTitle), Large and Small images, etc -->
    <Application.Commands>
        <Command Name="cmdNew" Id="0xE100" Symbol="ID_CMD_NEW" LabelTitle="New document" />
        <Command Name="cmdSaveAs" Id="0xE102" Symbol="ID_CMD_SAVEAS" LabelTitle="Save as" />
        <Command Name="cmdOpen" Id="0xE103" Symbol="ID_CMD_OPEN" LabelTitle="Open" />
        <Command Name="cmdExit" Id="0xE104" Symbol="ID_CMD_EXIT" LabelTitle="Exit" />
        <Command Name="cmdUndo" Id="0xE105" Symbol="ID_CMD_UNDO" LabelTitle="Undo" />

        <Command Name="cmdCut" Id="0xE110" Symbol="ID_CMD_CUT" LabelTitle="Cut" />
        <Command Name="cmdCopy" Id="0xE111" Symbol="ID_CMD_COPY" LabelTitle="Copy" />
        <Command Name="cmdPaste" Id="0xE112" Symbol="ID_CMD_PASTE" LabelTitle="Paste" />
        <Command Name="cmdDelete" Id="0xE113" Symbol="ID_CMD_DELETE" LabelTitle="Delete" />
        <Command Name="cmdZoom" Id="0xE114" Symbol="ID_CMD_ZOOM" LabelTitle="Zoom" />

        <Command Name="tabHome" LabelTitle="Home" />

            <Command Name="grpActions" LabelTitle="Actions" />
                <Command Name="cmdSaveAndClose" Id="1101" Symbol="ID_ACTION_SAVEANDCLOSE" LabelTitle="Save and Close">
                    <Command.TooltipTitle>Save and Close (Alt+S)</Command.TooltipTitle>
                    <Command.TooltipDescription>Saves the current ticket and closes the detail screen.</Command.TooltipDescription>
                    <Command.LargeImages>
                        <Image Source="SaveAndClose.bmp" />
                    </Command.LargeImages>
                </Command>
                <Command Name="cmdBack" Id="1102" LabelTitle="Back" />
                <Command Name="cmdControlPanel" Id="1103" LabelTitle="Control Panel" />
                <Command Name="cmdSave" Id="1104" LabelTitle="Save" />

            <Command Name="grpShow" Id="1201" LabelTitle="Show" />
                <Command Name="cmdShowTicket" Id="1202" LabelTitle="Ticket" ></Command>
                <Command Name="cmdShowDiaryEntries" Id="1203" LabelTitle="Diary Entries" >
                    <Command.LargeImages>
                        <Image Source="PencilLog_32x32.bmp" />
                    </Command.LargeImages>
                </Command>
                <Command Name="cmdShowAttachments" Id="1204" LabelTitle="Attachments" />
                <Command Name="cmdShowAuditLog" Id="1205" LabelTitle="Audit Log" />
                <Command Name="cmdShowAdditional" Id="1206" LabelTitle="Additional" />

            <Command Name="grpActivity" LabelTitle="Activity" />
                <Command Name="cmdStartWorking" Id="1301" LabelTitle="Start Working"></Command>
                <Command Name="cmdStopWorking" Id="1302" LabelTitle="Stop Working"></Command>
                <Command Name="cmdPrint" Id="1303" LabelTitle="Print" >
                    <Command.LargeImages>
                        <Image Source="Printer - 256x256.bmp" />
                    </Command.LargeImages>
                    <Command.SmallImages>
                        <Image Source="Printer_16x16.bmp" />
                    </Command.SmallImages>
                </Command>
                <Command Name="cmdDuplicateTicket" Id="1304" LabelTitle="Duplicate Ticket" >
                    <Command.SmallImages>
                        <Image Source="DuplicateTicket16.bmp" />
                    </Command.SmallImages>
                </Command>

            <Command Name="grpTicketStatus" LabelTitle="Ticket Status" />
                <Command Name="cmdCloseTicket" Id="1402" LabelTitle="Close Ticket" />
                <Command Name="cmdOnHold" Id="1403" LabelTitle="On Hold" />
                <Command Name="cmdReadyForInstall" Id="1404" LabelTitle="Ready for install" />
                <Command Name="cmdReopenTicket" Id="1405" LabelTitle="Reopen Ticket" />

    </Application.Commands>

    <!-- Above is all the commands (i.e. Actions). Now we get to the tool on screen (i.e. a DFM) -->
    <Application.Views>
        <Ribbon>

            <!-- Items that appear under the "round button" menu -->
            <Ribbon.ApplicationMenu>
                <ApplicationMenu CommandName="cmdFileMenu">
                    <MenuGroup>
                        <Button CommandName="cmdNew" />
                        <Button CommandName="cmdOpen" />
                        <Button CommandName="cmdSave" />
                        <Button CommandName="cmdSaveAs" />
                    </MenuGroup>
                    <MenuGroup>
                        <Button CommandName="cmdExit" />
                    </MenuGroup>
                </ApplicationMenu>
            </Ribbon.ApplicationMenu>

            <!--What commands to add to the quick access toolbar
                    Right now only Save and Undo, just for fun-->
            <Ribbon.QuickAccessToolbar>
                <QuickAccessToolbar>
                    <QuickAccessToolbar.ApplicationDefaults>
                        <Button CommandName="cmdSave" />
                        <Button CommandName="cmdUndo" />
                    </QuickAccessToolbar.ApplicationDefaults>
                </QuickAccessToolbar>
            </Ribbon.QuickAccessToolbar>

            <!-- And now finally the actual tabs -->
            <Ribbon.Tabs>
                <!--Our one and only tab is "Home" -->
                <Tab CommandName="tabHome">
                    <Tab.ScalingPolicy>
                        <ScalingPolicy>
                            <ScalingPolicy.IdealSizes>
                                <Scale Group="grpActions" Size="Medium"/>
                                <Scale Group="grpShow" Size="Medium"/>
                                <Scale Group="grpActivity" Size="Medium"/>
                                <Scale Group="grpTicketStatus" Size="Medium"/>
                            </ScalingPolicy.IdealSizes>
                            <Scale Group="grpActions" Size="Small"/>
                            <Scale Group="grpShow" Size="Small"/>
                            <Scale Group="grpActivity" Size="Small"/>
                            <Scale Group="grpTicketStatus" Size="Small"/>
                        </ScalingPolicy>
                    </Tab.ScalingPolicy>

                    <!-- Home\Actions -->
                    <Group CommandName="grpActions" SizeDefinition="FourButtons">
                        <Button CommandName="cmdSaveAndClose" />
                        <Button CommandName="cmdBack" />
                        <Button CommandName="cmdControlPanel" />
                        <Button CommandName="cmdSave" />
                    </Group>

                    <!-- Home\Show group -->
                    <Group CommandName="grpShow" SizeDefinition="FiveButtons">
                        <ToggleButton CommandName="cmdShowTicket" />
                        <ToggleButton CommandName="cmdShowDiaryEntries" />
                        <ToggleButton CommandName="cmdShowAttachments" />
                        <ToggleButton CommandName="cmdShowAuditLog" />
                        <ToggleButton CommandName="cmdShowAdditional" />
                    </Group>

                    <!-- Home\Activity group, with a custom sizing definition 
                            so i get my "FourButtons-TwoBigTwoSmall" look -->
                    <Group CommandName="grpActivity" >
                        <SizeDefinition>
                            <ControlNameMap>
                                <ControlNameDefinition Name="button1"/>
                                <ControlNameDefinition Name="button2"/>
                                <ControlNameDefinition Name="button3"/>
                                <ControlNameDefinition Name="button4"/>
                            </ControlNameMap>
                            <GroupSizeDefinition Size="Large">
                                <ControlSizeDefinition ControlName="button1" ImageSize="Large" IsLabelVisible="true" />
                                <ControlSizeDefinition ControlName="button2" ImageSize="Large" IsLabelVisible="true" />
                                <ColumnBreak ShowSeparator="true"/>
                                <ControlSizeDefinition ControlName="button3" ImageSize="Large" IsLabelVisible="true" />
                                <ControlSizeDefinition ControlName="button4" ImageSize="Large" IsLabelVisible="true" />
                            </GroupSizeDefinition>
                            <GroupSizeDefinition Size="Medium">
                                <ControlSizeDefinition ControlName="button1" ImageSize="Large" IsLabelVisible="true" />
                                <ControlSizeDefinition ControlName="button2" ImageSize="Large" IsLabelVisible="true" />
                                <ColumnBreak ShowSeparator="true"/>
                                <Row>
                                    <ControlSizeDefinition ControlName="button3" ImageSize="Small" IsLabelVisible="true" />
                                </Row>
                                <Row>
                                    <ControlSizeDefinition ControlName="button4" ImageSize="Small" IsLabelVisible="true" />
                                </Row>
                            </GroupSizeDefinition>
                            <GroupSizeDefinition Size="Small">
                                <Row>
                                    <ControlSizeDefinition ControlName="button1" ImageSize="Small" IsLabelVisible="true" />
                                    <ControlSizeDefinition ControlName="button3" ImageSize="Small" IsLabelVisible="false" />
                                </Row>
                                <Row>
                                    <ControlSizeDefinition ControlName="button2" ImageSize="Small" IsLabelVisible="true" />
                                    <ControlSizeDefinition ControlName="button4" ImageSize="Small" IsLabelVisible="false" />
                                </Row>
                            </GroupSizeDefinition>
                        </SizeDefinition>

                        <Button CommandName="cmdStartWorking" />
                        <Button CommandName="cmdStopWorking" />
                        <Button CommandName="cmdPrint" />
                        <Button CommandName="cmdDuplicateTicket" />
                    </Group>

                    <!-- Home\Ticket Status group -->
                    <Group CommandName="grpTicketStatus" SizeDefinition="FourButtons">
                        <Button CommandName="cmdCloseTicket" />
                        <Button CommandName="cmdOnHold" />
                        <Button CommandName="cmdReadyForInstall" />
                        <Button CommandName="cmdReopenTicket" />
                    </Group>
                </Tab>
            </Ribbon.Tabs>
            <!-- End of the actual tabs -->

        </Ribbon>
    </Application.Views>
</Application>

【讨论】:

  • 最好使用包装它的 Delphi 组件,正如@A.Bouchez 所建议的那样
  • 哇,这看起来需要付出很多努力,但感谢分享,看到这段代码很有趣。
  • +1 将重要信息放在 StackOverflow 上,尽管我同意 David 关于使用 @ABouchez 建议的已经单元测试的包装器
  • @David, Warren:链接的包装器不适用于所有版本的 Delphi。有些人必须求助于直接调用 Windows API。示例代码也可以帮助理解包装器。
猜你喜欢
  • 2014-09-05
  • 1970-01-01
  • 1970-01-01
  • 2015-08-30
  • 1970-01-01
  • 2020-03-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多