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.
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()); | |
} |
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(); | |
} |
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.
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> |
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()); | |
} |
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); | |
} | |
} |
Test this yourself by downloading the sample from the GitHub repository.