Blazor
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
| Package | Source | Purpose |
|---|---|---|
TXTextControl.Web.Blazor.DocumentEditor | nuget.org | The DocumentEditor component |
TXTextControl.Web.Blazor.DocumentViewer | nuget.org | The DocumentViewer component |
TXTextControl.Web | Text Control Private NuGet Feed | The WebSocket middleware of the editor |
TXTextControl.Web.DocumentEditor.Backend | Text Control Private NuGet Feed | The backend process of the editor and the hosted service that starts it |
TXTextControl.Web.DocumentViewer | Text Control Private NuGet Feed | The backend of the viewer |
TXTextControl.TextControl.Core.SDK | Text Control Private NuGet Feed | The 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:
| Component | Interactive server rendering | Interactive WebAssembly and Auto rendering |
|---|---|---|
DocumentEditor | Supported | Not 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'." |
DocumentViewer | Supported | The 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:
At the top of the file, add the namespace of the viewer:
using TXTextControl.Web.MVC.DocumentViewer;Before
var app = builder.Build();, register the controllers, which include the controller of the viewer:builder.Services.AddControllers();Add
app.UseRouting();andapp.UseTXDocumentViewer();before the Razor components are mapped. The README of the viewer package places them aroundapp.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:
@refstores a reference to the component, so the C# code can call its methods.LoadDocumenttakes 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:
| Parameter | Description |
|---|---|
BasePath | The URL of the viewer backend. By default, the viewer uses the Blazor application itself. |
SignatureSettings | The 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. |
UserNames | An array of user names. |
OnLoad | An 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:
| Parameter | Default | Description |
|---|---|---|
Width and Height | 800px and 600px | The size of the editor |
WebSocketURL | The address of the Blazor application with api/TXWebSocket | The WebSocket URL of the backend. A value must start with wss:// or ws://. |
ContainerID | A generated ID | The ID of the container element |
OnLoad | An event raised when the editor is ready and connected |
Its methods work on the document of the connected editor on the server:
| Methods | Purpose |
|---|---|
LoadDocumentAsync | Load 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. |
SaveDocumentAsync | Save the document to a file path or a FileStream, or return it as a byte array or a string |
SetEditModeAsync, GetEditModeAsync | Set or get the EditMode, for example to show a document read-only |
SetUserNamesAsync, GetUserNamesAsync | Set or get the users who have access to editable regions |
GetSelectionAsync, SetSelectionAsync | Get or set the Selection |
GetTextPartsAsync | Get 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:
OnLoadloads the template as soon as the editor is ready.SaveDocumentAsyncreturns the content of the editor as a byte array in the internal format of TX Text Control. Await the method instead of reading itsResult, 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 useTXTextControl.Web.BinaryStreamType. LoadDocumentshows 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 JSprovides the JavaScript runtime of the page, andInvokeVoidAsynccalls the functionenableTrackChangesin the browser.- TXTextControl.setIsTrackChangesEnabled switches on track changes in the editor. The property
isTrackChangesEnableddoes the same, but it's obsolete. - Call such functions after the editor is ready, for example from a button or from the
OnLoadevent 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
- Document Editor: Blazor: the first editor in a new Blazor Web App.
- ASP.NET Core: services and dependency injection for
ServerTextControl. - Mail Merge and Reporting: templates, merge fields and data sources for the merge preview.
- WebSocket Architecture: the connection between the editor and the backend in detail.