ribbon 相关问题

功能区是一个界面,其中使用选项卡组织一组工具栏。不要将此标记用于与Netflix Ribbon组件相关的问题,请改用[netflix-ribbon]。

我正在尝试使用ribbonmenu,但我遇到了很多麻烦,更改鼠标背景。

是以下内容: resourceDictionary 我只需要使用鼠标通行证更改按钮(<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Fluent="clr-namespace:Fluent;assembly=Fluent"> <ControlTemplate x:Key="Fluent.Ribbon.Templates.FocusVisual.Empty" TargetType="{x:Type Control}"> <Rectangle Fill="#00000000" Stroke="Transparent" /> </ControlTemplate> <Style x:Key="Fluent.Ribbon.Styles.FocusVisual.Empty" TargetType="{x:Type Control}"> <Setter Property="Template" Value="{DynamicResource Fluent.Ribbon.Templates.FocusVisual.Empty}" /> </Style> <ControlTemplate x:Key="Fluent.Ribbon.Templates.MenuSeparator" TargetType="{x:Type Separator}"> <Grid Height="Auto"> <Path Height="1" Margin="32,1,3,2" VerticalAlignment="Center" Data="F0 M 0,0 L 1,0 " Fill="{x:Null}" Stretch="Fill" Stroke="{DynamicResource Fluent.Ribbon.Brushes.Separator.Border}" StrokeDashArray="2 2" /> </Grid> </ControlTemplate> <Style x:Key="Fluent.Ribbon.Styles.MenuSeparator" TargetType="{x:Type Separator}"> <Setter Property="Template" Value="{DynamicResource Fluent.Ribbon.Templates.MenuSeparator}" /> </Style> <ControlTemplate x:Key="Fluent.Ribbon.Templates.Ribbon" TargetType="{x:Type Fluent:Ribbon}"> <ControlTemplate.Resources> <Style BasedOn="{StaticResource Fluent.Ribbon.Styles.MenuSeparator}" TargetType="{x:Type Separator}" /> </ControlTemplate.Resources> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Grid x:Name="PART_LayoutRoot"> <Grid.RowDefinitions> <RowDefinition Height="*" /> <RowDefinition Height="Auto" /> </Grid.RowDefinitions> <Fluent:RibbonTabControl x:Name="PART_RibbonTabControl" AreTabHeadersVisible="{TemplateBinding AreTabHeadersVisible}" ContentHeight="{TemplateBinding ContentHeight}" ContextMenu="{Binding ContextMenu, ElementName=PART_LayoutRoot}" IsDisplayOptionsButtonVisible="{TemplateBinding IsDisplayOptionsButtonVisible}" IsMouseWheelScrollingEnabled="{TemplateBinding IsMouseWheelScrollingEnabled}" IsMouseWheelScrollingEnabledEverywhere="{TemplateBinding IsMouseWheelScrollingEnabledEverywhere}" IsToolBarVisible="{TemplateBinding IsToolBarVisible}" Menu="{TemplateBinding Menu}" /> <ContentControl x:Name="quickAccessToolBarHolder" Grid.Row="1" Height="{TemplateBinding QuickAccessToolBarHeight}" HorizontalAlignment="Left"> <Fluent:QuickAccessToolBar x:Name="PART_QuickAccessToolBar" HorizontalAlignment="Left" Focusable="False" IsMenuDropDownVisible="{TemplateBinding IsQuickAccessToolBarMenuDropDownVisible}" ShowAboveRibbon="{Binding ShowQuickAccessToolBarAboveRibbon, Mode=TwoWay, RelativeSource={RelativeSource Mode=TemplatedParent}}" /> </ContentControl> </Grid> </Border> <ControlTemplate.Triggers> <Trigger Property="IsQuickAccessToolBarVisible" Value="False"> <Setter TargetName="PART_QuickAccessToolBar" Property="Visibility" Value="Collapsed" /> <Setter TargetName="quickAccessToolBarHolder" Property="Visibility" Value="Collapsed" /> </Trigger> <Trigger Property="IsBackstageOrStartScreenOpen" Value="True"> <Setter TargetName="PART_QuickAccessToolBar" Property="Visibility" Value="Collapsed" /> <Setter TargetName="quickAccessToolBarHolder" Property="Visibility" Value="Collapsed" /> </Trigger> <Trigger Property="IsCollapsed" Value="True"> <Setter TargetName="PART_RibbonTabControl" Property="Visibility" Value="Collapsed" /> <Setter TargetName="quickAccessToolBarHolder" Property="Visibility" Value="Collapsed" /> </Trigger> <Trigger Property="QuickAccessToolBar" Value="{x:Null}"> <Setter TargetName="quickAccessToolBarHolder" Property="Content" Value="{x:Null}" /> <Setter TargetName="quickAccessToolBarHolder" Property="Visibility" Value="Collapsed" /> </Trigger> <Trigger Property="ShowQuickAccessToolBarAboveRibbon" Value="True"> <Setter TargetName="quickAccessToolBarHolder" Property="Content" Value="{x:Null}" /> <Setter TargetName="quickAccessToolBarHolder" Property="Visibility" Value="Collapsed" /> </Trigger> <Trigger Property="IsSimplified" Value="True"> <Setter TargetName="PART_RibbonTabControl" Property="ContentHeight" Value="42" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> <Style x:Key="RibbonStyle1" TargetType="{x:Type Fluent:Ribbon}"> <Setter Property="Background" Value="{DynamicResource Fluent.Ribbon.Brushes.Ribbon.Background}" /> <Setter Property="Fluent:FrameworkHelper.UseLayoutRounding" Value="True" /> <Setter Property="FocusVisualStyle" Value="{DynamicResource Fluent.Ribbon.Styles.FocusVisual.Empty}" /> <Setter Property="Foreground" Value="{DynamicResource Fluent.Ribbon.Brushes.LabelText}" /> <Setter Property="IsTabStop" Value="False" /> <Setter Property="QuickAccessToolBarHeight" Value="{Binding TitleBar.ActualHeight, FallbackValue=23, RelativeSource={RelativeSource AncestorType={x:Type Fluent:IRibbonWindow}, Mode=FindAncestor}}" /> <Setter Property="Template" Value="{DynamicResource Fluent.Ribbon.Templates.Ribbon}" /> <Setter Property="TitleBar" Value="{Binding TitleBar, FallbackValue={x:Null}, RelativeSource={RelativeSource AncestorType={x:Type Fluent:IRibbonWindow}, Mode=FindAncestor}}" /> <Setter Property="VerticalAlignment" Value="Top" /> </Style> <Style x:Key="RibbonButtonStyle" TargetType="{x:Type Fluent:Button}"> <Setter Property="Background" Value="{DynamicResource Fluent.Ribbon.Brushes.Button.Normal.Background}" /> <Setter Property="BorderBrush" Value="{DynamicResource Fluent.Ribbon.Brushes.Button.Normal.Border}" /> <Setter Property="Foreground" Value="{DynamicResource Fluent.Ribbon.Brushes.LabelText}" /> <Setter Property="FocusVisualStyle" Value="{DynamicResource Fluent.Ribbon.Styles.FocusVisual.Empty}" /> <Setter Property="Width" Value="90" /> <Setter Property="Padding" Value="5" /> <Setter Property="Template" Value="{DynamicResource Fluent.Ribbon.Templates.Button}" /> <Style.Triggers> <!-- Cambia il colore di sfondo e bordo quando il mouse è sopra il bottone --> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="DarkGray" /> <Setter Property="BorderBrush" Value="White" /> </Trigger> <!-- Cambia quando il bottone è premuto --> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" Value="LightGray" /> <Setter Property="BorderBrush" Value="DarkGray" /> </Trigger> </Style.Triggers> </Style> </ResourceDictionary> )的背景和前景。另一个好事是按下时更改背景。 另一个问题:一旦我按下它,是否有可能将按钮“选择”保留?也许改变颜色或类似的东西? 我在做什么错? 谢谢你 如果您想处理特定状态(Fluent:Button和等),则可以使用。您定义触发器的顺序也很重要,因为首先声明的触发器可以被其下方的触发器覆盖。 在下面的示例中,按下按钮时,将是IsPressed 对于所选零件,您应该只能在IsMouseOver上使用MultiTrigger来处理“选定”状态,在单击后,控件将保持专注。 Background

回答 1 投票 0



我可以在 WPF 应用程序 VS 2022 和 .NET 9 中使用功能区吗

我正在使用 .NET 9 创建一个新的 WPF 应用程序项目,并希望使用我认为在 WPF 中可用的功能区控件。我已经有一段时间没有创建 .NET 桌面应用程序了……

回答 1 投票 0

无法从快速访问工具栏 (QAT) 菜单中删除自定义功能区控件

我有一个适用于 Word 的自定义加载项(也可能适用于 Excel)。 该加载项有一个功能区,其中包含多个组和多个控件(按钮)。 这是一个正在进行的项目和一些丝带

回答 2 投票 0

CopyMemory API 恢复 Excel IRibbonUI 的替代方案是什么

我使用下面的代码来检索在 Template_Rib 全局变量中的功能区回调上设置的 IRibbonUI,该变量随机丢失其值。 但是 CopyMemory API 会使 Excel 随机崩溃。是...

回答 2 投票 0

有没有办法让自定义字体组在受限编辑模式下的 Word-Ribbon 上保持活动状态?

我需要想出一个适用于限制编辑的单词模板来修复样式,但需要粗体、斜体、突出显示、下划线、对齐等活动按钮。有没有办法创建一个

回答 2 投票 0

MS Word 2019 - 在ribbon.xml中创建自己的后台视图窗格

在 Word 2010 时代,我可以使用如下内容在 Ribbon.xml 中创建后台视图,没有任何问题: https://learn.microsoft.com/en-us/previous-versions/office/developer/office-2010/ee69183...

回答 1 投票 0

如何在多个编辑框功能区Excel中设置文本?

我创建了一个自定义选项卡功能区 Excel 界面。 我有两个编辑框,但有一个回调宏。 我创建了一个自定义选项卡功能区 Excel 界面。 我有两个 editBox,但有一个回调宏。 <?xml version="1.0" encoding="utf-8"?> <customUI onLoad="OpenFile" xmlns="http://schemas.microsoft.com/office/2006/01/customui"> <ribbon> <tab id="customTab" label="Exsem" <tabs> <editBox id = "editBox1" getText = "MacroGetText" /> <editBox id = "editBox2" getText = "MacroGetText" /> 这段代码运行良好: Public myRibbon As IRibbonUI Public TextInEditBox As String Sub OpenFile(ribbon As IRibbonUI) Set myRibbon = ribbon TextInEditBox = "" End Sub Private Sub MacroGetText(control As IRibbonControl, ByRef text) text = TextInEditBox End Sub 我可以更改一个编辑框中的值: Public Sub MyTest() TextInEditBox = "1" myRibbon.InvalidateControl ("editBox1") End Sub 这段代码的工作方式很奇怪。 我不明白为什么会这样。 Public Sub MyTest() TextInEditBox = "1" myRibbon.InvalidateControl ("editBox1") TextInEditBox = "2" myRibbon.InvalidateControl ("editBox2") End Sub 如果我这样做,那么只有最后一个值将被插入到两个编辑框中。 为什么我不能向editBox1和editBox2插入两个不同的值?我将不胜感激任何帮助。 我解决了这个问题。有必要将宏和变量分开。像这样: XML <?xml version="1.0" encoding="utf-8"?> <customUI onLoad="OpenFile" xmlns="http://schemas.microsoft.com/office/2006/01/customui"> <ribbon> <tab id="customTab" label="Exsem" <tabs> <editBox id = "editBox1" getText = "MacroGetText1" /> <editBox id = "editBox2" getText = "MacroGetText2" /> VBA Public myRibbon As IRibbonUI Public text1 As String, text2 As String Global TextInEditBox1 As String, TextInEditBox2 As String Sub OpenFile(ribbon As IRibbonUI) Set myRibbon = ribbon End Sub Private Sub MacroGetText1(control As IRibbonControl, ByRef text1) text1 = TextInEditBox1 End Sub Private Sub MacroGetText2(control As IRibbonControl, ByRef text2) text2 = TextInEditBox2 End Sub Sub NowEverythingIsFine() TextInEditBox1 = "1" TextInEditBox2 = "2" myRibbon.Invalidate End Sub

回答 1 投票 0

WPF 中 devexpress 功能区控件的主题

如何在WPF中为devexpressribbon控件设置主题?已安装WPF主题编辑器,但产品浏览器中没有项目可供选择。我尝试了属性功能区样式,但它没有...

回答 2 投票 0

如何使用 Openfeign 配置 Ribbon 来管理重试 - Spring?

我想管理重试。我有 openfeign 客户端,两个微服务。怎么做呢?当我在 yaml 配置中设置时: 富: 丝带: 最大自动重试次数:5 这不起作用。在我的 pom.xml 中是

回答 2 投票 0

从 VBA 定义的宏创建 PowerPoint 加载项

因此,我在 PowerPoint 演示文稿中创建了一个宏,并且我想让其他人可以轻松使用此宏。根据我收集的信息,最好的方法是创建一个加载项...

回答 2 投票 0

Office 2010 添加自定义选项卡/组包含不需要的命令

我继承了一个 Excel 2010 VSTO,它在自己的选项卡下有一个按钮来启动一些代码(我也是 VS 和 C# 的新手)。 我的问题是,当我部署它时,我会收到额外的命令出现在“...

回答 2 投票 0

Excel 隐藏/显示功能区上除自定义选项卡之外的所有选项卡

如何使用 VBA(而不是 XML)隐藏和显示所有标准 Excel 功能区选项卡。我不想隐藏整个功能区(如此处所要求的:VBA 最小化 Excel 中的功能区)而只是隐藏选项卡。我知道如何...

回答 7 投票 0

带有自定义按钮的 Outlook 插件“主页”选项卡

嘿,我正在尝试在 Outlook 2010 的默认“主页”选项卡中创建一个按钮。问题是,在 VS2013 中,我添加了一个功能区(视觉)并添加了带有该按钮的组,但它一直在添加...

回答 1 投票 0

是否有 IRibbonControl.Context 的解决方法或替代方法来访问 Excel 2016 或更高版本中的正确窗口和工作簿?

SO 上已有关于相关主题的帖子,并且 MSDN 上有未解决/未修复的支持请求。听起来“正确的方法”不起作用,所以我想知道......

回答 2 投票 0

Excel自定义RibbonUI + COM可见dll:我可以订阅dll中的功能区控件事件吗?

我正在用C#开发一个COM可见的DLL,并且这个DLL在Excel VBA项目中被引用。这个想法是将所有代码外包给 DLL,而不是使用 VBA。 VBA仅用于订阅...

回答 1 投票 0

Excel:无法删除加载项功能区

我正在尝试在 VBA 中添加一些自定义按钮。问题是我以某种方式在功能区中创建了一个无法删除的菜单。没有启用任何加载项,但每次我打开一个新的

回答 2 投票 0

包含 Excel 文件的自定义功能区按钮

我需要一个功能区选项卡/按钮,仅在 Excel 中打开特定文件时才出现。 单击后,它会运行该 Excel 文件中包含的宏。 就像我想包括功能区选项卡/按钮...

回答 1 投票 0

如何在 Excel-VBA 中以编程方式取消切换所有自定义功能区按钮(仅适用于断点)?

我使用 Office Ribbon X 编辑器为 Excel 创建了自定义功能区 UI。该功能区上有几个切换按钮。 XML 看起来像这样: 我使用 Office Ribbon X 编辑器为 Excel 创建了一个 自定义功能区 UI。该功能区上有几个切换按钮。 XML 如下所示: <customUI xmlns="http://schemas.microsoft.com/office/2009/07/customui" onLoad="OnRibbonLoad"> <ribbon> <tabs> <tab id="customTab" label="MyRibbon" insertAfterMso="TabHome"> <group id="customGroup1" label="MyButtons"> <toggleButton id="customButton1" label="Label1" getPressed="GetPressed" onAction="AnyButtonGotPressed"/> <toggleButton id="customButton2" label="Label2" getPressed="GetPressed" onAction="AnyButtonGotPressed"/> <toggleButton id="customButton3" label="Label3" getPressed="GetPressed" onAction="AnyButtonGotPressed"/> [...] </group> </tab> </tabs> </ribbon> </customUI 如果用户按下其中一个按钮,则会执行一个宏,然后“侦听”用户在工作簿单元格中执行的单击操作,并对单击的单元格进行填充,具体取决于当前按下的切换按钮。由于用户知道将执行哪个宏非常重要,因此有必要 一次只能“切换”一个切换按钮(在活动状态的意义上,true、1、按下... ). 因此我尝试编写一个例程,将所有切换按钮放入一个数组中并循环遍历该数组以使每个与触发例程的控件不匹配的control.ID 无效。我以某种方式实现它,自定义功能区上的每个切换按钮都调用“AnyButtonGotPressed”事件,然后该事件执行循环和无效,然后使用 Select Case 语句调用相关宏。 VBA 代码如下所示,并且对于“第一”、“第二”和“第三”宏基本相同: Option Explicit Public myRibbon As IRibbonUI Public pressed As Boolean ---------------------------------------------------------------------------------- Sub OnRibbonLoad(ribbon As IRibbonUI) Set myRibbon = ribbon End Sub ---------------------------------------------------------------------------------- Sub GetPressed(control As IRibbonControl, ByRef returnedVal) 'However this seems to be neccessary to "unpress" the toggleButtons End Sub ---------------------------------------------------------------------------------- Sub AnyButtonGotPressed(control As IRibbonControl, IsPressed) Dim arrButtons As Variant Dim varButton As Variant arrButtons = Array("customButton1", "customButton2", "customButton3") For Each varButton In arrButtons If Not varButton = control.ID Then myRibbon.InvalidateControl varButton Next pressed = IsPressed Select Case control.ID Case "customButton1" Call FirstMacro(control.ID) Case "customButton2" Call SecondMacro(control.ID) Case "customButton3" Call ThirdMacro(control.ID) End Select End Sub ---------------------------------------------------------------------------------- Sub FirstMacro(ButtonName As String) If Not pressed Then GoTo ErrorHandler On Error GoTo ErrorHandler With ThisWorkbook.Sheets(1) Continue: .Range("A1").Select Do While Selection.Address = "$A$1" DoEvents If Not pressed Then GoTo ErrorHandler Loop 'Here comes stuff to do with the cell, if the user clicks somewhere out of "A1" If pressed Then GoTo Continue End With ErrorHandler: myRibbon.InvalidateControl ButtonName End Sub 不幸的是,这并不能按预期工作,即如果用户选择一个新按钮,切换按钮不会“取消切换”。相反,所有连续选定的按钮都会同时按下。 在尝试调试时,我发现如果我在“AnyButtonGotPressed”的“Select Case”处放置断点,它会变得很奇怪: 我收到的“无法在中断模式下执行代码”警告与我拥有与“AnyButtonGotPressed”相关的切换按钮一样多。 单击“确定”后代码继续执行并按预期工作,这意味着所有其他切换按钮均未按下,只有最后按下的按钮显示为当前按下的功能区。 但不幸的是,如果没有断点,我无法让它像我想要的那样工作。 我已经尝试在“invalidate”循环中添加几个“DoEvents”,然后还将Application.ScreenUpdating设置为False和True以及.EnableEvents但没有成功,功能区不会在没有成功的情况下取消按下toggleButtons断点。我还在没有循环的情况下进行了尝试,并在另一个之后放置了几个“If”语句,以使应取消按下的切换按钮无效。但这些都没有帮助。 请您给我一些想法,如何解决这个问题? 如果可能的话,我更喜欢最简单/最简单的解决方案,因为会有超过 14 个切换按钮(我也已经与 chatGPT 进行了辩论,但她的建议并不简单和/或不起作用 - 比如添加DoEvents 和 100 毫秒的中断以避免计时问题)。 首先,我们需要一种在工作簿中存储数据的方法。您可以使用命名范围或其他内容,但我将使用我的库 LibExcelBookItems,因为它非常易于使用。 LibExcelBookItems模块的代码也在这里: '''============================================================================= ''' Excel VBA Tools ''' ----------------------------------------------- ''' https://github.com/cristianbuse/Excel-VBA-Tools ''' ----------------------------------------------- ''' MIT License ''' ''' Copyright (c) 2018 Ion Cristian Buse ''' ''' Permission is hereby granted, free of charge, to any person obtaining a copy ''' of this software and associated documentation files (the "Software"), to ''' deal in the Software without restriction, including without limitation the ''' rights to use, copy, modify, merge, publish, distribute, sublicense, and/or ''' sell copies of the Software, and to permit persons to whom the Software is ''' furnished to do so, subject to the following conditions: ''' ''' The above copyright notice and this permission notice shall be included in ''' all copies or substantial portions of the Software. ''' ''' THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ''' IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ''' FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE ''' AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ''' LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ''' FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS ''' IN THE SOFTWARE. '''============================================================================= '******************************************************************************* '' Description: '' - Simple strings can be stored/retrieved using CustomXMLParts per book '' - This module encapsulates the XML logic and exposes easy-to-use methods '' without the need to write actual XML '' Public/Exposed methods: '' - BookItem - parametric property Get/Let '' - GetBookItemNames '' Notes: '' - To delete a property simply set the value to a null string '' e.g. BookItem(ThisWorkbook, "itemName") = vbNullString '******************************************************************************* Option Explicit Option Private Module Private Const XML_NAMESPACE As String = "ManagedExcelCustomXML" Private Const rootName As String = "root" '******************************************************************************* 'Returns the Root CustomXMLPart under the custom namespace 'part is created if missing! '******************************************************************************* Private Function GetRootXMLPart(ByVal book As Workbook) As CustomXMLPart Const xmlDeclaration As String = "<?xml version=""1.0"" encoding=""UTF-8""?>" Const rootTag As String = "<" & rootName & " xmlns=""" & XML_NAMESPACE _ & """></" & rootName & ">" Const rootXmlPart As String = xmlDeclaration & rootTag ' With book.CustomXMLParts.SelectByNamespace(XML_NAMESPACE) If .Count = 0 Then Set GetRootXMLPart = book.CustomXMLParts.Add(rootXmlPart) Else Set GetRootXMLPart = .Item(1) End If End With End Function '******************************************************************************* 'Clears all CustomXMLParts under the custom namespace '******************************************************************************* Private Sub ClearRootXMLParts(ByVal book As Workbook) With book.CustomXMLParts.SelectByNamespace(XML_NAMESPACE) Dim i As Long For i = .Count To 1 Step -1 .Item(i).Delete Next i End With End Sub '******************************************************************************* 'Get the Root Node under the custom namespace 'Node is created if missing! '******************************************************************************* Private Function GetRootNode(ByVal book As Workbook) As CustomXMLNode Dim root As CustomXMLNode If root Is Nothing Then With GetRootXMLPart(book) Dim nsPrefix As String nsPrefix = .NamespaceManager.LookupPrefix(XML_NAMESPACE) Set root = .SelectSingleNode("/" & nsPrefix & ":" & rootName & "[1]") End With End If Set GetRootNode = root End Function '******************************************************************************* 'Get an XML Node. Create it if missing '******************************************************************************* Private Function GetNode(ByVal book As Workbook _ , ByVal nodeName As String _ , ByVal addIfMIssing As Boolean) As CustomXMLNode Dim node As CustomXMLNode Dim expr As String ' With GetRootNode(book) expr = .XPath & "/" & nodeName & "[1]" Set node = .SelectSingleNode(expr) If node Is Nothing And addIfMIssing Then .AppendChildNode nodeName Set node = .SelectSingleNode(expr) End If End With Set GetNode = node End Function '******************************************************************************* 'Retrieves/sets a book property value from a CustomXMLNode '******************************************************************************* Public Property Get BookItem(ByVal book As Workbook _ , ByVal itemName As String) As String ThrowIfInvalid book, itemName Dim node As CustomXMLNode Set node = GetNode(book, itemName, False) If Not node Is Nothing Then BookItem = node.Text End Property Public Property Let BookItem(ByVal book As Workbook _ , ByVal itemName As String _ , ByVal itemValue As String) ThrowIfInvalid book, itemName If LenB(itemValue) = 0 Then Dim node As CustomXMLNode Set node = GetNode(book, itemName, False) If Not node Is Nothing Then node.Delete Else GetNode(book, itemName, True).Text = itemValue End If End Property Private Sub ThrowIfInvalid(ByRef book As Workbook, ByRef itemName As String) Const methodName As String = "BookItem" If book Is Nothing Then Err.Raise 91, methodName, "Book not set" ElseIf LenB(itemName) = 0 Then Err.Raise 5, methodName, "Invalid item name" End If End Sub '******************************************************************************* 'Returns a collection of custom node names within the custom namespace '******************************************************************************* Public Function GetBookItemNames(ByVal book As Workbook) As Collection If book Is Nothing Then Err.Raise 91, "GetBookItemNames", "Book not set" ' Dim coll As New Collection With GetRootNode(book).ChildNodes Dim i As Long ReDim arr(0 To .Count - 1) For i = 1 To .Count coll.Add .Item(i).BaseName Next i End With Set GetBookItemNames = coll End Function 调用示例: BookItem(ThisWorkbook, "myVar") = myTextValue Debug.Print BookItem(ThisWorkbook, "myVar") 现在我们有了一种持久存储数据的方法,让我们确保在状态丢失的情况下可以恢复功能区。替换这个: Option Explicit Public myRibbon As IRibbonUI Public pressed As Boolean ---------------------------------------------------------------------------------- Sub OnRibbonLoad(ribbon As IRibbonUI) Set myRibbon = ribbon End Sub 这样: Option Explicit Private myRibbon As IRibbonUI #If Mac Then #If VBA7 Then Private Declare PtrSafe Function CopyMemory Lib "/usr/lib/libc.dylib" Alias "memmove" (Destination As Any, Source As Any, ByVal Length As LongPtr) As LongPtr #Else Private Declare Function CopyMemory Lib "/usr/lib/libc.dylib" Alias "memmove" (Destination As Any, Source As Any, ByVal Length As Long) As Long #End If #Else 'Windows 'https://msdn.microsoft.com/en-us/library/mt723419(v=vs.85).aspx #If VBA7 Then Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr) #Else Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) #End If #End If Sub OnRibbonLoad(ByVal ribbon As IRibbonUI) Set SafeRibbon = ribbon End Sub '=============================================================================== 'Set/Get Ribbon object '=============================================================================== Private Property Set SafeRibbon(ByVal ribbonUI As IRibbonUI) Set myRibbon = ribbonUI Dim mustAvoidSaveDialog As Boolean mustAvoidSaveDialog = ThisWorkbook.IsAddin And ThisWorkbook.Saved BookItem(ThisWorkbook, "BookPtr") = CStr(ObjPtr(ThisWorkbook)) BookItem(ThisWorkbook, "RibbonPtr") = CStr(ObjPtr(myRibbon)) If mustAvoidSaveDialog Then ThisWorkbook.Saved = True End Property Public Property Get SafeRibbon() As IRibbonUI If myRibbon Is Nothing Then If BookItem(ThisWorkbook, "BookPtr") = CStr(ObjPtr(ThisWorkbook)) Then 'Restore ribbon #If Win64 Then Dim ptr As LongLong Const ptrSize As Long = 8 #Else Dim ptr As Long Const ptrSize As Long = 4 #End If Dim obj As Object ' ptr = Int(BookItem(ThisWorkbook, "RibbonPtr")) CopyMemory obj, ptr, ptrSize 'Unmanaged - reference not counted Set myRibbon = obj CopyMemory obj, 0, ptrSize 'Reference count not decremented End If End If Set SafeRibbon = myRibbon End Property 请注意,myRibbon 现在是私有的,并且只能对 SafeRibbon 参数属性进行调用。 另请注意,我删除了 pressed 模块成员 - 我们不再需要它了。 当一个宏已经在循环中运行时,我们不能简单地调用另一个宏。我们必须等待第一个退出。我们可以使用异步调用来实现这一点。 我们还可以使用图书项目库来检索发生失效时最后按下的按钮的状态: Sub GetPressed(control As IRibbonControl, ByRef returnedVal) Dim lastIDPressed As String ' lastIDPressed = BookItem(ThisWorkbook, "lastIDPressed") returnedVal = (lastIDPressed = control.ID) If Len(lastIDPressed) > 0 Then AsyncMacro lastIDPressed End Sub Sub AnyButtonGotPressed(control As IRibbonControl, IsPressed) If Not IsPressed Then BookItem(ThisWorkbook, "lastIDPressed") = vbNullString Exit Sub End If ' BookItem(ThisWorkbook, "lastIDPressed") = control.ID If Not isMacroRunning Then AsyncMacro control.ID End Sub Private Sub AsyncMacro(ByVal ctrlID As String) Application.OnTime Now(), CallbackName("AsyncMacroCallback", ctrlID) End Sub Private Function CallbackName(ByVal funcName As String, ByVal ctrlID As String) As String CallbackName = "'" & Replace(ThisWorkbook.Name, "'", "''") _ & "'!'" & funcName & " """ & ctrlID & """'" End Function Public Sub AsyncMacroCallback(Optional ByVal ctrlID As String) isMacroRunning = True Select Case ctrlID Case "customButton1": FirstMacro ctrlID Case "customButton2": SecondMacro ctrlID Case "customButton3": ThirdMacro ctrlID Case Else '... End Select BookItem(ThisWorkbook, ctrlID) = CStr(False) SafeRibbon.InvalidateControl ctrlID isMacroRunning = False End Sub 其中 Private isMacroRunning As Boolean 在模块级别声明。 最终代码可能如下所示: Option Explicit Private myRibbon As IRibbonUI Private isMacroRunning As Boolean #If Mac Then #If VBA7 Then Private Declare PtrSafe Function CopyMemory Lib "/usr/lib/libc.dylib" Alias "memmove" (Destination As Any, Source As Any, ByVal Length As LongPtr) As LongPtr #Else Private Declare Function CopyMemory Lib "/usr/lib/libc.dylib" Alias "memmove" (Destination As Any, Source As Any, ByVal Length As Long) As Long #End If #Else 'Windows 'https://msdn.microsoft.com/en-us/library/mt723419(v=vs.85).aspx #If VBA7 Then Private Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As LongPtr) #Else Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (Destination As Any, Source As Any, ByVal Length As Long) #End If #End If Sub OnRibbonLoad(ByVal ribbon As IRibbonUI) Set SafeRibbon = ribbon End Sub '=============================================================================== 'Set/Get Ribbon object '=============================================================================== Private Property Set SafeRibbon(ByVal ribbonUI As IRibbonUI) Set myRibbon = ribbonUI Dim mustAvoidSaveDialog As Boolean mustAvoidSaveDialog = ThisWorkbook.IsAddin And ThisWorkbook.Saved BookItem(ThisWorkbook, "BookPtr") = CStr(ObjPtr(ThisWorkbook)) BookItem(ThisWorkbook, "RibbonPtr") = CStr(ObjPtr(myRibbon)) If mustAvoidSaveDialog Then ThisWorkbook.Saved = True End Property Public Property Get SafeRibbon() As IRibbonUI If myRibbon Is Nothing Then If BookItem(ThisWorkbook, "BookPtr") = CStr(ObjPtr(ThisWorkbook)) Then 'Restore ribbon #If Win64 Then Dim ptr As LongLong Const ptrSize As Long = 8 #Else Dim ptr As Long Const ptrSize As Long = 4 #End If Dim obj As Object ' ptr = Int(BookItem(ThisWorkbook, "RibbonPtr")) CopyMemory obj, ptr, ptrSize 'Unmanaged - reference not counted Set myRibbon = obj CopyMemory obj, 0, ptrSize 'Reference count not decremented End If End If Set SafeRibbon = myRibbon End Property Sub GetPressed(control As IRibbonControl, ByRef returnedVal) Dim lastIDPressed As String ' lastIDPressed = BookItem(ThisWorkbook, "lastIDPressed") returnedVal = (lastIDPressed = control.ID) If Len(lastIDPressed) > 0 Then AsyncMacro lastIDPressed End Sub Sub AnyButtonGotPressed(control As IRibbonControl, IsPressed) If Not IsPressed Then BookItem(ThisWorkbook, "lastIDPressed") = vbNullString Exit Sub End If ' BookItem(ThisWorkbook, "lastIDPressed") = control.ID If Not isMacroRunning Then AsyncMacro control.ID End Sub Private Sub AsyncMacro(ByVal ctrlID As String) Application.OnTime Now(), CallbackName("AsyncMacroCallback", ctrlID) End Sub Private Function CallbackName(ByVal funcName As String, ByVal ctrlID As String) As String CallbackName = "'" & Replace(ThisWorkbook.Name, "'", "''") _ & "'!'" & funcName & " """ & ctrlID & """'" End Function Public Sub AsyncMacroCallback(Optional ByVal ctrlID As String) isMacroRunning = True Select Case ctrlID Case "customButton1": FirstMacro ctrlID Case "customButton2": SecondMacro ctrlID Case "customButton3": ThirdMacro ctrlID Case Else '... End Select BookItem(ThisWorkbook, ctrlID) = CStr(False) SafeRibbon.InvalidateControl ctrlID isMacroRunning = False End Sub Sub FirstMacro(ButtonName As String) Debug.Print "Enter macro 1 " & Now On Error GoTo CleanExit Do While BookItem(ThisWorkbook, "lastIDPressed") = ButtonName With ThisWorkbook.Sheets(1) .Range("A1").Select If ActiveCell.Address <> .Range("A1") Then 'Here comes stuff to do with the cell, if the user clicks somewhere out of "A1" End If DoEvents End With Loop CleanExit: Debug.Print "Exit macro 1 " & Now End Sub Sub SecondMacro(ButtonName As String) Debug.Print "Enter macro 2 " & Now On Error GoTo CleanExit Do While BookItem(ThisWorkbook, "lastIDPressed") = ButtonName With ThisWorkbook.Sheets(1) .Range("B2").Select If ActiveCell.Address <> .Range("B2") Then 'Here comes stuff to do with the cell, if the user clicks somewhere out of "B2" End If DoEvents End With Loop CleanExit: Debug.Print "Exit macro 2 " & Now End Sub Sub ThirdMacro(ButtonName As String) Debug.Print "Enter macro 3 " & Now On Error GoTo CleanExit Do While BookItem(ThisWorkbook, "lastIDPressed") = ButtonName With ThisWorkbook.Sheets(1) .Range("C3").Select If ActiveCell.Address <> .Range("C3") Then 'Here comes stuff to do with the cell, if the user clicks somewhere out of "C3" End If DoEvents End With Loop CleanExit: Debug.Print "Enter macro 3 " & Now End Sub 请注意,实际的宏(第一、第二、第三)都不需要对 InvalidateControl 进行更多调用,并且我们也不需要在 InvalidateControl 方法中将所有控件循环到 AnyButtonGotPressed .

回答 1 投票 0

最新问题
© www.soinside.com 2019 - 2025. All rights reserved.