Home > other >  How to Access WPF controls using ElementHost?
How to Access WPF controls using ElementHost?

Time:10-20

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

CodePudding user response:

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.

  • Related