一、前言
在NanUI官方的文档中,原本是有一个NanUI.FileResourceHandler的扩展包的,但现在官方已经无法下载了,现在只有0.88版本中有一个NanUI.LocalFileResource程序包,而0.77版本只剩下了一个读取嵌入式资源的程序包。
关于NanUI:NanUI | .Net/.Net Core界面组件NanUI 0.7版正式发布 - 林选臣 - 博客园
在扩展功能之前,请参考[资源处理器]-04 自定义资源处理器 - 知乎 ,我参考这个帖子进行扩展的,也不知道改的对不对,总之功能是实现了。
二、搭建环境
将NanUI0.77源码拷贝到你的项目中,将源码中的Resources.Designer.cs代码复制到你项目中的Resources.Designer.cs 中,
Resources.Designer.cs 中要加入的代码:
/// <summary>
/// 查找类似于 (图标) 的 System.Drawing.Icon 类型的本地化资源。
/// </summary>
internal static System.Drawing.Icon DevToolsIcon {
get {
object obj = ResourceManager.GetObject("DevToolsIcon", resourceCulture);
return ((System.Drawing.Icon)(obj));
}
}
/// <summary>
/// 查找类似 var NanUI = NanUI || {};
///
///(function(target) {
/// const CMD_ATTR_PREFIX = "nanui-command";
///
/// const RAISE_FUNC_NAME = "__nanui_raiseHostWindowEvent";
///
/// const GLOBAL_CLICK_HANDLER_REGISTERED =
/// "__nanui_global_click_handler_registered";
///
/// if (!window.hasOwnProperty(RAISE_FUNC_NAME)) {
/// window[RAISE_FUNC_NAME] = function(eventName, data) {
/// window.dispatchEvent(new CustomEvent(eventName, { detail: data }));
/// };
/// }
///
/// if (!(GLOBAL_CLICK_HANDLER_REGISTERED in window)) {
/// windo [字符串的其余部分被截断]"; 的本地化字符串。
/// </summary>
internal static string FrameGlobalRegistrationScript {
get {
return ResourceManager.GetString("FrameGlobalRegistrationScript", resourceCulture);
}
}
/// <summary>
/// 查找类似 var NanUI = NanUI || {};
///
///(function (target) {
/// var hostWindow = {
/// minimize: function () {
/// native function Minimize();
/// return Minimize();
/// },
/// maximize: function () {
/// native function Maximize();
/// return Maximize();
/// },
/// restore: function () {
/// native function Restore();
/// return Restore();
/// },
/// close: function () {
/// native function Close();
/// return [字符串的其余部分被截断]"; 的本地化字符串。
/// </summary>
internal static string HostWindowExtension {
get {
return ResourceManager.GetString("HostWindowExtension", resourceCulture);
}
}
/// <summary>
/// 查找类似
/// P.O.W.E.R.E.D BY
/// ███╗ ██╗ █████╗ ███╗ ██╗██╗ ██╗██╗
/// ████╗ ██║██╔══██╗████╗ ██║██║ ██║██║
/// ██╔██╗ ██║███████║██╔██╗ ██║██║ ██║██║
/// ██║╚██╗██║██╔══██║██║╚██╗██║██║ ██║██║
/// ██║ ╚████║██║ ██║██║ ╚████║╚██████╔╝██║
/// ╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚═╝
/// S.I.N.C.E 2016
/// 的本地化字符串。
/// </summary>
internal static string nanui {
get {
return ResourceManager.GetString("nanui", resourceCulture);
}
}
/// <summary>
/// 查找类似 var NanUI = NanUI || {};
///
///(function (target) {
///
/// target.__defineGetter__("version", function () {
/// native function Version();
/// return Version();
/// });
///
///})(NanUI); 的本地化字符串。
/// </summary>
internal static string NanUIExtension {
get {
return ResourceManager.GetString("NanUIExtension", resourceCulture);
}
}
/// <summary>
/// 查找 System.Drawing.Bitmap 类型的本地化资源。
/// </summary>
internal static System.Drawing.Bitmap ShadowTemplate {
get {
object obj = ResourceManager.GetObject("ShadowTemplate", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
读取资源的代码也会有几个地方报错,比如,Bootstrap.cs中的Announce方法,由于源码使用的命名空间不一样,无法读取到方法,这里改为你项目的命名空间就好了
下面的NanUI077_SourceCode为我的命名空间
Log(NanUI077_SourceCode.Properties.Resources.nanui.White().OnDarkRed());
接下来将源码中的Resources文件夹拖拽到你的项目中,这些资源都用来显示浏览器的控制台。
打开Resources.resx,点击添加资源,将Resources文件夹所有文件选中添加,就如下图所示。
另外,将允许不安全代码勾选上,否则源码中的部分代码会报错。
如果你的源码正常运行,成功显示页面和控制台,js调用c# ,和c#调用js都正常,说明各方面搭建成功,可以开始下面的步骤。
我项目结构如下图,部分文件夹是我自己加的
打开ResourceHandlerRegister.cs ,这个脚本没有可以自己新建,这里我是导入了官方的嵌入式资源源码,所以已经有这个脚本了,这些源码可以搜索作者的github可以自行下载。
using System;
namespace NetDimension.NanUI
{
public static class ResourceHandlerRegister
{
//读取嵌入式资源
//参考:https://zhuanlan.zhihu.com/p/109022286
public static Bootstrap UseAssembledResource(this Bootstrap _, ResourceHandlerScheme scheme, string domain, string basePath = null)
{
//GC.Collect();
Bootstrap.RegisterCustomResourceHandler(() => new AssembledResource(scheme, domain, basePath));
return _;
}
//REST资源处理器
//参考:https://zhuanlan.zhihu.com/p/109023845
public static Bootstrap UseRestfulService(this Bootstrap _, ResourceHandlerScheme scheme, string domain, Action<RestfulService.RestfulServiceProvider> provider = null)
{
//GC.Collect();
Bootstrap.RegisterCustomResourceHandler(() =>
{
var resourceHandler = new RestfulServiceResource(scheme, domain);
provider?.Invoke(RestfulService.RestfulServiceProvider.Create(resourceHandler));
return resourceHandler;
});
return _;
}
//读取本地资源目录
//参考:https://zhuanlan.zhihu.com/p/109023019
public static Bootstrap FileResource(this Bootstrap _, ResourceHandlerScheme scheme, string domain, string filePath)// 如果您的资源处理器有其他参数,参数表因添加在末尾处)
{
//GC.Collect();
Bootstrap.RegisterCustomResourceHandler(() => new CustomResourceFactory(scheme, domain, filePath));
return _;
}
}
}
新建脚本 CustomResourceFactory.cs
using Chromium;
using NetDimension.NanUI.ResourceHandler;
namespace NetDimension.NanUI
{
public class CustomResourceFactory : CustomResource
{
private string LocalDomain = string.Empty;
public CustomResourceFactory(ResourceHandlerScheme scheme, string domain, string viewFolder)
: base(scheme, domain)
{
string top = scheme == ResourceHandlerScheme.Http ? "http://" : "https://";
LocalDomain = string.Format("{0}{1}", top, domain);
}
protected override ResourceHandlerBase GetResourceHandler(string schemeName, CfxBrowser browser, CfxFrame frame, CfxRequest request)
{
return new CustomResourceHandler(LocalDomain);
}
}
}
新建脚本 CustomResourceHandler.cs
using Chromium;
using NetDimension.NanUI.ResourceHandler;
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
namespace NetDimension.NanUI
{
public class CustomResourceHandler : ResourceHandlerBase
{
private string LocalDomain = string.Empty;
protected override FormiumResponse GetResponse(FormiumRequest request)
{
FormiumResponse response = new FormiumResponse();
if (request.Method != Method.GET)
{
Console.WriteLine("============[CustomResourceHandler]非Git请求");
response.Status = (int)System.Net.HttpStatusCode.NotFound;
return response;
}
string physicalFilePath = string.Empty;
if (ReadFile.Instance.FilePathList.Count > 0)
{
int leng = request.RequestUrl.Length;
int start = LocalDomain.Length + 1;
string indexPath = request.RequestUrl.Substring(start, leng - start);
for (int i = 0; i < ReadFile.Instance.FilePathList.Count; i++)
{
if (ReadFile.Instance.FileNameList[i].Equals(indexPath))
{
physicalFilePath = ReadFile.Instance.FilePathList[i];
break;
}
}
}
else
{
MessageBox.Show("[CustomResourceHandler] ReadFile 中资源列表 FilePathList 为空");
response.Status = (int)System.Net.HttpStatusCode.NotFound;
return response;
}
if (string.IsNullOrEmpty(physicalFilePath))
{
MessageBox.Show("[CustomResourceHandler] physicalFilePath 路径为空");
response.Status = (int)System.Net.HttpStatusCode.NotFound;
return response;
}
response.ContentStream = File.OpenRead(physicalFilePath);
response.MimeType = CfxRuntime.GetMimeType(Path.GetExtension(physicalFilePath).Trim('.')) ?? "text/plain";
return response;
}
public CustomResourceHandler(string localDomain)
{
LocalDomain = localDomain;
}
}
public class ReadFile
{
private static ReadFile _instance = null;
public static ReadFile Instance
{
get
{
if (_instance == null)
{
_instance = new ReadFile();
}
return _instance;
}
}
private List<string> _FilePathList = new List<string>();
public List<string> FilePathList { get => _FilePathList; }
//web前端代码在不同的文件夹可能出现同名文件,所以这里并没有使用字典这样的数据结构
private List<string> _FileNameList = new List<string>();
public List<string> FileNameList { get => _FileNameList; }
private string ViewFullPath = string.Empty;
public void Init(string dir)
{
if (_FilePathList.Count <= 0)
{
ViewFullPath = System.IO.Path.Combine(Application.StartupPath, dir);
GetDirectoryFileList(ViewFullPath);
}
}
private void GetDirectoryFileList(string path)
{
DirectoryInfo directory = new DirectoryInfo(path);
FileSystemInfo[] filesArray = directory.GetFileSystemInfos();
foreach (var item in filesArray)
{
if (item.Attributes == FileAttributes.Directory)
{
GetDirectoryFileList(item.FullName);
}
else
{
_FilePathList.Add(item.FullName);
int leng = item.FullName.Length;
int start = ViewFullPath.Length + 1;
string newPath = item.FullName.Substring(start, leng - start);
if (newPath.Contains(@"\")) newPath = newPath.Replace(@"\", "/");
_FileNameList.Add(newPath);
}
}
}
private ReadFile()
{
}
}
}
调用,Program.cs
using NetDimension.NanUI;
using System;
using System.Windows.Forms;
namespace NanUI077_SourceCode
{
static class Program
{
/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
string View = "View";
//读取web前端文件夹所有的资源
ReadFile.Instance.Init(View);
Bootstrap
.Initialize()
//读取本地资源
.FileResource(ResourceHandlerScheme.Http, "www.app.local", View)
.WithChromiumCommandLineArguments((procesName, cmd) =>
{
// 在此处处理CEF的命令行参数
})
.WithChromiumSettings(settings =>
{
// 在此处处理CEF的设置
})
.WhenLibCefNotFound(args =>
{
// 如果NanUI启动器没有检测到正确的CEF以及ChromiumFX运行环境,将执行此处理过程。
MessageBox.Show("没有检测到Chromium Embedded运行环境,请确认libcef环境配置正确。", "libcef.dll is not found", MessageBoxButtons.OK, MessageBoxIcon.Error);
})
.Run(() =>
{
return new MainIndex();
});
}
}
}
在这里还可以提升一下读取的速度,在ReadFile类中加入下面方法,每次打开网页的时候,就不用老是重复去调用IO去读取本地资源了,我测试过,整体流畅度确实提升一些,但整体读取速度不如NanUI0.88版本。
private Dictionary<string, FileStream> StreamDic = new Dictionary<string, FileStream>();
/// <summary>
/// 读取文件的FileStream
/// </summary>
/// <param name="areaPath">区域路径</param>
/// <param name="fullPath">完整路径</param>
/// <returns></returns>
public FileStream GetFileStream(string areaPath,string fullPath)
{
if (StreamDic.ContainsKey(areaPath))
{
return StreamDic[areaPath];
}
else
{
//清除资源内存在 ResourceHandlerBase.cs 中的 OnRead 方法中
//我将 formiumResponse.Dispose(); 这句注释了,否则读取会报错
FileStream fileStream = File.OpenRead(fullPath);
StreamDic.Add(areaPath, fileStream);
return fileStream;
}
}
如果光这么写会报错,看代码中的注释,请将ResourceHandlerBase.cs 中 formiumResponse.Dispose() 这句代码注释
MainIndex.cs 脚本看我其他帖子,已经写了很多遍了,这里不贴代码了。
fx文件夹是cef的资源文件夹,这个如果没有,去你其他的NanUI0.77项目中拷贝过来放到Debug文件夹,或者安装NetDimension.NanUI.Runtime插件,版本选择0.77版。
安装完成后,在运行的时候,会自动生成cef文件。
在你的项目.exe文件同级目录下,加入一个View文件夹,将web前端代码放进去。
view文件夹
运行:
结束
如果这个帖子对你有用,欢迎 关注 + 点赞 + 留言,谢谢
end