0

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
Edgar O.
  • 37
  • 1
  • 5
  • So what is `uc.Tab2` in your (poor) example? You forgot to include the markup/code for `UserControl1`. – mm8 Oct 18 '22 at 19:38
  • @mm8 @mm8 I'm sorry about the lack of information, the question has been updated with it. As for `uc.Tab2`, that should be a TabItem in the TabControl. When I click a button in Ribbon1, the code I posted shows the myCustomTaskPane with the TabControl inside it. The code I posted does reach the Header attribute of the TabItem named "Tab2" and successfully displays it as "CC" in a message. Then it should also be able to change the header of "Tab2", to "new header" but that does not happen. – Edgar O. Oct 19 '22 at 04:18
  • You are creating a new `UserControl1`. Why? Shouldn't you modify the tab of `myUserControl1`? Or what's the difference between `UserControl1` and `MyUserControl`? – mm8 Oct 19 '22 at 10:59
  • MyUserControl is a Windows Forms User Control. Inside it, there's a WPF User Control that I called UserControl1 and it's using ElementHost to "link?" them. According to the addin tutorial from microsoft, I can not directly use WPF controls, there's gotta be a windows forms ui element hosting it. I think it has something to do with the fact that I was not able to add a WPF User Control to the CustomTaskPanes collection that come with the add in. – Edgar O. Oct 19 '22 at 20:32

1 Answers1

1

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.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • Thank you, your insights helped me realize several problems in my understanding and thus my approach was flawed. The WPF user control is inside a WF user control, so I was accessing the header defined in the class itself, but since the tab displayed on screen was inside an instance of that class, I was not able to modify it. To fix it, I simply used `myUserControl1.UserControl1.Tab2.Header = "new header"`. I will update the question to make it more helpful to others that might be as confused as me, so thank you for making me see my error. – Edgar O. Oct 19 '22 at 20:25