Running Local Web Pages in CefSharp.WPF

by filip 18. January 2017 18:33

I needed to create a program which will run a web page from the local file system. Using the WebBrowser control wasn't really an option since that uses the Trident engine which didn't support many of the things we were trying to do in the web page. I went with the latest build of CefSharp.Wpf, but that doesn't natively run local files, so it had to be modified. Fortunately, I found some posts on how to enable that feature (here and here) - however, the first link was for an older version of CefSharp, and the second link only works with static resource files. I ended up merging both concepts to get it to work.

The code below works on version 53.0.1 of CefSharp.Wpf. 

First, the following two classes needed to be created:

LocalResourceHandlerFactory.cs:

using System;
using CefSharp;
namespace MyCustomNamespace
{
    public class LocalResourceHandlerFactory : IResourceHandlerFactory
    {
        public bool HasHandlers
        {
            get
            {
                return true;
            }
        }

        public IResourceHandler GetResourceHandler(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request)
        {
            return new LocalResourceHandler();
        }
    }
}

LocalResourceHandler.cs:

using System;
using System.IO;
using CefSharp;
using System.Collections.Generic;

namespace MyCustomNamespace
{
    class LocalResourceHandler : IResourceHandler
    {
        private string mimeType;
        private MemoryStream stream;

        public void Cancel()
        {

        }

        public bool CanGetCookie(Cookie cookie)
        {
            return true;
        }

        public bool CanSetCookie(Cookie cookie)
        {
            return true;
        }

        public void Dispose()
        {

        }

        public void GetResponseHeaders(IResponse response, out long responseLength, out string redirectUrl)
        {
            responseLength = stream == null ? 0 : stream.Length;
            redirectUrl = null;

            response.StatusCode = (int)System.Net.HttpStatusCode.OK;
            response.StatusText = "OK";
            response.MimeType = mimeType;
        }

        public bool ProcessRequest(IRequest request, ICallback callback)
        {
            // The 'host' portion is entirely ignored by this scheme handler.
            var uri = new Uri(request.Url);
            var file = uri.AbsolutePath;

            if (File.Exists(file))
            {
                Byte[] bytes = File.ReadAllBytes(file);
                stream = new MemoryStream(bytes);
                switch (Path.GetExtension(file))
                {
                    case ".html":
                    case ".htm":
                        mimeType = "text/html";
                        break;
                    case ".css":
                        mimeType = "text/css";
                        break;
                    case ".js":
                        mimeType = "text/javascript";
                        break;
                    case ".png":
                        mimeType = "image/png";
                        break;
                    case ".jpg":
                    case ".jpeg":
                        mimeType = "image/jpeg";
                        break;
                    case ".gif":
                        mimeType = "image/gif";
                        break;
                    case ".woff":
                        mimeType = "application/font-woff";
                        break;
                    case ".eot":
                        mimeType = "application/vnd.ms-fontobject";
                        break;
                    case ".ttf":
                        mimeType = "application/font-ttf";
                        break;
                    case ".otf":
                        mimeType = "application/font-otf";
                        break;
                    case ".svg":
                        mimeType = "image/svg+xml";
                        break;
                    case ".appcache":
                    case ".manifest":
                        mimeType = "text/cache-manifest";
                        break;
                    default:
                        mimeType = "application/octet-stream";
                        break;
                }
                callback.Continue();
                return true;
            }
            return false;
        }

        public bool ReadResponse(Stream dataOut, out int bytesRead, ICallback callback)
        {
            //Dispose the callback as it's an unmanaged resource, we don't need it in this case
            callback.Dispose();

            if (stream == null)
            {
                bytesRead = 0;
                return false;
            }

            //Data out represents an underlying buffer (typically 32kb in size).
            var buffer = new byte[dataOut.Length];
            bytesRead = stream.Read(buffer, 0, buffer.Length);

            dataOut.Write(buffer, 0, buffer.Length);

            return bytesRead > 0;
        }
    }
}

With the above code in place, we can now load the web page from a local file. The code below assumes that the HTML files are located in a 'web' subdirectory (for example, /bin/debug/web/)

public MainWindow()
        {
            
            var settings = new CefSharp.CefSettings { RemoteDebuggingPort = 1234 };
            // for some reason, performance sucks w/ the gpu enabled
            settings.CefCommandLineArgs.Add("disable-gpu", "1");
            settings.CefCommandLineArgs.Add("disable-gpu-vsync", "1");
            CefSharp.Cef.Initialize(settings);

            InitializeComponent();

            browser.ResourceHandlerFactory = new LocalResourceHandlerFactory();

            browser.BrowserSettings.FileAccessFromFileUrls = CefSharp.CefState.Enabled;
            browser.BrowserSettings.UniversalAccessFromFileUrls = CefSharp.CefState.Enabled;
            browser.BrowserSettings.WebSecurity = CefSharp.CefState.Disabled;
            //browser.BrowserSettings.WebGl = CefSharp.CefState.Disabled;



            string dir = System.IO.Directory.GetCurrentDirectory();
            String page = string.Format("{0}/web/index.html", dir);
            browser.Address = page;
        }

 

Tags: , ,

Web Development | Windows Development

Extract Formatted Text From Excel Cell With C# (Rich Text Format)

by filip 16. September 2009 23:17

I was writing an application that needed to convert text in a cell in an Excel workbook to HTML. It is fairly trivial to get formatting for the entire cell, but each individual character in the cell could have different formatting itself, so I needed something more specific than cell-level formatting info.

At first, I started using the Excel.Range.get_Characters( pos, len ) method to get info out of the cell.  The code would loop through all characters, get them one by one, and check the formatting.  For example:

   1: Microsoft.Office.Interop.Excel.Range Range = (Microsoft.Office.Interop.Excel.Range)Cell;
   2: int TextLength = Range.Text.ToString().Length;
   3: for (int CharCount = 1; CharCount <= TextLength; CharCount++)
   4: {
   5:     Microsoft.Office.Interop.Excel.Characters charToTest = Range.get_Characters(CharCount, 1);
   6:     bool IsBold = (bool)charToTest.Font.Bold;
   7:     bool IsItalic = (bool)charToTest.Font.Italic;
   8:     // other formatting tests here
   9: }

However, that method proved to be incredibly slow for cells that have more than just a few characters.  For cells that have 1000+ characters, it would take several minutes to run the test across all characters. I kept playing around with different ways to speed up the whole process, but it just became apparent that making the call to Excel to get all of this information was not going to be acceptable.

Finally, I think I’ve found the solution. It is possible to copy the text from a cell to the clipboard, and then use the Clipboard class to retrieve the formatted text, and parse it with C#. I ended up using the System.Windows.DataFormats.Rtf format to extract the data from the clipboard in the following way:

string rtf = (string)System.Windows.Clipboard.GetData(System.Windows.DataFormats.Rtf);

Then, I create a System.Windows.Forms.RichTextBox, and use that to parse the data. The following is a sample of the solution, and it is reasonably quick.

   1: Microsoft.Office.Interop.Excel.Range Range = (Microsoft.Office.Interop.Excel.Range)Cell;
   2: Range.Copy(System.Reflection.Missing.Value);
   3:             
   4: string rtf = (string)System.Windows.Clipboard.GetData(System.Windows.DataFormats.Rtf);
   5: System.Windows.Forms.RichTextBox rtb = new System.Windows.Forms.RichTextBox();
   6: rtb.Rtf = rtf;
   7:             
   8: int CharCount = rtb.Text.Length;
   9:  
  10: for (int CharNum = 0; CharNum < CharCount; CharNum++)
  11: {
  12:    rtb.Select(CharNum, 1);
  13:    System.Drawing.Font Font = rtb.SelectionFont;
  14:    bool IsCharBold = Font.Bold;
  15:    bool IsCharUnderline = Font.Underline;
  16:    bool IsCharItalic = Font.Italic;
  17:  
  18:    // other code here


I was also asked about getting the color in the comments. To get the color, you can use:

System.Drawing.Color color = rtb.SelectionColor;

There are also other properties of rtb dealing with selection, such as SelectionAlignment, SelectionBackColor, etc. See the RichTextBox class for more info.

Tags: , , ,

Windows Development

Vista Sidebar Gadget, Transparent Backgrounds, and Font Color

by filip 9. May 2008 09:30

gadgetssWe've had a request from a client to build a Windows Vista Sidebar Gadget.  The background on the gadget is to be somewhat see-through.  So I've played around with gadgets, and noticed that when I make a semi-transparent background on the gadget, the font color gets all messed up.  For example, I wanted white text on a blue background with 50% opacity, and the font appeared blueish, even though it was supposed to be white.  If I turn off the opacity on the background, the font becomes white, but as long as there's opacity, the font is messed up.

Well, after doing some research, it appears there is a way to display the proper font color on transparent backgrounds, although I didn't find this anywhere on the Microsoft site.  The trick is to use JavaScript and the addTextObject() method.  Here's an example of some code, and the resulting gadget:

 

 1: <html> 
 2:   <head> 
 3:     <title>Sample Gadget</title> 
 4:     <style> 
 5:       body { width: 120px; height: 200px; font-size: 12px; margin: 0; font-family: Verdana; color: #FFFFFF; } 
 6:       .gadget { margin: 3px; } 
 7:     </style> 
 8:   </head> 
 9:   <body> 
 10:     <div class="gadget"> 
 11:       <div>This text is not white.</div> 
 12:     </div> 
 13:     <g:background id="background" src="images\bgblue.png" style="width:100%;height:100%;z-index: -1; position: absolute; top: 0; left: 0;" opacity="50"></g:background> 
 14:     <script language="javascript" type="text/javascript">
 1: document.getElementById("background").addTextObject("This text is white.", "Verdana", 12, "white", 3, 50);
</script> 
 15:   </body> 
 16: </html>

Tags: ,

Windows Development

Bloodforge Band

Looking for the Bloodforge band site? Click here.

About Filip Stanek

Death Note Pic I'm a developer at ACG in Cincinnati, OH. I like ASP.NET, Flash, and other web technologies, & enjoy playing chess, video games, etc.

Currently playing:

Disqus

Month List