Kestrel is a cross-platform web server for ASP.NET Core. It is the web server that's included and enabled by default in ASP.NET Core project templates.

Embedded Kestrel

The advantage of Kestrel, however, is that this server can be launched from within any .NET Core application, such as Windows Forms. The TX Text Control Document Viewer (part of TX Text Control .NET Server for ASP.NET), available for ASP.NET Core, can be hosted on an HTML page that is then served by Kestrel within the same Windows Forms application.

Kestrel in Windows Forms

The following screenshot shows the Document Viewer with a local PDF document that is hosted by the Kestrel web server that is running on the local machine.

Starting Kestrel

In addition to the application itself, another task is started in the Program.cs of the Windows Forms application.

[STAThread]
static void Main()
{
Task.Run(() => StartWebServer());
ApplicationConfiguration.Initialize();
Application.Run(new Form1());
}
view raw test.cs hosted with ❤ by GitHub

The StartWebServer method basically creates the Kestrel web server that is listening on a specific IP and port number.

private static void StartWebServer()
{
var assembly = Assembly.Load("TXTextControl.Web.MVC.DocumentViewer");
var builder = WebHost.CreateDefaultBuilder();
var app = builder
.UseKestrel(options => options.Listen(IPAddress.Parse("127.0.0.1"), 8888))
.UseContentRoot(Directory.GetCurrentDirectory())
.UseWebRoot(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot"))
.ConfigureServices((services) =>
{
services.AddCors();
services.AddMvc().AddMvcOptions(options =>
{
options.EnableEndpointRouting = false;
}).AddApplicationPart(assembly);
})
.Configure(app =>
{
app.UseHttpsRedirection();
app.UseRouting();
app.UseCors(builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
app.UseAuthorization();
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")),
RequestPath = ""
});
app.UseMvcWithDefaultRoute();
}).Build();
app.Run();
}
view raw test.cs hosted with ❤ by GitHub

The trick is to add an ApplicationPart to the MVC routing by loading the Document Viewer assembly. This allows the Document Viewer to process controller requests necessary for viewer operations.

The Kestrel server adds MVC routing options and static data that is located in a folder named wwroot. This folder contains a file named viewer.html and is located in the root of your Windows Forms application.

Kestrel in Windows Forms

This is the file that contains the initialization of the Document Viewer.

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>TX Text Control Document Viewer from JS</title>
<script src="http://localhost:8888/TextControl/GetResource?Resource=minimized.tx-viewer.min.js"></script>
<script src="http://localhost:8888/TextControl/GetResource?Resource=minimized.tx-viewer-component.min.js"></script>
<style>
#txDocumentViewer {
height: 100vH;
width: 100vW;
}
html, body {
height: 100%;
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<tx-document-viewer id="viewer1"
settings='{"dock":1, "basePath":"http://localhost:8888"}'>
</tx-document-viewer>
</body>
</html>
view raw test.html hosted with ❤ by GitHub

The form contains a WebView2 control and the source is set to the location of the static file viewer.html, including the URL of the local Kestrel server.

private void Form1_Load1(object sender, EventArgs e)
{
webView21.Source = new Uri("http://127.0.0.1:8888/viewer.html?id=" + Guid.NewGuid().ToString());
}
view raw test.cs hosted with ❤ by GitHub

Clicking the Open... menu opens a local document, converts it to a base64-encoded string, and passes it to the loadDocument JavaScript function on the created viewer in the WebView2 control.

private void menuItemLoad_Click(object sender, EventArgs e)
{
// set filter to tx and pdf
openFileDialog1.Filter = "TX Text Control Documents (*.tx)|*.tx|PDF Documents (*.pdf)|*.pdf";
if (openFileDialog1.ShowDialog() == DialogResult.OK)
{
// load the document into a byte array
var bytes = File.ReadAllBytes(openFileDialog1.FileName);
// get the filename
string filename = Path.GetFileName(openFileDialog1.FileName);
// convert the byte array to a Base64 string
var stringBase64 = Convert.ToBase64String(bytes);
// create a JavaScript string that calls the loadDocument() function
string loadSettings = "{ pdfjs: { basePath: 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/3.2.146' } }";
string js = "TXDocumentViewer.loadDocument('" + stringBase64 + "', '" + filename + "', null, " + loadSettings + ");";
// execute the JavaScript string
webView21.ExecuteScriptAsync(js);
}
}
view raw test.cs hosted with ❤ by GitHub

Test this yourself by downloading the sample from the GitHub repository.