From 15b0646aa3eed1097071fe8fc44426e949a20f73 Mon Sep 17 00:00:00 2001 From: Maarten van Stam Date: Fri, 26 Apr 2024 01:52:15 +0200 Subject: [PATCH] Add Add-in Commands to the Ribbon to interop between JavaScript and C# with Blazor Pages. (#762) * Initial Branch Custom Add-in Functions from Ribbon * Made a little bit of progression on implementing Commands, not working though To be continued * Enabled JavaScript Add-in Commands (Color and Write), Blazor Functions still unable to be called from Add-in Commands. We know how to load the environment but Blazor load timing still arrives AFTER the function call. * Got the Ribbon .NET Blazor Interop to work from the Ribbon to run C# Methods in the OfficeJS Ribbon context. * Got everything to work, calling C# functions from the Ribbon, and calling JS functions from that C# function. * Added JS - CS Interop Functions to Word Office Add-in, similar to what we did earlier with Excel. Also some minor restructuring and fixes, removing obsolete code. * Updated final Excel Checks, Readme still open to finalize. * Making Minor Changes, still need to finalize Readme and checks before PR * Updated ReadMe and cleaning up before creating PR to Microsoft Repo * Remove generated items from csproj.user (in general I think .user should not even be in the project ...) * Added some comments * Update Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/BubbleChart.razor.js Co-authored-by: David Chesnut * Update Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/Commands/commands.js Co-authored-by: David Chesnut * Update Samples/blazor-add-in/excel-blazor-add-in/README.md Co-authored-by: David Chesnut * Update Samples/blazor-add-in/word-blazor-add-in/README.md Co-authored-by: David Chesnut * Update Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/Commands/commands.js Co-authored-by: David Chesnut * Update Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-sideloader/excel-blazor-sideloaderManifest/excel-blazor-sideloader.xml Co-authored-by: David Chesnut * Update Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-sideloader/excel-blazor-sideloaderManifest/excel-blazor-sideloader.xml Co-authored-by: David Chesnut * Update Samples/blazor-add-in/excel-blazor-add-in/README.md Co-authored-by: David Chesnut * Update Samples/blazor-add-in/excel-blazor-add-in/README.md Co-authored-by: David Chesnut * Update Samples/blazor-add-in/excel-blazor-add-in/README.md Co-authored-by: David Chesnut * Update Samples/blazor-add-in/word-blazor-add-in/README.md Co-authored-by: David Chesnut * Update Samples/blazor-add-in/excel-blazor-add-in/README.md Co-authored-by: David Chesnut * Update Samples/blazor-add-in/word-blazor-add-in/README.md Co-authored-by: David Chesnut * Update Samples/blazor-add-in/word-blazor-add-in/README.md Co-authored-by: David Chesnut * Update Samples/blazor-add-in/word-blazor-add-in/README.md Co-authored-by: David Chesnut * Added suggestions from PR * Added some errorhandling and fixing Excel API call * Another missed change ... --------- Co-authored-by: Maarten van Stam Co-authored-by: David Chesnut --- .../excel-blazor-add-in/README.md | 12 +- .../excel-blazor-add-in/BlazorAddIn.csproj | 9 +- .../Pages/BubbleChart.razor.cs | 34 +- .../Pages/BubbleChart.razor.js | 52 +-- .../excel-blazor-add-in/Pages/Index.razor.cs | 17 +- .../excel-blazor-add-in/Pages/Index.razor.js | 5 + .../wwwroot/BlazorAddIn.lib.module.js | 10 +- .../wwwroot/Commands/commands.js | 197 +++++++++++ .../excel-blazor-add-in/wwwroot/index.html | 9 +- .../excel-blazor-sideloader.csproj.user | 8 + .../excel-blazor-sideloader.xml | 172 ++++++++-- .../word-blazor-add-in/README.md | 12 +- .../word-blazor-add-in/BlazorAddIn.csproj | 11 +- .../word-blazor-add-in/Pages/Index.razor.cs | 30 +- .../word-blazor-add-in/Program.cs | 1 + .../wwwroot/BlazorAddIn.lib.module.js | 9 +- .../wwwroot/Commands/commands.js | 28 ++ .../word-blazor-add-in/wwwroot/index.html | 19 +- .../word-blazor-sideloader.csproj.user | 2 +- .../word-blazor-sideloader.xml | 305 +++++++++++------- 20 files changed, 730 insertions(+), 212 deletions(-) create mode 100644 Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/Commands/commands.js create mode 100644 Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/Commands/commands.js diff --git a/Samples/blazor-add-in/excel-blazor-add-in/README.md b/Samples/blazor-add-in/excel-blazor-add-in/README.md index f1669d77d..b1db9921e 100644 --- a/Samples/blazor-add-in/excel-blazor-add-in/README.md +++ b/Samples/blazor-add-in/excel-blazor-add-in/README.md @@ -24,6 +24,8 @@ This sample shows how to build an Excel add-in using .NET Blazor technologies. B - Initialize the Office JavaScript API library in Blazor context. - Interact with Excel to manipulate worksheets. - Interact with workbook content through Office JavaScript APIs. +- Interact with methods defined on the Blazor Pages. +- Interop between OfficeJS - JavaScript - C# and back to JavaScript. ## Applies to @@ -38,9 +40,9 @@ This sample shows how to build an Excel add-in using .NET Blazor technologies. B 1. Download or clone the [Office Add-ins samples repository](https://github.com/OfficeDev/Office-Add-in-samples). 1. Open Visual Studio 2022 and open the **Office-Add-in-samples\Samples\blazor-add-in\excel-blazor-add-in\excel-blazor-add-in.sln** solution. (Do **not** open Visual Studio "as administrator". There is a bug that will prevent the add-in from sideloading when Visual Studio is elevated in this way.) 1. Choose **Debug** > **Start Debugging**. Or press F5 to start the solution. -1. When Excel opens, choose **Home** > **Show Taskpane**. - -Next, try out the controls. +1. When Excel opens, choose **Sample Add-in** > **Show task pane** (if not already open). +1. Try out the controls on the task panes. +1. Try out the custom buttons on the **Sample Add-in** tab on the ribbon. ## Understand an Office Add-in in Blazor Context @@ -94,6 +96,9 @@ The fundamental pattern includes the following steps. 1. Use **JSModule.InvokeVoidAsync** to call JavaScript functions from your C# code. 1. Call Office JS APIs to interact with the worksheet from JavaScript code. +### Blazor interop with Add-in Commands +This sample shows how to use Blazor with custom buttons on the ribbon. The buttons call the same functions that are defined on the task pane. This sample is configured to use the shared runtime which is required for this interop to work correctly. + ## Debugging This sample is configured to support debugging both JavaScript and C# files. New Blazor projects need the following file updates to support C# debugging. @@ -131,6 +136,7 @@ Version | Date | Comments ---------| ---------------- | ------------------ 1.0 | May 27, 2022 | Initial release 2.0 | February 1, 2024 | Upgraded to .NET 8 +3.0 | April 18, 2024 | Added Add-in Commands, demo JS and C# Interop from the Ribbon ## Copyright diff --git a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/BlazorAddIn.csproj b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/BlazorAddIn.csproj index d2cb89f59..d6e677998 100644 --- a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/BlazorAddIn.csproj +++ b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/BlazorAddIn.csproj @@ -5,12 +5,13 @@ enable enable 15.0 + true - + - - - + + + diff --git a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/BubbleChart.razor.cs b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/BubbleChart.razor.cs index 01612679b..36aa0aee2 100644 --- a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/BubbleChart.razor.cs +++ b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/BubbleChart.razor.cs @@ -1,18 +1,22 @@ /* Copyright(c) Maarten van Stam. All rights reserved. Licensed under the MIT License. */ - using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices.JavaScript; +using System.Runtime.Versioning; + namespace BlazorAddIn.Pages { /// /// Starter class to demo how to create a bubble chart. /// + [SupportedOSPlatform("browser")] public partial class BubbleChart { - [Inject] - public IJSRuntime JSRuntime { get; set; } = default!; - public IJSObjectReference JSModule { get; set; } = default!; + [Inject, AllowNull] + private IJSRuntime JSRuntime { get; set; } + private IJSObjectReference? JSModule { get; set; } protected override async Task OnAfterRenderAsync(bool firstRender) { @@ -31,8 +35,28 @@ private async Task CreateTable() => /// /// Function to create the actual bubble chart. /// - private async Task CreateBubbleChart() => + private async Task CreateBubbleChart() => await JSModule.InvokeVoidAsync("createBubbleChart"); + + [JSImport("createTable", "BubbleChart")] + internal static partial Task CreateImportedTable(); + + [JSImport("createBubbleChart", "BubbleChart")] + internal static partial Task RunCreateChart(); + + [JSInvokable] + public static async Task CreateBubbles() + { + await JSHost.ImportAsync("BubbleChart", "../Pages/BubbleChart.razor.js"); + await CreateImportedTable(); + await RunCreateChart(); + } + + [JSInvokable] + public static string SayHelloBubble(string name) + { + return $"Hello Bubble, {name} from BubbleChart!"; + } } } diff --git a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/BubbleChart.razor.js b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/BubbleChart.razor.js index d1405bb5c..5c920d57d 100644 --- a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/BubbleChart.razor.js +++ b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/BubbleChart.razor.js @@ -3,6 +3,33 @@ * This bubble chart sample is extracted from the excellent helper tool called Script Lab. * Find this sample and more at https://aka.ms/getscriptlab. */ +console.log("Loading BubbleChart.razor.js"); + +export async function createTable() { + await Excel.run(async (context) => { + context.workbook.worksheets.getItemOrNullObject("Sample").delete(); + const sheet = context.workbook.worksheets.add("Sample"); + + const inventoryTable = sheet.tables.add("A1:D1", true); + inventoryTable.name = "Sales"; + inventoryTable.getHeaderRowRange().values = [["Product", "Inventory", "Price", "Current Market Share"]]; + + inventoryTable.rows.add(null, [ + ["Calamansi", 2000, "$2.45", "10%"], + ["Cara cara orange", 10000, "$2.12", "45%"], + ["Limequat", 4000, "$0.70", "66%"], + ["Meyer lemon", 100, "$2.65", "5%"], + ["Pomelo", 4000, "$1.69", "14%"], + ["Yuzu", 7500, "$3.23", "34%"] + ]); + + sheet.getUsedRange().format.autofitColumns(); + sheet.getUsedRange().format.autofitRows(); + + sheet.activate(); + await context.sync(); + }); +} export async function createBubbleChart() { await Excel.run(async (context) => { @@ -51,28 +78,3 @@ export async function createBubbleChart() { }); } -export async function createTable() { - await Excel.run(async (context) => { - context.workbook.worksheets.getItemOrNullObject("Sample").delete(); - const sheet = context.workbook.worksheets.add("Sample"); - - let inventoryTable = sheet.tables.add("A1:D1", true); - inventoryTable.name = "Sales"; - inventoryTable.getHeaderRowRange().values = [["Product", "Inventory", "Price", "Current Market Share"]]; - - inventoryTable.rows.add(null, [ - ["Calamansi", 2000, "$2.45", "10%"], - ["Cara cara orange", 10000, "$2.12", "45%"], - ["Limequat", 4000, "$0.70", "66%"], - ["Meyer lemon", 100, "$2.65", "5%"], - ["Pomelo", 4000, "$1.69", "14%"], - ["Yuzu", 7500, "$3.23", "34%"] - ]); - - sheet.getUsedRange().format.autofitColumns(); - sheet.getUsedRange().format.autofitRows(); - - sheet.activate(); - await context.sync(); - }); -} \ No newline at end of file diff --git a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/Index.razor.cs b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/Index.razor.cs index 3e86633de..834491514 100644 --- a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/Index.razor.cs +++ b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/Index.razor.cs @@ -3,6 +3,9 @@ using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + namespace BlazorAddIn.Pages { /// @@ -10,14 +13,16 @@ namespace BlazorAddIn.Pages /// public partial class Index { - [Inject] - public IJSRuntime JSRuntime { get; set; } = default!; - public IJSObjectReference JSModule { get; set; } = default!; + [Inject, AllowNull] + private IJSRuntime JSRuntime { get; set; } + private IJSObjectReference JSModule { get; set; } = default!; protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { + Debug.WriteLine("Hit OnAfterRenderAsync in Index.razor.cs!"); + Console.WriteLine("Hit OnAfterRenderAsync in Index.razor.cs in Console!"); JSModule = await JSRuntime.InvokeAsync("import", "./Pages/Index.razor.js"); } } @@ -27,5 +32,11 @@ protected override async Task OnAfterRenderAsync(bool firstRender) /// private async Task HelloButton() => await JSModule.InvokeVoidAsync("helloButton"); + + [JSInvokable] + public static string SayHelloIndex(string name) + { + return $"Hello Index, {name} from Index!"; + } } } diff --git a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/Index.razor.js b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/Index.razor.js index 785e7098a..62eb3f66f 100644 --- a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/Index.razor.js +++ b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/Pages/Index.razor.js @@ -2,10 +2,15 @@ /** * Basic function to show how to insert a value into cell A1 on the selected Excel worksheet. */ +console.log("Loading Index.razor.js"); + export function helloButton() { + console.log("We are now entering function: helloButton"); + return Excel.run(context => { + // Insert text 'Hello world!' into cell A1. context.workbook.worksheets.getActiveWorksheet().getRange("A1").values = [['Hello world!']]; diff --git a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/BlazorAddIn.lib.module.js b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/BlazorAddIn.lib.module.js index 24e42e70e..d79f71fc9 100644 --- a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/BlazorAddIn.lib.module.js +++ b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/BlazorAddIn.lib.module.js @@ -7,6 +7,8 @@ * - Configuring Blazor settings. */ +console.log("Loading BlazorAddin.lib.module.js"); + /** * beforeStart(options, extensions): * @@ -19,6 +21,10 @@ export async function beforeStart(wasmoptions, extensions) { console.log("We are now entering function: beforeStart"); Office.onReady((info) => { + + // Office Finished Loading + console.log("Office onReady."); + // Check that we loaded into Excel. if (info.host === Office.HostType.Excel) { console.log("We are now hosting in Excel."); @@ -26,7 +32,6 @@ export async function beforeStart(wasmoptions, extensions) { else { console.log("We are now hosting in The Browser (of your choice)."); } - console.log("Office onReady."); }); } @@ -38,4 +43,5 @@ export async function beforeStart(wasmoptions, extensions) { */ export async function afterStarted(blazor) { console.log("We are now entering function: afterStarted"); -} \ No newline at end of file +} + diff --git a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/Commands/commands.js b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/Commands/commands.js new file mode 100644 index 000000000..30910be0f --- /dev/null +++ b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/Commands/commands.js @@ -0,0 +1,197 @@ +/* + * Copyright (c) Maarten van Stam. All rights reserved. Licensed under the MIT license. + * See LICENSE in the project root for license information. + * + */ + +console.log("Loading command.js"); + +/** + * Writes the event source id to the document when ExecuteFunction runs. + * @param event {Office.AddinCommands.Event} + */ +async function writeValue(event) { + + console.log("In writeValue"); + + try { + let message = "ExecuteFunction works. Button ID=" + event.source.id; + + await Excel.run(async (context) => { + const range = context.workbook.getSelectedRange(); + range.values = [[message]]; + range.getEntireColumn().format.autofitColumns(); + await context.sync(); + }); + + console.log("writeValue Succeeded"); + + } catch (err) { + await Excel.run(async (context) => { + const range = context.workbook.getSelectedRange(); + const cellRange = range.getCell(0, 0); + cellRange.values = [[err.message]]; + await context.sync(); + }); + console.log(); + console.log("Error call : " + err.message); + } + finally { + console.log("Finish callStaticLocalComponentMethodinit"); + } + + // Calling event.completed is required. event.completed lets the platform know that processing has completed. + event.completed(); +} + +/** + * Calls the JSInvokable function CreateBubbles to create a bubble chart, after adding the data in the active worksheet. + * @param event {Office.AddinCommands.Event} + */ +async function createBubbles(event) { + + console.log("Running createBubbles"); + + // Implement your custom code here. The following code is a simple Excel example. + try { + + // Call JSInvokable Function here ... + await DotNet.invokeMethodAsync("BlazorAddIn", "CreateBubbles"); + + console.log("Finished createBubbles") + + } catch (error) { + // Note: In a production add-in, notify the user through your add-in's UI. + console.error(error); + } + + // Calling event.completed is required. event.completed lets the platform know that processing has completed. + event.completed(); +} + +/** + * Writes the text from the Index Blazor Page to the Worksheet when highlightSelectionIndex runs. + * @param event {Office.AddinCommands.Event} + */ +async function highlightSelectionIndex(event) { + + // Implement your custom code here. The following code is a simple Excel example. + try { + console.log("Running highlightSelectionIndex"); + + console.log("Before callStaticLocalComponentMethodinit"); + await callStaticLocalComponentMethodinit("SayHelloIndex"); + console.log("After callStaticLocalComponentMethodinit"); + + // Used to verify the previous function call, if that fails, this will not run. + // It will be skipped on error and jump into the catch block. + await Excel.run(async (context) => { + const range = context.workbook.getSelectedRange(); + range.format.fill.color = "LightBlue"; + await context.sync(); + }); + + } catch (error) { + // Note: In a production add-in, notify the user through your add-in's UI. + console.error(error); + } + + // Calling event.completed is required. event.completed lets the platform know that processing has completed. + event.completed(); +} + +/** + * Writes the text from the BubbleChart Blazor Page to the Worksheet when highlightSelectionIndex runs. + * @param event {Office.AddinCommands.Event} + */ +async function highlightSelectionBubble(event) { + + // Implement your custom code here. The following code is a simple Excel example. + try { + console.log("Running highlightSelectionBubble"); + + console.log("Before callStaticLocalComponentMethodinit"); + await callStaticLocalComponentMethodinit("SayHelloBubble"); + console.log("After callStaticLocalComponentMethodinit"); + + // Used to verify the previous function call, if that fails, this will not run. + // It will be skipped on error and jump into the catch block. + await Excel.run(async (context) => { + const range = context.workbook.getSelectedRange(); + range.format.fill.color = "LightBlue"; + await context.sync(); + }); + + } catch (error) { + + await Excel.run(async (context) => { + const range = context.workbook.getSelectedRange(); + range.format.fill.color = "Red"; + await context.sync(); + }); + + // Note: In a production add-in, notify the user through your add-in's UI. + console.error(error); + } + + // Calling event.completed is required. event.completed lets the platform know that processing has completed. + event.completed(); +} + +/** + * Local function to call the JSInvokable function in the Blazor Component. + * @param methodname {methodname} + */ +async function callStaticLocalComponentMethodinit(methodname) { + + console.log("In callStaticLocalComponentMethodinit"); + + try { + let name = "Initializing"; + + // Call JSInvokable Function here ... + name = await DotNet.invokeMethodAsync("BlazorAddIn", methodname, "Blazor Fan"); + console.log("Finished Initializing: " + name) + + await Excel.run(async (context) => { + const range = context.workbook.getSelectedRange(); + range.values = [[name]]; + range.getEntireColumn().format.autofitColumns(); + await context.sync(); + }); + + // Used to verify the previous function call, if that fails, this will not run. + // It will be skipped on error and jump into the catch block. + await Excel.run(async (context) => { + const range = context.workbook.getSelectedRange(); + range.format.fill.color = "yellow"; + await context.sync(); + }); + } + catch (err) { + await Excel.run(async (context) => { + const range = context.workbook.getSelectedRange(); + const cellRange = range.getCell(0, 0); + cellRange.values = [[err.message]]; + await context.sync(); + }); + + await Excel.run(async (context) => { + const range = context.workbook.getSelectedRange(); + range.format.fill.color = "red"; + await context.sync(); + }); + + console.log(); + console.log("Error call : " + err.message); + } + finally { + console.log("Finish callStaticLocalComponentMethodinit"); + } +} + +// Associate the functions with their named counterparts in the manifest XML. +Office.actions.associate("writeValue", writeValue); +Office.actions.associate("createBubbles", createBubbles); +Office.actions.associate("highlightSelectionIndex", highlightSelectionIndex); +Office.actions.associate("highlightSelectionBubble", highlightSelectionBubble); \ No newline at end of file diff --git a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/index.html b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/index.html index 16f5e4a8f..b0b1867e6 100644 --- a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/index.html +++ b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-add-in/wwwroot/index.html @@ -8,18 +8,19 @@ Blazor Excel add-in - + - + - + + @@ -58,6 +59,8 @@

Loading...

+ + diff --git a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-sideloader/excel-blazor-sideloader.csproj.user b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-sideloader/excel-blazor-sideloader.csproj.user index b93214cca..5c651b943 100644 --- a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-sideloader/excel-blazor-sideloader.csproj.user +++ b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-sideloader/excel-blazor-sideloader.csproj.user @@ -3,5 +3,13 @@ https://localhost:7287/ {wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri} + ShowAllFiles + c4ff1608-2872-4976-bfbe-36a1100b47e2 + ff9b7763-2e6e-45cb-a0e6-3b4375c4bc76 + {981cac76-6e01-4f68-a45b-1f4166ce176c} + {7ec6e44b-2635-4c5b-94b8-e8e52b0fe0c0} + {3d0e8b92-853a-47d1-b3da-e39faadd1a3a} + 1.0.0.0 + JQH0lynzkXtzxYCHMCCnPKqBhLuEGirA3ah9wzcUeTk= \ No newline at end of file diff --git a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-sideloader/excel-blazor-sideloaderManifest/excel-blazor-sideloader.xml b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-sideloader/excel-blazor-sideloaderManifest/excel-blazor-sideloader.xml index 945050405..731e02bc2 100644 --- a/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-sideloader/excel-blazor-sideloaderManifest/excel-blazor-sideloader.xml +++ b/Samples/blazor-add-in/excel-blazor-add-in/excel-blazor-sideloader/excel-blazor-sideloaderManifest/excel-blazor-sideloader.xml @@ -1,9 +1,9 @@  - @@ -13,7 +13,7 @@ 4aeb7f5e-a283-4a08-83f7-5e7271e4fa2a - 2.0.0.0 + 3.0.0.0 Contoso en-US @@ -30,11 +30,19 @@ AppDomain3 - + + + + + + + + + @@ -44,17 +52,21 @@ - + + + + + @@ -64,14 +76,15 @@ <!-- Points to a URL resource which details how the add-in should be used. --> <LearnMoreUrl resid="Contoso.GetStarted.LearnMoreUrl"/> </GetStarted> + <!-- Function file is a HTML page that includes the JavaScript where functions for ExecuteAction will be called. Think of the FunctionFile as the code behind ExecuteFunction. --> - <FunctionFile resid="Contoso.DesktopFunctionFile.Url" /> + <FunctionFile resid="Contoso.Shared.Url" /> <!-- PrimaryCommandSurface is the main Office Ribbon. --> <ExtensionPoint xsi:type="PrimaryCommandSurface"> <!-- Use OfficeTab to extend an existing Tab. Use CustomTab to create a new tab. --> - <OfficeTab id="TabHome"> + <CustomTab id="AddinCommandsTab"> <!-- Ensure you provide a unique id for the group. Recommendation for any IDs is to namespace using your company name. --> <Group id="Contoso.Group1"> <!-- Label for your group. resid must point to a ShortString resource. --> @@ -101,18 +114,128 @@ <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> <Action xsi:type="ShowTaskpane"> - <TaskpaneId>ButtonId1</TaskpaneId> + <!-- Taskpane prepared to be activated as AutoOpen Taskpane --> + <!-- See: Samples/office-add-in-commands/auto-open-task-pane --> + <TaskpaneId>Office.AutoShowTaskpaneWithDocument</TaskpaneId> + <SourceLocation resid="Contoso.Shared.Url" /> + <!--<TaskpaneId>SampleAddinTaskpaneId</TaskpaneId>--> <!-- Provide a URL resource id for the location that will be displayed on the task pane. --> - <SourceLocation resid="Contoso.Taskpane.Url" /> </Action> </Control> </Group> - </OfficeTab> + + <Group id="Contoso.Group2"> + <!-- Label for your group. resid must point to a ShortString resource. --> + <Label resid="Contoso.Group2Label" /> + <!-- Icons. Required sizes 16,32,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX. --> + <!-- Use PNG icons. All URLs on the resources section must use HTTPS. --> + <Icon> + <bt:Image size="16" resid="Contoso.tpicon_16x16" /> + <bt:Image size="32" resid="Contoso.tpicon_32x32" /> + <bt:Image size="80" resid="Contoso.tpicon_80x80" /> + </Icon> + + <!-- Second Button Section --> + + <Control xsi:type="Button" id="Contoso.FunctionButton1"> + <Label resid="Contoso.FunctionButton1.Label" /> + <Supertip> + <!-- ToolTip title. resid must point to a ShortString resource. --> + <Title resid="Contoso.FunctionButton1.Label" /> + <!-- ToolTip description. resid must point to a LongString resource. --> + <Description resid="Contoso.FunctionButton1.Tooltip" /> + </Supertip> + <Icon> + <bt:Image size="16" resid="Contoso.tpicon_16x16" /> + <bt:Image size="32" resid="Contoso.tpicon_32x32" /> + <bt:Image size="80" resid="Contoso.tpicon_80x80" /> + </Icon> + + <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> + <Action xsi:type="ExecuteFunction"> + <FunctionName>highlightSelectionIndex</FunctionName> + </Action> + </Control> + + <Control xsi:type="Button" id="Contoso.FunctionButton2"> + <Label resid="Contoso.FunctionButton2.Label" /> + <Supertip> + <!-- ToolTip title. resid must point to a ShortString resource. --> + <Title resid="Contoso.FunctionButton2.Label" /> + <!-- ToolTip description. resid must point to a LongString resource. --> + <Description resid="Contoso.FunctionButton2.Tooltip" /> + </Supertip> + <Icon> + <bt:Image size="16" resid="Contoso.tpicon_16x16" /> + <bt:Image size="32" resid="Contoso.tpicon_32x32" /> + <bt:Image size="80" resid="Contoso.tpicon_80x80" /> + </Icon> + + <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> + <Action xsi:type="ExecuteFunction"> + <FunctionName>highlightSelectionBubble</FunctionName> + </Action> + </Control> + + <Control xsi:type="Button" id="Contoso.FunctionButton3"> + <Label resid="Contoso.FunctionButton3.Label" /> + <Supertip> + <!-- ToolTip title. resid must point to a ShortString resource. --> + <Title resid="Contoso.FunctionButton3.Label" /> + <!-- ToolTip description. resid must point to a LongString resource. --> + <Description resid="Contoso.FunctionButton3.Tooltip" /> + </Supertip> + <Icon> + <bt:Image size="16" resid="Contoso.tpicon_16x16" /> + <bt:Image size="32" resid="Contoso.tpicon_32x32" /> + <bt:Image size="80" resid="Contoso.tpicon_80x80" /> + </Icon> + + <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> + <Action xsi:type="ExecuteFunction"> + <FunctionName>writeValue</FunctionName> + </Action> + </Control> + </Group> + + <Group id="Contoso.Group3"> + <!-- Label for your group. resid must point to a ShortString resource. --> + <Label resid="Contoso.Group3Label" /> + <!-- Icons. Required sizes 16,32,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX. --> + <!-- Use PNG icons. All URLs on the resources section must use HTTPS. --> + <Icon> + <bt:Image size="16" resid="Contoso.tpicon_16x16" /> + <bt:Image size="32" resid="Contoso.tpicon_32x32" /> + <bt:Image size="80" resid="Contoso.tpicon_80x80" /> + </Icon> + + <Control xsi:type="Button" id="Contoso.FunctionButton4"> + <Label resid="Contoso.FunctionButton4.Label" /> + <Supertip> + <!-- ToolTip title. resid must point to a ShortString resource. --> + <Title resid="Contoso.FunctionButton4.Label" /> + <!-- ToolTip description. resid must point to a LongString resource. --> + <Description resid="Contoso.FunctionButton4.Tooltip" /> + </Supertip> + <Icon> + <bt:Image size="16" resid="Contoso.tpicon_16x16" /> + <bt:Image size="32" resid="Contoso.tpicon_32x32" /> + <bt:Image size="80" resid="Contoso.tpicon_80x80" /> + </Icon> + + <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> + <Action xsi:type="ExecuteFunction"> + <FunctionName>createBubbles</FunctionName> + </Action> + </Control> + </Group> + <Label resid="Contoso.CustomTabLabel"/> + </CustomTab> </ExtensionPoint> </DesktopFormFactor> </Host> </Hosts> - + <!-- You can use resources across hosts and form factors. --> <Resources> <bt:Images> @@ -121,21 +244,34 @@ <bt:Image id="Contoso.tpicon_80x80" DefaultValue="~remoteAppUrl/Images/Button80x80.png" /> </bt:Images> <bt:Urls> - <bt:Url id="Contoso.DesktopFunctionFile.Url" DefaultValue="~remoteAppUrl/Functions/FunctionFile.html" /> - <bt:Url id="Contoso.Taskpane.Url" DefaultValue="~remoteAppUrl/" /> + <bt:Url id="Contoso.Shared.Url" DefaultValue="~remoteAppUrl/" /> <bt:Url id="Contoso.GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> </bt:Urls> + <!-- ShortStrings max characters==125. --> <bt:ShortStrings> - <bt:String id="Contoso.TaskpaneButton.Label" DefaultValue="Show Taskpane" /> - <bt:String id="Contoso.Group1Label" DefaultValue="Commands Group" /> <bt:String id="Contoso.GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> + <bt:String id="Contoso.CustomTabLabel" DefaultValue="Sample Add-in" /> + <bt:String id="Contoso.Group1Label" DefaultValue="TaskPane" /> + <bt:String id="Contoso.Group2Label" DefaultValue="Texts" /> + <bt:String id="Contoso.Group3Label" DefaultValue="Bubbles" /> + <bt:String id="Contoso.TaskpaneButton.Label" DefaultValue="Show Task Pane" /> + <bt:String id="Contoso.FunctionButton1.Label" DefaultValue="Insert Name From Index" /> + <bt:String id="Contoso.FunctionButton2.Label" DefaultValue="Insert Name From Bubble" /> + <bt:String id="Contoso.FunctionButton3.Label" DefaultValue="Write JS" /> + <bt:String id="Contoso.FunctionButton4.Label" DefaultValue="Bubbles" /> </bt:ShortStrings> + <!-- LongStrings max characters==250. --> <bt:LongStrings> <bt:String id="Contoso.TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> - <bt:String id="Contoso.GetStarted.Description" DefaultValue="Your sample add-in loaded succesfully. Go to the HOME tab and click the 'Show Taskpane' button to get started." /> + <bt:String id="Contoso.FunctionButton1.Tooltip" DefaultValue="Click to insert text from Index Page" /> + <bt:String id="Contoso.FunctionButton3.Tooltip" DefaultValue="Click to insert text from BubbleChart Page" /> + <bt:String id="Contoso.FunctionButton2.Tooltip" DefaultValue="Click to insert text from commands.js" /> + <bt:String id="Contoso.FunctionButton4.Tooltip" DefaultValue="Click to Run CreateBubbles from BubbleChart Page" /> + <bt:String id="Contoso.GetStarted.Description" DefaultValue="Your sample add-in loaded succesfully. Go to the Sample Add-in tab and click the 'Show Task Pane' button to get started." /> </bt:LongStrings> + </Resources> </VersionOverrides> <!-- End Add-in Commands Mode integration. --> diff --git a/Samples/blazor-add-in/word-blazor-add-in/README.md b/Samples/blazor-add-in/word-blazor-add-in/README.md index 1d10a78c1..4948dcc60 100644 --- a/Samples/blazor-add-in/word-blazor-add-in/README.md +++ b/Samples/blazor-add-in/word-blazor-add-in/README.md @@ -24,6 +24,8 @@ This sample shows how to build a Word add-in using .NET Blazor technologies. Bla - Initialize the Office JavaScript API library in Blazor context. - Interact with Word to manipulate paragraphs and content controls. - Interact with document content through Office JavaScript APIs. +- Interact with methods defined on the Blazor Pages. +- Interop between OfficeJS - JavaScript - C# and back to JavaScript. ## Applies to @@ -38,9 +40,9 @@ This sample shows how to build a Word add-in using .NET Blazor technologies. Bla 1. Download or clone the [Office Add-ins samples repository](https://github.com/OfficeDev/Office-Add-in-samples). 1. Open Visual Studio 2022 and open the: **Office-Add-in-samples\Samples\blazor-add-in\word-blazor-add-in\word-blazor-add-in.sln** solution. 1. Choose **Debug** > **Start Debugging**. Or press F5 to start the solution. -1. When Word opens, choose **Home** > **Show Taskpane**. - -Next, try out the controls. +1. When Word opens, choose **Sample Add-in** > **Show task pane** (if not already open). +1. Try out the controls on the task panes. +1. Try out the custom buttons on the **Sample Add-in** tab on the ribbon. ## Understand an Office Add-in in Blazor Context @@ -97,6 +99,9 @@ The fundamental pattern includes the following steps. 1. Use **JSModule.InvokeVoidAsync** to call JavaScript functions from your C# code. 1. Call Office JS APIs to interact with the document from JavaScript code. +### Blazor interop with Add-in Commands +This sample shows how to use Blazor with custom buttons on the ribbon. The buttons call the same functions that are defined on the task pane. This sample is configured to use the shared runtime which is required for this interop to work correctly. + ## Debugging This sample is configured to support debugging both JavaScript and C# files. New Blazor projects need the following file updates to support C# debugging. @@ -134,6 +139,7 @@ This sample is configured to support debugging both JavaScript and C# files. New | ------- | ---------------- | ------------------ | | 1.0 | April 25, 2022 | Initial release | | 2.0 | February 1, 2024 | Upgraded to .NET 8 | +| 3.0 | April 18, 2024 | Added Add-in Commands, demo JS and C# Interop from the Ribbon | ## Copyright diff --git a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/BlazorAddIn.csproj b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/BlazorAddIn.csproj index e80ab4b25..6e73a4c9c 100644 --- a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/BlazorAddIn.csproj +++ b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/BlazorAddIn.csproj @@ -5,16 +5,13 @@ <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> <TargetOfficeVersion>15.0</TargetOfficeVersion> + <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <ItemGroup> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.1" /> - <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.1" PrivateAssets="all" /> - <PackageReference Include="Microsoft.JSInterop.WebAssembly" Version="8.0.1" /> - </ItemGroup> - - <ItemGroup> - <Folder Include="wwwroot\scripts\" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.4" /> + <PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.4" PrivateAssets="all" /> + <PackageReference Include="Microsoft.JSInterop.WebAssembly" Version="8.0.4" /> </ItemGroup> </Project> diff --git a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/Pages/Index.razor.cs b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/Pages/Index.razor.cs index 4e7fa990e..cecf133de 100644 --- a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/Pages/Index.razor.cs +++ b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/Pages/Index.razor.cs @@ -1,13 +1,17 @@ /* Copyright(c) Maarten van Stam. All rights reserved. Licensed under the MIT License. */ - using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices.JavaScript; +using System.Runtime.Versioning; + namespace BlazorAddIn.Pages { + [SupportedOSPlatform("browser")] public partial class Index { - [Inject] + [Inject, AllowNull] public IJSRuntime JSRuntime { get; set; } = default!; public IJSObjectReference JSModule { get; set; } = default!; @@ -61,5 +65,27 @@ internal async Task DeRegisterContentControlEvents() => /// </summary> internal async Task DeleteContentControl() => await JSModule.InvokeVoidAsync("deleteContentControl"); + + [JSImport("setupDocument", "Index")] + internal static partial Task SetupDocument(); + + [JSImport("insertContentControls", "Index")] + internal static partial Task InsertContentControlsFunction(); + + [JSImport("tagContentControls", "Index")] + internal static partial Task TagContentControlsFunction(); + + [JSImport("modifyContentControls", "Index")] + internal static partial Task ModifyContentControlsFunction(); + + [JSInvokable] + public static async Task PrepareDocument() + { + await JSHost.ImportAsync("Index", "../Pages/Index.razor.js"); + await SetupDocument(); + await InsertContentControlsFunction(); + await TagContentControlsFunction(); + await ModifyContentControlsFunction(); + } } } \ No newline at end of file diff --git a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/Program.cs b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/Program.cs index 1625b92e2..de58cc606 100644 --- a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/Program.cs +++ b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/Program.cs @@ -1,4 +1,5 @@ using BlazorAddIn; + using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; diff --git a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/BlazorAddIn.lib.module.js b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/BlazorAddIn.lib.module.js index 704b31655..6cdcc61f1 100644 --- a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/BlazorAddIn.lib.module.js +++ b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/BlazorAddIn.lib.module.js @@ -6,7 +6,9 @@ * - Initializing libraries before Blazor starts up. * - Configuring Blazor settings. */ - + +console.log("Loading BlazorAddin.lib.module.js"); + /** * beforeStart(options, extensions): * @@ -19,6 +21,10 @@ export async function beforeStart(wasmoptions, extensions) { console.log("We are now entering function: beforeStart"); Office.onReady((info) => { + + // Office Finished Loading + console.log("Office onReady."); + // Check that we loaded into Word if (info.host === Office.HostType.Word) { console.log("We are now hosting in Word"); @@ -26,7 +32,6 @@ export async function beforeStart(wasmoptions, extensions) { else { console.log("We are now hosting in The Browser (of your choice)"); } - console.log("Office onReady"); }); } diff --git a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/Commands/commands.js b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/Commands/commands.js new file mode 100644 index 000000000..25e6ffba7 --- /dev/null +++ b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/Commands/commands.js @@ -0,0 +1,28 @@ +/* + * Copyright (c) Maarten van Stam. All rights reserved. Licensed under the MIT license. + * See LICENSE in the project root for license information. + * + */ + +async function prepareDocument(event) { + + console.log("Running prepareDocument"); + + // Implement your custom code here. The following code is a simple Word example. + try { + + // Call JSInvokable Function here ... + await DotNet.invokeMethodAsync("BlazorAddIn", "PrepareDocument"); + + console.log("Finished prepareDocument") + + } catch (error) { + // Note: In a production add-in, notify the user through your add-in's UI. + console.error(error); + } + + // Calling event.completed is required. event.completed lets the platform know that processing has completed. + event.completed(); +} + +Office.actions.associate("prepareDocument", prepareDocument); \ No newline at end of file diff --git a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/index.html b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/index.html index e257a26b7..2295e4f4f 100644 --- a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/index.html +++ b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-add-in/wwwroot/index.html @@ -5,28 +5,27 @@ <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> - <title>BlazorAddIn + Blazor Word add-in - + - - + + + - - - + +
@@ -60,6 +59,8 @@

Loading...

+ + diff --git a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-sideloader/word-blazor-sideloader.csproj.user b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-sideloader/word-blazor-sideloader.csproj.user index 8577ecf1d..84cc5ee35 100644 --- a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-sideloader/word-blazor-sideloader.csproj.user +++ b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-sideloader/word-blazor-sideloader.csproj.user @@ -2,6 +2,6 @@ https://localhost:7096/ - {wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri} + {wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri} \ No newline at end of file diff --git a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-sideloader/word-blazor-sideloaderManifest/word-blazor-sideloader.xml b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-sideloader/word-blazor-sideloaderManifest/word-blazor-sideloader.xml index 65b55b55f..f9a89f6ed 100644 --- a/Samples/blazor-add-in/word-blazor-add-in/word-blazor-sideloader/word-blazor-sideloaderManifest/word-blazor-sideloader.xml +++ b/Samples/blazor-add-in/word-blazor-add-in/word-blazor-sideloader/word-blazor-sideloaderManifest/word-blazor-sideloader.xml @@ -7,133 +7,188 @@ xmlns:ov="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="TaskPaneApp"> - - - - 303da7ed-183e-412e-9234-8e80cff67ace - - - 2.0.0.0 - Contoso - en-US - - - - - - - - - - AppDomain1 - AppDomain2 - AppDomain3 - - - - - - - - - - - - - ReadWriteDocument - - - - - + + + + 303da7ed-183e-412e-9234-8e80cff67ace + + + 3.0.0.0 + Contoso + en-US + + + + + + + + + + AppDomain1 + AppDomain2 + AppDomain3 + + + + - - - - - - - - - - - - <!-- Description of the Getting Started callout. resid points to a LongString resource --> - <Description resid="Contoso.GetStarted.Description"/> - - <!-- Points to a URL resource which details how the add-in should be used. --> - <LearnMoreUrl resid="Contoso.GetStarted.LearnMoreUrl"/> - </GetStarted> - - <!-- PrimaryCommandSurface is the main Office Ribbon. --> - <ExtensionPoint xsi:type="PrimaryCommandSurface"> - <!-- Use OfficeTab to extend an existing Tab. Use CustomTab to create a new tab. --> - <OfficeTab id="TabHome"> - <!-- Ensure you provide a unique id for the group. Recommendation for any IDs is to namespace using your company name. --> - <Group id="Contoso.Group1"> - <!-- Label for your group. resid must point to a ShortString resource. --> - <Label resid="Contoso.Group1Label" /> - <!-- Icons. Required sizes 16,32,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX. --> - <!-- Use PNG icons. All URLs on the resources section must use HTTPS. --> - <Icon> - <bt:Image size="16" resid="Contoso.tpicon_16x16" /> - <bt:Image size="32" resid="Contoso.tpicon_32x32" /> - <bt:Image size="80" resid="Contoso.tpicon_80x80" /> - </Icon> - - <!-- Control. It can be of type "Button" or "Menu". --> - <Control xsi:type="Button" id="Contoso.TaskpaneButton"> - <Label resid="Contoso.TaskpaneButton.Label" /> - <Supertip> - <!-- ToolTip title. resid must point to a ShortString resource. --> - <Title resid="Contoso.TaskpaneButton.Label" /> - <!-- ToolTip description. resid must point to a LongString resource. --> - <Description resid="Contoso.TaskpaneButton.Tooltip" /> - </Supertip> - <Icon> - <bt:Image size="16" resid="Contoso.tpicon_16x16" /> - <bt:Image size="32" resid="Contoso.tpicon_32x32" /> - <bt:Image size="80" resid="Contoso.tpicon_80x80" /> - </Icon> - - <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> - <Action xsi:type="ShowTaskpane"> - <TaskpaneId>ButtonId1</TaskpaneId> - <!-- Provide a URL resource id for the location that will be displayed on the task pane. --> - <SourceLocation resid="Contoso.Taskpane.Url" /> - </Action> - </Control> - </Group> - </OfficeTab> - </ExtensionPoint> - </DesktopFormFactor> - </Host> + <Host Name="Document" /> </Hosts> + <Requirements> + <Sets DefaultMinVersion="1.1"> + <Set Name="SharedRuntime" MinVersion="1.1"/> + <Set Name="WordApi" MinVersion="1.7"/> + <Set Name="DialogApi" MinVersion="1.2"/> + <Set Name="AddinCommands" MinVersion ="1.1"/> + </Sets> + </Requirements> + <DefaultSettings> + <SourceLocation DefaultValue="~remoteAppUrl/" /> + </DefaultSettings> + <!-- End TaskPane Mode integration. --> + + <Permissions>ReadWriteDocument</Permissions> + + <!-- Begin Add-in Commands Mode integration. --> + <VersionOverrides xmlns="http://schemas.microsoft.com/office/taskpaneappversionoverrides" xsi:type="VersionOverridesV1_0"> + + <!-- The Hosts node is required. --> + <Hosts> + <!-- Each host can have a different set of commands. --> + <!-- Excel host is Workbook, Word host is Document, and PowerPoint host is Presentation. --> + <!-- Make sure the hosts you override match the hosts declared in the top section of the manifest. --> + <Host xsi:type="Document"> + <Runtimes> + <Runtime resid="Contoso.Shared.Url" lifetime="long" /> + </Runtimes> + <!-- Form factor. Currently only DesktopFormFactor is supported. --> + <DesktopFormFactor> + <!--"This code enables a customizable message to be displayed when the add-in is loaded successfully upon individual install."--> + <GetStarted> + <!-- Title of the Getting Started callout. The resid attribute points to a ShortString resource --> + <Title resid="Contoso.GetStarted.Title"/> + + <!-- Description of the Getting Started callout. resid points to a LongString resource --> + <Description resid="Contoso.GetStarted.Description"/> + + <!-- Points to a URL resource which details how the add-in should be used. --> + <LearnMoreUrl resid="Contoso.GetStarted.LearnMoreUrl"/> + </GetStarted> + + <!-- Function file is a HTML page that includes the JavaScript where functions for ExecuteAction will be called. + Think of the FunctionFile as the code behind ExecuteFunction. --> + <FunctionFile resid="Contoso.Shared.Url" /> + + <!-- PrimaryCommandSurface is the main Office Ribbon. --> + <ExtensionPoint xsi:type="PrimaryCommandSurface"> + <!-- Use OfficeTab to extend an existing Tab. Use CustomTab to create a new tab. --> + <CustomTab id="AddinCommandsTab"> + <!-- Ensure you provide a unique id for the group. Recommendation for any IDs is to namespace using your company name. --> + <Group id="Contoso.Group1"> + <!-- Label for your group. resid must point to a ShortString resource. --> + <Label resid="Contoso.Group1Label" /> + <!-- Icons. Required sizes 16,32,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX. --> + <!-- Use PNG icons. All URLs on the resources section must use HTTPS. --> + <Icon> + <bt:Image size="16" resid="Contoso.tpicon_16x16" /> + <bt:Image size="32" resid="Contoso.tpicon_32x32" /> + <bt:Image size="80" resid="Contoso.tpicon_80x80" /> + </Icon> + + <!-- Control. It can be of type "Button" or "Menu". --> + <Control xsi:type="Button" id="Contoso.TaskpaneButton"> + <Label resid="Contoso.TaskpaneButton.Label" /> + <Supertip> + <!-- ToolTip title. resid must point to a ShortString resource. --> + <Title resid="Contoso.TaskpaneButton.Label" /> + <!-- ToolTip description. resid must point to a LongString resource. --> + <Description resid="Contoso.TaskpaneButton.Tooltip" /> + </Supertip> + <Icon> + <bt:Image size="16" resid="Contoso.tpicon_16x16" /> + <bt:Image size="32" resid="Contoso.tpicon_32x32" /> + <bt:Image size="80" resid="Contoso.tpicon_80x80" /> + </Icon> + + <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> + <Action xsi:type="ShowTaskpane"> + <TaskpaneId>ButtonId1</TaskpaneId> + <!-- Provide a URL resource id for the location that will be displayed on the task pane. --> + <SourceLocation resid="Contoso.Shared.Url" /> + </Action> + </Control> + </Group> + + <Group id="Contoso.Group2"> + <!-- Label for your group. resid must point to a ShortString resource. --> + <Label resid="Contoso.Group2Label" /> + <!-- Icons. Required sizes 16,32,80, optional 20, 24, 40, 48, 64. Strongly recommended to provide all sizes for great UX. --> + <!-- Use PNG icons. All URLs on the resources section must use HTTPS. --> + <Icon> + <bt:Image size="16" resid="Contoso.tpicon_16x16" /> + <bt:Image size="32" resid="Contoso.tpicon_32x32" /> + <bt:Image size="80" resid="Contoso.tpicon_80x80" /> + </Icon> + <!-- Second Button Section --> + + <Control xsi:type="Button" id="Contoso.FunctionButton1"> + <Label resid="Contoso.FunctionButton1.Label" /> + <Supertip> + <!-- ToolTip title. resid must point to a ShortString resource. --> + <Title resid="Contoso.FunctionButton1.Label" /> + <!-- ToolTip description. resid must point to a LongString resource. --> + <Description resid="Contoso.FunctionButton1.Tooltip" /> + </Supertip> + <Icon> + <bt:Image size="16" resid="Contoso.tpicon_16x16" /> + <bt:Image size="32" resid="Contoso.tpicon_32x32" /> + <bt:Image size="80" resid="Contoso.tpicon_80x80" /> + </Icon> + + <!-- This is what happens when the command is triggered (E.g. click on the Ribbon). Supported actions are ExecuteFunction or ShowTaskpane. --> + <Action xsi:type="ExecuteFunction"> + <FunctionName>prepareDocument</FunctionName> + </Action> + </Control> + </Group> + <Label resid="Contoso.CustomTabLabel"/> + </CustomTab> + </ExtensionPoint> + </DesktopFormFactor> + </Host> + </Hosts> + + <!-- You can use resources across hosts and form factors. --> + <Resources> + <bt:Images> + <bt:Image id="Contoso.tpicon_16x16" DefaultValue="~remoteAppUrl/Images/Button16x16.png" /> + <bt:Image id="Contoso.tpicon_32x32" DefaultValue="~remoteAppUrl/Images/Button32x32.png" /> + <bt:Image id="Contoso.tpicon_80x80" DefaultValue="~remoteAppUrl/Images/Button80x80.png" /> + </bt:Images> + <bt:Urls> + <!--<bt:Url id="Contoso.Taskpane.Url" DefaultValue="~remoteAppUrl/" />--> + <bt:Url id="Contoso.Shared.Url" DefaultValue="~remoteAppUrl/" /> + <bt:Url id="Contoso.GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> + </bt:Urls> + <!-- ShortStrings max characters==125. --> + <bt:ShortStrings> + <bt:String id="Contoso.TaskpaneButton.Label" DefaultValue="Show Taskpane" /> + <bt:String id="Contoso.FunctionButton1.Label" DefaultValue="Prepare Document" /> + <bt:String id="Contoso.Group1Label" DefaultValue="TaskPane" /> + <bt:String id="Contoso.Group2Label" DefaultValue="Content Controls" /> + <bt:String id="Contoso.CustomTabLabel" DefaultValue="Sample Add-in" /> + <bt:String id="Contoso.GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> + </bt:ShortStrings> + <!-- LongStrings max characters==250. --> + <bt:LongStrings> + <bt:String id="Contoso.TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> + <bt:String id="Contoso.FunctionButton1.Tooltip" DefaultValue="Click to Run a Function" /> + <bt:String id="Contoso.GetStarted.Description" DefaultValue="Your sample add-in loaded succesfully. Go to the HOME tab and click the 'Show Taskpane' button to get started." /> + </bt:LongStrings> - <!-- You can use resources across hosts and form factors. --> - <Resources> - <bt:Images> - <bt:Image id="Contoso.tpicon_16x16" DefaultValue="~remoteAppUrl/Images/Button16x16.png" /> - <bt:Image id="Contoso.tpicon_32x32" DefaultValue="~remoteAppUrl/Images/Button32x32.png" /> - <bt:Image id="Contoso.tpicon_80x80" DefaultValue="~remoteAppUrl/Images/Button80x80.png" /> - </bt:Images> - <bt:Urls> - <bt:Url id="Contoso.Taskpane.Url" DefaultValue="~remoteAppUrl/" /> - <bt:Url id="Contoso.GetStarted.LearnMoreUrl" DefaultValue="https://go.microsoft.com/fwlink/?LinkId=276812" /> - </bt:Urls> - <!-- ShortStrings max characters==125. --> - <bt:ShortStrings> - <bt:String id="Contoso.TaskpaneButton.Label" DefaultValue="Show Taskpane" /> - <bt:String id="Contoso.Group1Label" DefaultValue="Commands Group" /> - <bt:String id="Contoso.GetStarted.Title" DefaultValue="Get started with your sample add-in!" /> - </bt:ShortStrings> - <!-- LongStrings max characters==250. --> - <bt:LongStrings> - <bt:String id="Contoso.TaskpaneButton.Tooltip" DefaultValue="Click to Show a Taskpane" /> - <bt:String id="Contoso.GetStarted.Description" DefaultValue="Your sample add-in loaded succesfully. Go to the HOME tab and click the 'Show Taskpane' button to get started." /> - </bt:LongStrings> - </Resources> - </VersionOverrides> - <!-- End Add-in Commands Mode integration. --> + </Resources> + </VersionOverrides> + <!-- End Add-in Commands Mode integration. --> </OfficeApp>