I have a CefSharp browser created in my Winform and I need to dynamically build an HTML page in memory and then have CefSharp render it.
Ideally I would like to pass the constructor a string with the HTML in it but it is expecting a URL. The answer is probably no, but is there a directive you can prepend the string with to let CefSharp know it is a string that contains a web page? Then CefSharp will create a temp file?
If not, where is the Chromium temp folder set to? Will it work if I write a file to there and then pass that as a fully qualified path? I know Chrome will support something like file:///Users/dmacdonald/Documents/myFile.htm as a URL but not sure how to form a URL if using the temp structure.
Here is my new code but my browser object doesn't have a ResourceHandler property. I see it has a ResourceHandlerFactory
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using CefSharp.WinForms;
using CefSharp;
namespace DanCefWinForm
{
public partial class Form1 : Form
{
public const string TestResourceUrl = "http://maps/resource/load";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ChromiumWebBrowser browser = new ChromiumWebBrowser("http://maps/resource/load")
{
Dock = DockStyle.Fill,
};
var handler = browser.ResourceHandler;
browser.Location = new Point(20, 20);
browser.Size = new Size(100, 100);
this.Controls.Add(browser);
}
}
}
The Simple Approach (one "file", one page)
LoadString() can be used to load directly from a string:
Alternatively, LoadHtml() can load from a string in a given encoding:
I tried both, and they both seem to work, at least with CefSharp.Wpf v51.0.0. According to WebBrowserExtensions.cs,
LoadHtml()
usesRegisterHandler()
to register aResourceHandler
. It is not clear to me howLoadString()
works, but both functions seem to have the same effect.Be sure to use a valid URL format for the fake URL, such as:
The Complex Approach (multiple "files", such as doc + images)
Create a class deriving from
IResourceHandlerFactory
. Using VS2015, mousing over the red-underlined name should give the option of Implement interface. This auto-complete option vastly simplifies creation of the class, so be sure to use it.Similar to in step 1, create a class deriving from
IResourceHandler
. Be sure to use the Implement interface auto-complete option if you can.In the class created in step 1 (derived from
IResourceHandlerFactory
), there is a function calledGetResourceHandler()
. Within this function, return a new instance of your derived class from step 2 (based onIResourceHandler
). Usingnew
here is essential since the Web browser may request multiple files simultaneously. EachIResourceHandler
instance should handle one request from the browser (no worries, this is done for you).As mentioned by OP, the browser control has a member called
ResourceHandlerFactory
. Set this member equal to a new instance of your class you created in step 1 (deriving fromIResourceHandlerFactory
). This is what links the Chromium Web Browser control to your interface classes. In step 3 you linked both your classes, so we have a full chain.Within the class from step 2, there is a function called
ProcessRequest()
. This is the first function called when a request is made by a Web page. Your goal here is to record the requested URL and any POST data, then decide whether to allow the request, calling eithercallback.Continue()
orcallback.Cancel()
. Return true to continue.Again in the class from step 2, there is a function called
GetResponseHeaders()
. This is the second function called. Your goal here is to check the URL, possibly fetching file data from wherever you store it (but not yet sending it), determine the response length (file or string size), and set an appropriate status code within the response object. Be sure to set all these variables so the request can proceed correctly.Your final step, again in the class from step 2, is to complete the request within the third called function:
ReadResponse()
. Within this function, write your data fetched in step 6 to thedataOut
stream. If your data exceeds about 32kB, you may need to send it in multiple chunks. Be absolutely sure to limit the amount you write in a given call to the length of thedataOut
stream. SetbytesRead
to whatever you wrote in this particular call. On the last call, when no more data remains, simply setbytesRead
to zero and returnfalse
. Because you may be called upon multiple times for a given file, be sure to track your current read location so you know where you are and how much data has been sent.For those unfamiliar with the matter, you can store data files directly compiled into your EXE by adding them to your project and setting their "Build Action" to "Embedded Resource", followed by loading their data programmatically using
System.Reflection.Assembly.GetManifestResourceStream()
. Using the above methods, there is no need to create or read any files from disk.See https://github.com/cefsharp/CefSharp/blob/v39.0.0-pre02/CefSharp.Example/CefExample.cs#L44 for an example of registering a
ResourceHandler
for an in-memory string.As you can see, it still has an URL (web resources generally tend to have that) but it can be a dummy one of your choice.
Here's the GitHub search for how it's called in the WinForms (and WPF) example apps: https://github.com/cefsharp/CefSharp/search?utf8=%E2%9C%93&q=RegisterTestResources
Another, probably less favourable, option with a temp file (anywhere?) in the local file system is to use
FileAccessFromFileUrlsAllowed
Update from the comments below:
What CefSharp version are you on now? Note if you look at github.com/cefsharp/CefSharp/releases and search for
resource
you see the API changed in version 49 (look under breaking changes for that version) - see comments below for furtther gotcha'sYou probably need to use custom scheme handler, in order to serve local files, and "bypass" chromium security regarding file protocol.
I wrote blog post on this matter.
What you want to add is your scheme handler and its factory:
And then register it before calling Cef.Initialize:
Here is an example of a custom factory that loads resources from the file system:
And here is how you would apply it: