I would like to override the behavior of RoutedUICommand "Copy" of a WPF TextBox.
Is it possible without creating a new TextBoxExtended class that inherits from TextBox?
I have reached that point, but now I am bit lost.
Private Sub tbSource_PreviewExecuted(ByVal sender As System.Object, ByVal e As System.Windows.Input.ExecutedRoutedEventArgs)
Dim commandName = DirectCast(e.Command, Input.RoutedUICommand).Text
If commandName = "Copy" Then
End If
End Sub
Do you have any idea how to continue?
You can add a command binding to the text box to handle the "Copy" command. For example, like this:
<StackPanel>
<TextBlock x:Name="TextBox">
<TextBlock.CommandBindings>
<CommandBinding Command="{x:Static ApplicationCommands.Copy}"
Executed="CommandBinding_Executed"/>
</TextBlock.CommandBindings>
</TextBlock>
<Button Content="Copy"
Command="{x:Static ApplicationCommands.Copy}"
CommandTarget="{Binding ElementName=TextBox}"/>
</StackPanel>
Related
Description: This is Excel 2013 VBA case. I created AddIn which should be able to install and uninstall another AddIns from AddIn list stored in global variable "listOfAddIns" which is Array of Arrays of Variant Type called "valueArray". There are "group control" buttons (for purpose of install or uninstall all available addins) and "individual control" toogle buttons (for install or uninstall specific AddIn). I use a lot of callback functions for purpose of full customization of all elements from VBA.
Problem: Everything works perfectly for installing/uninstalling AddIns without their own Ribbon Tabs. Once installed AddIn has its own Ribbon Tab, I am no longer able to use ribbon object .Invalidate method and call callbacks. I get this poorly descriped Error Message:
Run-Time error'-2147467259(80004005)': Method 'Invalidate' of object 'IRibbonUI' failed
My Idea: Due to described symptomns, I suspect, that there are some duplicity problems of IRibbonControl from different AddIns. Despite a lot of efforts, I cannot figure it out.
Relevant part of CustomUI XML:
<customUI onLoad="OnLoad" xmlns="http://schemas.microsoft.com/office/2006/01/customui">
<ribbon startFromScratch="false">
<tabs>
<tab id = "INST1ID" label="Installer">
<group id="GroupControls" label="Group controls">
<button id="B1ID" label="Install All" size="large" onAction="InstallAll" imageMso="AcceptInvitation" />
<button id="B2ID" label="Uninstall ALL" size="large" onAction="UninstallAll" imageMso="MasterViewClose" />
</group>
<group id="IndividualControls" label="Individual controls">
<toggleButton id="TB1ID" imageMso="HappyFace" label="AddIn1" onAction="TBsControl" size="large" tag="1" getEnabled="TBsGetEnabled" getPressed="TBsGetPressed" getVisible="TBsGetVisible" />
<toggleButton id="TB2ID" imageMso="HappyFace" label="AddIn2" onAction="TBsControl" size="large" tag="2" getEnabled="TBsGetEnabled" getPressed="TBsGetPressed" getVisible="TBsGetVisible" />
<toggleButton id="TB3ID" imageMso="HappyFace" label="AddIn3" onAction="TBsControl" size="large" tag="3" getEnabled="TBsGetEnabled" getPressed="TBsGetPressed" getVisible="TBsGetVisible" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
OnLoad Sub:
Public Sub OnLoad(ribbon As IRibbonUI)
Set ribbonObject = ribbon
End Sub
Main procedure for "Group control" example:
Public Sub InstallAll(control As IRibbonControl)
Call ResetSomePublicVariables
Call CreateEntryValuesAndFillToListOfAddIns
Call CheckAvailabilityAndInstallationOfAddInsAndFillToListOfAddIns
Call UpdateListOfAddInsToInstallAllAvailableAddIns
Call InstallOrUninstallAddInsDueToListOfAddIns
ribbonObject.Invalidate
End Sub
Main procedure for "Individual control" and expanded 4th Sub:
Public Sub TBsControl(control As IRibbonControl, pressed As Boolean)
Call ResetSomePublicVariables
Call CreateEntryValuesAndFillToListOfAddIns
Call CheckAvailabilityAndInstallationOfAddInsAndFillToListOfAddIns
Call UpdateListOfAddInsDueToToogleButtonRequest(control.tag, pressed)
Call InstallOrUninstallAddInsDueToListOfAddIns
ribbonObject.Invalidate
End Sub
Public Sub UpdateListOfAddInsDueToToogleButtonRequest(tag As Long, pressed As Boolean)
Dim valueArray() As Variant
valueArray = listOfAddIns(tag)
If pressed = True Then
valueArray(VAColumnIndex_Installed) = 1
Else
valueArray(VAColumnIndex_Installed) = 0
End If
listOfAddIns(tag) = valueArray
End Sub
Callback example:
Public Sub TBsGetVisible(control As IRibbonControl, ByRef returnedVal)
For Each Item In listOfAddIns
If control.tag = Item(VAColumnIndex_Number) Then
returnedVal = True
Exit For
Else
returnedVal = False
End If
Next Item
End Sub
I didnt used Custom UI Editor or similar software and I guess that it will not be helpful in this phase
I tried to move onLoad sub to ThisWorkbook module. I understand, that moving callbacks to ThisWorkbook is wrong way. I also tried ThisWorkbook and ThisAddin prefixes to IRibbonControl argument, but thats also wrong way. So far with my tries to somehow differentiate eventual IRibbonControl duplicities (my idea only).
I tried to rename IRibbonUI objects in other AddIns without any progress.
I read a lot of sources and saw a lot of examples and different approaches on Microsoft, StackOverFlow on link below but it didnt helped me with this.
https://excelguru.ca/?s=ribbon+part
Any ideas and especially simple solutions of this problem are very welcomed, thank you very much
Solved.
First line of XML file of all my AddIns looked like this and that was a source of all problems described before.
<customUI onLoad="OnLoad" xmlns="http://schemas.microsoft.com/office/2006/01/customui">
Same name for onLoad procedure in different AddIns resulted in described behaviour including earlier mentioned error when 2 or more AddIns with this same name was installed. Renaming onLoad procedures to different names solved everything instantly.
Source which helped me: https://excelguru.ca/debugging-ribbonx-invalidateinvalidatecontrol-failures%E2%80%A6/
I'm building an Add-in for Excel. I wanted to use WPF controls, so I followed this tutorial
https://learn.microsoft.com/en-us/visualstudio/vsto/using-wpf-controls-in-office-solutions?view=vs-2022
I added some tab controls to get my feet wet, I particularly wanted to programmatically go to a tab item if some condition was met upon clicking a button in the Ribbon, I was able to access the header text of the tab item, but I was not able to modify it or set it to isSelected.
This is the code that I'm using:
Imports Microsoft.Office.Tools
Imports Microsoft.Office.Tools.Ribbon
Public Class Ribbon1
Private myUserControl1 As MyUserControl
Private myCustomTaskPane As CustomTaskPane
Private Sub btnCheck_Click(sender As Object, e As RibbonControlEventArgs) Handles btnCheck.Click
' Show the taskpane
myUserControl1 = New MyUserControl
myCustomTaskPane = Globals.ThisAddIn.CustomTaskPanes.Add(myUserControl1, "Test addin")
myCustomTaskPane.Width = 500
myCustomTaskPane.Visible = True
Dim uc As New UserControl1
' Check if there's a table
If isThereATable() Then
MsgBox("There is")
MsgBox(uc.Tab2.Header) ' works
uc.Tab2.Header = "new header" ' does not work
Else
MsgBox("There is not")
End If
End Sub
End Class
And this is the markup code:
<UserControl x:Class="UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:AutoTable"
mc:Ignorable="d"
d:DesignHeight="450" Width="500">
<Grid Width="500">
<TabControl BorderThickness="1" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Stretch" HorizontalAlignment="Left" Width="500" Name="MainTab">
<TabItem Header="Init" Name="Tab1" >
<Grid Background="#FFE5E5E5" Margin="0">
<TextBlock HorizontalAlignment="Left" TextWrapping="Wrap" Text="The following button should create a table." VerticalAlignment="Top" FontSize="16" Margin="10,10,0,0"/>
<Button Content="Create table" HorizontalAlignment="Left" Margin="5,60,0,0" VerticalAlignment="Top" Width="100" Click="Button_Click" Height="30" FontSize="14"/>
</Grid>
</TabItem>
<TabItem Header="CC" Name="Tab2" >
<Grid Background="#FFE5E5E5"/>
</TabItem>
</TabControl>
</Grid>
</UserControl>
I have called the tab control "MainTab", and the two tab items "Tab1" and "Tab2", I can not manipulate any of them with my current code.
UPDATE:
In order to use WPF controls in an Excel Add-In, the WPF User Control must be hosted by a Windows Forms UI Element, according to the docs. In this case, the WPF User Control is inside a Windows Forms User Control. The link above explains how this is done. In the code above, I was using variable names provided by microsoft's tutorial, but by using arguably better variable names, this piece of code would clarify the approach I was following and its solution:
Public Class Ribbon1
Private ThisUserControl As UserControlWF
Private ThisTaskPane As Microsoft.Office.Tools.CustomTaskPane
Private Sub ShowTaskPane_Click(sender As Object, e As RibbonControlEventArgs) Handles ShowTaskPane.Click
ThisUserControl = New UserControlWF
'CustomTaskPanes can only add Windows Forms UI elements, a WPF User Control throws an error
ThisTaskPane = Globals.ThisAddIn.CustomTaskPanes.Add(ThisUserControl, "Title")
ThisTaskPane.Width = 510
ThisTaskPane.Visible = True
' Just to test if a condition is true after it loads the task pane
If isThereATable() Then
MsgBox("There is a table")
ThisUserControl.UserControlWPF1.Tab2.Header = "Different Header" 'Original header name is "TabItem2"
ThisUserControl.UserControlWPF1.Tab2.IsSelected = True 'Tab1 is selected by default
' In my first approach, I was initializing another instance of the WPF control
' instead of using the one that was already initialized:
' Dim uc As New UserControlWPF
' MsgBox(uc.Tab2.Header) ' <- it worked
' uc.Tab2.Header = "Different Header" ' <- it did not work
' That's why I was able to access the Header from the class, but I couldn't
' modify the header that was already loaded
Else
MsgBox("There is no table")
End If
End Sub
End Class
You are setting the Header property of the TabItem in uc, which is a new control that you create without adding it to the add-in.
I guess you should set it in myUserControl1:
Private Sub btnCheck_Click(sender As Object, e As RibbonControlEventArgs) Handles btnCheck.Click
' Show the taskpane
myUserControl1 = New MyUserControl
myCustomTaskPane = Globals.ThisAddIn.CustomTaskPanes.Add(myUserControl1, "Test addin")
myCustomTaskPane.Width = 500
myCustomTaskPane.Visible = True
' Check if there's a table
If isThereATable() Then
MsgBox("There is")
MsgBox(myUserControl1.Tab2.Header) ' works
myUserControl1.Tab2.Header = "new header" ' does not work
Else
MsgBox("There is not")
End If
End Sub
Or you should add uc to Globals.ThisAddIn.CustomTaskPanes to be able to see the change.
I'm trying to find a ButtonBar solution to add to a sheet in excel (so not in forms, directly on the sheet)
While looking I ran into the ActivX controll: ButtonBar Class, that gets added like the code below.
Can anyone tell me how I can add buttons to this control?
Or do you know of any other buttonbar bype controls I could use on an Exel sheet?
ActiveSheet.OLEObjects.Add(ClassType:="UmOutlookAddin.ButtonBar.1", Link:= _
False, DisplayAsIcon:=False, Left:=96.75, Top:=15, Width:=214.5, _
Height:=17.25).Select
You can control the click unsing the code below, but I have not found a way to add new buttons:
Private Sub ButtonBar1_OnClick(ByVal ButtonId As Long)
I don't think you can add buttons. I tried changing the label and that crashed Excel:
Sub Test()
Dim bb As ButtonBar
Set bb = ActiveSheet.OLEObjects(1).Object
bb.SetButtonLabel PlayButtonId, "Test" 'Boom
End Sub
The ButtonBar seems too unstable and I would not recommend using it.
However, you have other options. For example, on the Developer tab you have the simple Button control:
You can add multiple buttons and then group them:
You could obviously make them adjacent to mimic a bar:
As you can see, they even have a 'pressed' animation when you click them (mid button).
If you don't need the animation then you can just add any shape to work as a button. You would add one shape and format it and then make copies and assign a different macro for each (with right click and Assign Macro...). You would then group them when done. For example:
Or, you could just use a custom ribbon tab if you don't necessarily need the buttons in the sheet itself. Here is an example where I showed step by step how to add a custom ribbon but there are many ways of doing it if you search the web. In that example the custom ribbon is not used to display anything but rather is used for it's Init event. But it's easy to replace the xml at step 2f with something like this:
<customUI xmlns="http://schemas.microsoft.com/office/2006/01/customui" onLoad="InitRibbon">
<ribbon>
<tabs>
<tab id ="TestTabID" Label="Test">
<group id="FirstGroupID" Label="First Group">
<button id="RefreshData" label="Refresh Data" size="large" imageMso="Refresh" onAction="RibbonCallTool" />
<button id="UnloadData" label="Unload Data" size="large" imageMso="RecordsDeleteRecord" onAction="RibbonCallTool" />
</group>
</tab>
</tabs>
</ribbon>
</customUI>
in which case you would also have this method in a standard VBA module:
'*******************************************************************************
'Callback ("onAction"). Runs when a control is clicked in the Custom Ribbon tab
'*******************************************************************************
Public Sub RibbonCallTool(ByVal ctrl As IRibbonControl)
Select Case ctrl.ID
Case "RefreshData"
MsgBox "Refresh"
Case "UnloadData"
MsgBox "Unload"
Case Else
Debug.Print "Control <" & ctrl.ID & "> does not have an associated action attached!"
End Select
End Sub
Finally, you could always have a single button that opens a modeless form with all the menus you need.
Intro:
I have written some short excel macros (tested, they work fine) and want to link them to a button in the Ribbon (Excel 2010). I had already done it successfully in Excel 2007.
I am using Custom UI Editor to build a new ribbon, which also works fine. Everything is packaged in a .xlam add-in and added to Excel. The ribbon shows up nicely, all other buttons works, but ...
Problem:
when I hit the button that is linked to the macro I get the error: "wrong number of parameters or property assignment not valid" (message translated from Italian, might not be exactly the same in English)
Troubleshooting info:
The macros do not have parameters. The same macros can be successfully called and executed manually. I am even able to add the same macros to the Quick Access Toolbar.
Here is the specific portion of the ribbon script:
<group id="DupNumber" label="Number" insertBeforeMso="GroupNumber" >
<comboBox idMso="NumberFormatGallery"/>
<box id="HN1" boxStyle="horizontal">
<buttonGroup id="HNButtonGroup1">
<button id="Euro" onAction="Roberto.xlam!EURZ" imageMso="F" supertip="text ..."/>
<button id="EuroNZ" onAction="Roberto.xlam!EURNZ" imageMso="E" supertip="text ..."/>
<button idMso="PercentStyle"/>
<button id="Comma" onAction="Roberto.xlam!NewCommaFormat" imageMso="C" supertip="test ..."/>
<button idMso="PercentStyle"/>
</buttonGroup>
</box>
and here are the macros:
Sub EURZ()
Application.ActiveCell.NumberFormat = "€ #,##0.00"
End Sub
Sub EURNZ()
Application.ActiveCell.NumberFormat = "€ #,##0"
End Sub
Sub NewCommaFormat()
Application.ActiveCell.NumberFormat = "#,##0"
End Sub
Can you help me?
Thanks
Roberto
I believe you need to add this param to your macro: control As IRibbonControl
So it should look like this:
Sub EURZ(control As IRibbonControl)
Application.ActiveCell.NumberFormat = "€ #,##0.00"
End Sub
I'm trying to figure out how to disable a button within my userForm if a certain cell within my spreadsheet equals a certain number. I tried the code stated below, but it isn't working.
Private Sub UserForm_Initialize()
Label2 = Sheets("DATA").Range("AM2").Value
Label4 = Sheets("DATA").Range("AO2").Value
Label7 = Format(Sheets("DATA").Range("R8").Value, "Currency")
If Sheets("DATA").Range("AL10").Value = 10 Then
ActiveSheet.Shapes("CommandButton1").Select
UserFormact_Upgrade.CommandButton1.Enabled = False
Else
End If
End Sub
Your code should be working, as you're on the right path.
To test it, simply create a new form and add this code, you'll see it should work. Maybe you're having problems within the IF clause?
Besides, you don't need to select the shape prior to disabling it; just disable it right away.
Private Sub UserForm_Initialize()
CommandButton1.Enabled = False
End Sub
I know this is old, but got to this thread trying to solve my problem, and found a solution that wasn't mentioned here. So in case someone gets here like I did, and this didn't quite get them where they needed to go, I thought this might help.
I had a userform with a drop down box called cmdADAMFields, and I didn't want my submit button called FieldsSubmitButton to be enabled until I selected something from the dropdown box.
I had to break up my argument into two different private subs vs one larger If-Then-Else statement.
First, I put:
Private Sub UserForm_Activate()
If cmbADAMFields.ListIndex = -1 Then FieldsSubmitButton.Enabled = False
End Sub
Then when for my pulldown's private sub when it's value changed I wrote:
Private Sub cmbADAMFields_Change()
FieldsSubmitButton.Enabled = True
End Sub
The proper place for setting Enabled property is in Activate event (associated with Show method) and not Initialize event (associated with Load instruction).
The below code disable the button CommandButton1 when AL10 cell >= 10.
Private Sub UserForm_Activate()
CommandButton1.Enabled = ( Sheets("DATA").Range("AL10") < 10 )
End Sub
For buttons you can choose between normal buttons (property Enabled=False and property Visible=true), disabled buttons (property Enabled=False and property Visible=true) and invisible buttons (property Enabled=False and property Visible=False), that it is a cleaner interface, in most cases.
Concerning text boxes, besides normal, disabled and invisible status, there is a locked status, that is enabled and visible, but cannot be user edited. (property Locked = True)
A locked control only can be changed by VBA code. For instance, someone can includes date text boxes, that it's filled using a secondary popup date form with Calendar control.