By creating a Razor component using Interop JavaScript, the TX Text Control Document Editor can be integrated and initialized in Blazor. This sample uses the JSRuntime and DotNetObjectReference classes to communicate between the editor and the server-side ServerTextControl class in .NET code.

Applications built with TX Text Control in Blazor require some modifications compared to older .NET versions due to the many changes in .NET 8. The ASP.NET Core Hosted option has been removed and replaced with a new Blazor project template. Prior to .NET 8, the entire Blazor application would run in either Blazor WASM (Web Assembly) or Blazor Server. NET 8 gives you the ability to control the render mode on a per-component basis. This is a huge benefit because you can now use the TX Text Control, which requires server-side rendering, with WASM applications.

There are 4 render modes available for .NET 8 components:

  • Static server-side
    Requests are routed to a Razor Component which returns static HTML.
  • Interactive Server
    Components are more interactive when run through Blazor Server. Interactive DOM modification is dynamic.
  • Interactive WASM
    The component will be executed on the client side (in the browser) and not on the server side.
  • Interactive Auto
    The component uses the Blazor Server for the initial request and then uses the Blazor WASM for subsequent requests.

TX Text Control requires server-side rendering and an update after creation, so this example uses Interactive Server mode.

Data Flow

The diagram below is an illustration of the data flow in this concept. The Text Control Razor component dynamically adds the TX Text Control Document Editor to the page by initializing it with JavaScript. The JSRuntime is used to create an object reference to the created JavaScript file. This object reference calls the JavaScript API of the TX Text Control.

TX Text Control in Blazor

Creating the Application

Make sure that you downloaded the latest version of Visual Studio 2022 that comes with the .NET 8 SDK.

  1. In Visual Studio 2022, create a new project by choosing Create a new project.

  2. Select Blazor Web App as the project template and confirm with Next.

    Creating the .NET 8 project

  3. Choose a name for your project and confirm with Next.

  4. In the next dialog, choose .NET 8 (Long-term support) as the Framework. Select Server as the Interactive render mode and choose Per page/component as the Interactivity location. Confirm with Create.

    Creating the .NET 8 project

Adding the NuGet Package

  1. In the Solution Explorer, select your created project and choose Manage NuGet Packages... from the Project main menu.

    Package Source

    Select either Text Control Offline Packages or nuget.org as the Package source. Packages in the official Text Control NuGet profile are frequently updated.

    Browse and install the following packages:

    • TXTextControl.Web
    • TXTextControl.TextControl.ASP.SDK

    ASP.NET Core Web Application

Configure the Application

  1. Open the Program.cs file located in the project's root folder.

    At the very top of the file, insert the following code:

    using TXTextControl.Web;
    view raw test.cs hosted with ❤ by GitHub

    Add the following code after the entry app.UseStaticFiles();:

    // enable Web Sockets
    app.UseWebSockets();
    // attach the Text Control WebSocketHandler middleware
    app.UseTXWebSocketMiddleware();
    view raw test.cs hosted with ❤ by GitHub

Creating the Interop JavaScript

  1. Create a new folder scripts in your wwwroot folder, create a new JavaScript file and name it textcontrol.js. Paste the following content into the file you have just created.

    var dotNetObject;
    export async function addEditorToElement(dotNetRef, options) {
    dotNetObject = dotNetRef;
    TXTextControl.init({
    containerID: "txDocumentEditorContainer",
    webSocketURL: options.websocketurl
    });
    }
    export function saveDocument() {
    // save the document on TXTextControl object
    TXTextControl.saveDocument(TXTextControl.StreamType.InternalUnicodeFormat, function (document) {
    // call the .NET method 'ProcessDocument' with the saved document data
    dotNetObject.invokeMethodAsync('ProcessDocument', document.data);
    });
    };
    export function loadDocument(document) {
    // load the document back into the editor (TXTextControl)
    TXTextControl.loadDocument(TXTextControl.StreamType.InternalUnicodeFormat, document);
    };
    export function insertTable() {
    TXTextControl.tables.add(5, 5, 10, function (e) {
    if (e === true) { // if added
    TXTextControl.tables.getItem(function (table) {
    table.cells.forEach(function (cell) {
    cell.setText("Cell text");
    });
    }, null, 10);
    }
    })
    };
    view raw test.js hosted with ❤ by GitHub

Creating the Razor Component

This step creates a new Razor component named TextControl.razor that initializes both the .NET object reference and the JavaScript object reference.

  1. Select the project in the Solution Explorer and choose New Item... from the Project main menu. Select Razor Component, name it TextControl.razor and confirm with Add.

  2. Paste the following code into the newly created file:

    @using Microsoft.AspNetCore.Components.Web
    @using Microsoft.JSInterop
    @inject IJSRuntime JsRuntime
    @inject NavigationManager Navigator
    <script src="@WebSocketURL/api/TXWebSocket/GetResource?name=tx-document-editor.min.js"></script>
    <div id="txDocumentEditorContainer" style="width: @Width; height: @Height;"></div>
    @code
    {
    protected override Task OnInitializedAsync()
    {
    return base.OnInitializedAsync();
    }
    [Parameter]
    public string? WebSocketURL { get; set; }
    [Parameter]
    public string? ContainerID { get; set; }
    [Parameter]
    public string? Width { get; set; } = "800px";
    [Parameter]
    public string? Height { get; set; } = "600px";
    private DotNetObjectReference<TextControl> DotNetReference => DotNetObjectReference.Create(this);
    private IJSObjectReference? _txtextcontrol;
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
    if (firstRender)
    {
    _txtextcontrol = await JsRuntime.InvokeAsync<IJSObjectReference>("import", "./scripts/textcontrol.js");
    var options = new Dictionary<string, object?>();
    var webSocketURL = Navigator.BaseUri.Replace("https://", "wss://").Replace("http://", "ws://");
    if (WebSocketURL != null)
    {
    options["websocketurl"] = $"{webSocketURL}api/TXWebSocket";
    options["baseurl"] = $"{WebSocketURL}api/TXWebSocket";
    }
    if (ContainerID != null)
    {
    options["containerid"] = ContainerID;
    }
    await _txtextcontrol.InvokeVoidAsync("addEditorToElement", DotNetReference, options);
    }
    }
    public async Task SaveDocument()
    {
    await _txtextcontrol.InvokeVoidAsync("saveDocument", DotNetReference);
    }
    [JSInvokable("ProcessDocument")]
    public void ProcessDocument(string document)
    {
    byte[] bDocument;
    // create a ServerTextControl instance to load the saved document
    using (TXTextControl.ServerTextControl tx = new TXTextControl.ServerTextControl())
    {
    tx.Create();
    tx.Load(Convert.FromBase64String(document), TXTextControl.BinaryStreamType.InternalUnicodeFormat);
    // add additional text to the document
    tx.Selection.Text = "This document has been modified by .NET\r\n";
    // save back
    tx.Save(out bDocument, TXTextControl.BinaryStreamType.InternalUnicodeFormat);
    }
    // invoke the JS function 'loadDocument' to load back to the modified document
    _txtextcontrol.InvokeVoidAsync("loadDocument", Convert.ToBase64String(bDocument));
    }
    public async Task InsertTable()
    {
    await _txtextcontrol.InvokeVoidAsync("insertTable");
    }
    }
    view raw test.razor hosted with ❤ by GitHub

Any function of the referenced textcontrol.js JavaScript file can be called using the created IJSObjectReference _txtextcontrol. The DotNetObjectReference<TextControl> points to this Razor component. This is passed to the JavaScript functions to save and reload the document.

Consuming the TextControl Razor Component

In this step, we are going to use the Razor component we have created on a page.

  1. Find the Home.razor page in the Pages folder and replace the content with the following code:

    @page "/"
    @rendermode @(new InteractiveServerRenderMode(false))
    @inject NavigationManager Navigator;
    <PageTitle>Home</PageTitle>
    <TextControl WebSocketURL=@Navigator.BaseUri
    ContainerID="txDocumentEditorContainer"
    @ref="_txtextcontrol">
    </TextControl>
    <button @onclick="InsertTable">Insert Table</button>
    <button @onclick="SaveDocument">
    Save and Reload Document (using ServerTextControl)
    </button>
    @code
    {
    private TextControl _txtextcontrol = default!;
    // insert a table using the client-side API
    private async Task InsertTable()
    {
    await _txtextcontrol.InsertTable();
    }
    // save the document and reload it using ServerTextControl
    private async Task SaveDocument()
    {
    await _txtextcontrol.SaveDocument();
    }
    }
    view raw home.razor hosted with ❤ by GitHub

Compile and start the application.

Text Control in Blazor Server

First Button: Insert Table

The InsertTable() method is called in the .NET code of Index.razor when the first button is clicked.

private async Task InsertTable()
{
await _txtextcontrol.InsertTable();
}
view raw test.razor hosted with ❤ by GitHub

This calls the InsertTable() method of the TextControl.Razor component.

public async Task InsertTable()
{
await _txtextcontrol.InvokeVoidAsync("insertTable");
}
view raw test.razor hosted with ❤ by GitHub

In this method, the JavaScript object reference _txtextcontrol is used to invoke a JavaScript call to textcontrol.js.

export function insertTable() {
TXTextControl.tables.add(5, 5, 10, function (e) {
if (e === true) { // if added
TXTextControl.tables.getItem(function (table) {
table.cells.forEach(function (cell) {
cell.setText("Cell text");
});
}, null, 10);
}
})
};
view raw test.js hosted with ❤ by GitHub

Second Button: Save and Reload

TX Text Control's JavaScript API is used to store the document. The document is passed to .NET (the "server"). In .NET, the document is processed using an instance of ServerTextControl TX Text Control .NET Server for ASP.NET
TXTextControl Namespace
ServerTextControl Class
The ServerTextControl class implements a component that provide high-level text processing features for server-based applications.
and sent back to the client side by calling a JavaScript function that loads the content back into the TX Text Control document editor.

The SaveDocument() method is called in the .NET code of Index.razor when the second button is clicked.

private async Task SaveDocument()
{
await _txtextcontrol.SaveDocument();
}
view raw test.razor hosted with ❤ by GitHub

This calls the SaveDocument() method of the TextControl.Razor component.

export function saveDocument() {
// save the document on TXTextControl object
TXTextControl.saveDocument(TXTextControl.StreamType.InternalUnicodeFormat, function (document) {
// call the .NET method 'ProcessDocument' with the saved document data
dotNetObject.invokeMethodAsync('ProcessDocument', document.data);
});
};
view raw test.js hosted with ❤ by GitHub

This calls the JavaScript function saveDocument() in the textcontrol.js JavaScript file:

After the document is saved, it is passed to the .NET ProcessDocument method implemented in TextControl.razor using the created .NET object reference.

[JSInvokable("ProcessDocument")]
public void ProcessDocument(string document)
{
byte[] bDocument;
// create a ServerTextControl instance to load the saved document
using (TXTextControl.ServerTextControl tx = new TXTextControl.ServerTextControl())
{
tx.Create();
tx.Load(Convert.FromBase64String(document), TXTextControl.BinaryStreamType.InternalUnicodeFormat);
// add additional text to the document
tx.Selection.Text = "This document has been modified by .NET\r\n";
// save back
tx.Save(out bDocument, TXTextControl.BinaryStreamType.InternalUnicodeFormat);
}
// invoke the JS function 'loadDocument' to load back to the modified document
_txtextcontrol.InvokeVoidAsync("loadDocument", Convert.ToBase64String(bDocument));
}
view raw test.razor hosted with ❤ by GitHub

This method is parametrized as JSInvokable and can be called from JavaScript.

This method loads the document into a ServerTextControl instance to add text at the top. The document is then saved and passed to the loadDocument JavaScript function.

The JavaScript function loadDocument is finally loading the document back into the editor.

export function loadDocument(document) {
// load the document back into the editor (TXTextControl)
TXTextControl.loadDocument(TXTextControl.StreamType.InternalUnicodeFormat, document);
};
view raw test.js hosted with ❤ by GitHub

Text Control in Blazor Server

For your own testing, you can download and try this sample from our GitHub repository.