TX Text Control .NET Server

Blazor

Topic

Blazor applications program TX Text Control in C#. The DocumentEditor and DocumentViewer components run with interactive server rendering, so the code of a page works with their documents directly on the server: it loads files into them, saves their content and merges templates with ServerTextControl, while the JavaScript API remains available for tasks in the browser.

Document Editor: Blazor adds a first editor to a new Blazor Web App. Larger Blazor applications often go further, with both components in one application, the Document Viewer, the C# members of the editor, a merge preview and calls of the JavaScript API.

Packages and Render Mode

PackageSourcePurpose
TXTextControl.Web.Blazor.DocumentEditornuget.orgThe DocumentEditor component
TXTextControl.Web.Blazor.DocumentViewernuget.orgThe DocumentViewer component
TXTextControl.WebText Control Private NuGet FeedThe WebSocket middleware of the editor
TXTextControl.Web.DocumentEditor.BackendText Control Private NuGet FeedThe backend process of the editor and the hosted service that starts it
TXTextControl.Web.DocumentViewerText Control Private NuGet FeedThe backend of the viewer
TXTextControl.TextControl.Core.SDKText Control Private NuGet FeedThe document engine

Both Blazor packages target .NET 8 and work in a Blazor Web App. A page that contains one of the components uses interactive server rendering:

@rendermode InteractiveServer

Blazor sets the render mode per page or component, so other parts of the application can use other render modes. The TX Text Control components behave differently in them:

ComponentInteractive server renderingInteractive WebAssembly and Auto rendering
DocumentEditorSupportedNot possible. Components with these render modes belong in the .Client project, and adding the editor package there fails the build with error NETSDK1082: "There was no runtime pack for Microsoft.AspNetCore.App available for the specified RuntimeIdentifier 'browser-wasm'."
DocumentViewerSupportedThe component works in the .Client project with its backend in the server project. LoadDocument completes only after the viewer is ready; called in OnLoad, it doesn't return. Load documents later, for example from a user action.

Configure Program.cs for Both Components

The Blazor application hosts the backends of both components. Start with the Program.cs of Configure Program.cs in the Blazor tutorial, which starts the editor backend and adds the WebSocket middleware, and add the backend of the viewer:

  1. At the top of the file, add the namespace of the viewer:

    using TXTextControl.Web.MVC.DocumentViewer;
    
  2. Before var app = builder.Build();, register the controllers, which include the controller of the viewer:

    builder.Services.AddControllers();
    
  3. Add app.UseRouting(); and app.UseTXDocumentViewer(); before the Razor components are mapped. The README of the viewer package places them around app.UseAntiforgery();:

    app.UseRouting();
    app.UseAntiforgery();
    app.UseTXDocumentViewer();
    

Add the Document Viewer

The DocumentViewer component displays documents, form fields and signature fields. It has no size parameters, so place it in a container with a size. The following page loads Documents/template.docx into the viewer:

@page "/viewer"
@rendermode InteractiveServer
@using TXTextControl.Web.Blazor.DocumentViewer

<div style="width: 1000px; height: 600px;">
    <DocumentViewer @ref="_documentViewer" />
</div>

<button class="btn btn-primary mt-3" @onclick="ButtonLoad">Load Document</button>

@code {
    private DocumentViewer? _documentViewer;

    private async Task ButtonLoad()
    {
        byte[] document = await File.ReadAllBytesAsync("Documents/template.docx");
        await _documentViewer!.LoadDocument(document);
    }
}

The code works as follows:

  • @ref stores a reference to the component, so the C# code can call its methods.
  • LoadDocument takes the document as a byte array and sends it to the viewer. The method has no parameter for a document name.

The component has these parameters:

ParameterDescription
BasePathThe URL of the viewer backend. By default, the viewer uses the Blazor application itself.
SignatureSettingsThe signature settings for collecting signatures, with the signer, the signature boxes and the URL that receives the signed document. Define them when the viewer is initialized.
UserNamesAn array of user names.
OnLoadAn event that the component raises when it has loaded its script. With interactive server rendering, LoadDocument can be called in its handler.

The blog article E-Sign Comes to Blazor: Document Viewer 33.0.1 Released collects signatures with SignatureSettings, and Digital Signatures in the Document Viewer section explains the signing process.

Program the Editor in C#

The DocumentEditor component has these parameters:

ParameterDefaultDescription
Width and Height800px and 600pxThe size of the editor
WebSocketURLThe address of the Blazor application with api/TXWebSocketThe WebSocket URL of the backend. A value must start with wss:// or ws://.
ContainerIDA generated IDThe ID of the container element
OnLoadAn event raised when the editor is ready and connected

Its methods work on the document of the connected editor on the server:

MethodsPurpose
LoadDocumentAsyncLoad a document from a file path, a FileStream, a byte array or a string. The format is a StreamType, BinaryStreamType or StringStreamType value of TXTextControl.Web.
SaveDocumentAsyncSave the document to a file path or a FileStream, or return it as a byte array or a string
SetEditModeAsync, GetEditModeAsyncSet or get the EditMode, for example to show a document read-only
SetUserNamesAsync, GetUserNamesAsyncSet or get the users who have access to editable regions
GetSelectionAsync, SetSelectionAsyncGet or set the Selection
GetTextPartsAsyncGet the TextPartCollection of the document

Further methods show or hide control characters, table grid lines, text frame marker lines and document target markers, enable or disable context menus and set the background and status bar colors. The component package includes no reference documentation; the names above are those of the package's public API.

The methods find the session of the editor through the WebSocket middleware in the process of the Blazor application. They work only when the application itself hosts the middleware and the backend, as configured above. When WebSocketURL points to another application, the editor connects and raises OnLoad, but LoadDocumentAsync throws a NullReferenceException; in that setup, only the JavaScript API reaches the document.

Preview a Merged Template

The following page combines both components: the user edits a template in the editor, and a button merges it with Documents/data.json on the server and shows the result in the viewer.

@page "/"
@rendermode InteractiveServer
@using TXTextControl.Web.Blazor.DocumentViewer

<TXTextControl.Web.Blazor.DocumentEditor.DocumentEditor
    @ref="_documentEditor" OnLoad="DocumentEditorLoaded" Width="800px" Height="500px" />

<button class="btn btn-primary mt-3" @onclick="ButtonPreview">Preview</button>

<div style="width: 800px; height: 500px;">
    <DocumentViewer @ref="_documentViewer" />
</div>

@code {
    private TXTextControl.Web.Blazor.DocumentEditor.DocumentEditor _documentEditor = default!;
    private DocumentViewer? _documentViewer;

    private async Task DocumentEditorLoaded()
    {
        await _documentEditor.LoadDocumentAsync("Documents/template.docx",
            TXTextControl.Web.StreamType.WordprocessingML);
    }

    private async Task ButtonPreview()
    {
        // Get the edited template from the editor
        byte[] template = await _documentEditor.SaveDocumentAsync(
            TXTextControl.Web.BinaryStreamType.InternalUnicodeFormat);
        byte[] result;

        using (TXTextControl.ServerTextControl tx = new TXTextControl.ServerTextControl())
        {
            tx.Create();
            tx.Load(template, TXTextControl.BinaryStreamType.InternalUnicodeFormat);

            using (TXTextControl.DocumentServer.MailMerge mailMerge =
                new TXTextControl.DocumentServer.MailMerge { TextComponent = tx })
            {
                mailMerge.MergeJsonData(File.ReadAllText("Documents/data.json"));
            }

            tx.Save(out result, TXTextControl.BinaryStreamType.InternalUnicodeFormat);
        }

        await _documentViewer!.LoadDocument(result);
    }
}

The code works as follows:

  • OnLoad loads the template as soon as the editor is ready.
  • SaveDocumentAsync returns the content of the editor as a byte array in the internal format of TX Text Control. Await the method instead of reading its Result, which blocks the thread.
  • ServerTextControl loads the template, and MailMerge.MergeJsonData merges it with the JSON data. The document engine classes use TXTextControl.BinaryStreamType, while the editor methods use TXTextControl.Web.BinaryStreamType.
  • LoadDocument shows the merged document in the viewer. The template in the editor stays unchanged.

The blog article TX Text Control for Blazor: Mail Merge Integration Tutorial loads the merge result back into the editor and exports it as PDF.

Call the JavaScript API

Some tasks run in the browser, such as reacting to user interactions or switching on track changes. The global TXTextControl object is available in the page, and the C# code calls JavaScript functions with IJSRuntime. Microsoft recommends placing JavaScript for components with an interactive render mode in files rather than in script tags of .razor files. Create wwwroot/js/editor.js:

function enableTrackChanges() {
    TXTextControl.setIsTrackChangesEnabled(true);
}

Reference the file in Components/App.razor after the Blazor script:

<script src="@Assets["_framework/blazor.web.js"]"></script>
<script src="js/editor.js"></script>

The page calls the function:

@page "/review"
@rendermode InteractiveServer
@inject IJSRuntime JS

<TXTextControl.Web.Blazor.DocumentEditor.DocumentEditor Width="800px" Height="500px" />

<button class="btn btn-primary mt-3" @onclick="ButtonTrackChanges">Track Changes</button>

@code {
    private async Task ButtonTrackChanges()
    {
        await JS.InvokeVoidAsync("enableTrackChanges");
    }
}

The code works as follows:

  • App.razor loads the script once for the whole application, so the function is available on every page, also after Blazor's enhanced navigation between pages.
  • @inject IJSRuntime JS provides the JavaScript runtime of the page, and InvokeVoidAsync calls the function enableTrackChanges in the browser.
  • TXTextControl.setIsTrackChangesEnabled switches on track changes in the editor. The property isTrackChangesEnabled does the same, but it's obsolete.
  • Call such functions after the editor is ready, for example from a button or from the OnLoad event of the component.

For calls in the other direction, from JavaScript to C#, pass a DotNetObjectReference to a JavaScript function, which then calls a method marked with [JSInvokable]. Dispose the reference when the component is disposed, so that it doesn't leak memory. The blog article Using TX Text Control in a Blazor Server App with .NET 8 shows the pattern from before the components were released, and Call .NET methods from JavaScript functions in ASP.NET Core Blazor describes it in detail. JavaScript API and Events describe the objects and events of the editor.

Use Another Backend for the Viewer

The viewer backend doesn't have to run in the Blazor application. Set BasePath to the URL of another ASP.NET Core application that hosts the viewer:

<DocumentViewer @ref="_documentViewer" BasePath="https://localhost:7118" />

That application runs UseTXDocumentViewer and needs a CORS policy that allows the origin of the Blazor application, as Configure Program.cs for Separate Frontends shows. Omit the trailing slash, because the component appends the paths of its scripts to the URL.

Recognize Older Blazor Articles

Articles written before the components were released in version 33.0 use the Blazor Server App template and connect the JavaScript API of the editor or viewer to C# with their own interop code. The components replace this code. The getting started articles about the components from 2025 install the Blazor package from nuget.org and the engine and viewer packages from the offline package source of an installed trial version; with the private NuGet feed, install those packages as described above.

Next Steps