-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.json
1 lines (1 loc) · 21.3 KB
/
index.json
1
[{"content":"开发环境 运行环境:.Net 6\n开发环境:Visual Studio 2022 17.1.3\n框架语言:WPF\n安装WebView2 通过Package Manager控制台安装 Install-Package Microsoft.Web.WebView2 通过Nuget包管理器安装 !https://s1.ax1x.com/2022/04/09/LinhIe.png\n在窗体中添加名字空间: xmlns:wv2=\u0026#34;clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf\u0026#34; 引用WebView2控件: \u0026lt;wv2:WebView2 x:Name=\u0026#34;webview\u0026#34; Grid.Row=\u0026#34;1\u0026#34; Source=\u0026#34;\u0026lt;https://www.bilibili.com\u0026gt;\u0026#34; /\u0026gt; 使用WebView2 关于CoreWebView2 💡 Note:默认情况下,如果控件中没有设置Source属性,CoreWebView2是没有初始化的,此时需要我们手动初始化CoreWebView2,CoreWebView2初始化有以下两种方式:\n第一种:调用EnsureCoreWebView2Async()方法 if (webView.CoreWebView2 == null) { await webView.EnsureCoreWebView2Async(); } 第二种:为Source属性赋值 webView.Source = new Url(\u0026#34;\u0026lt;https://www.bilibili.com\u0026gt;\u0026#34;); 常用设置 从初始化方式说起: var env = await CoreWebView2Environment.CreateAsync(); await webView.EnsureCoreWebView2Async(env); 这和我们最开始讲的初始化相比,多传了一个env变量参数来初始化。这个变量的工厂函数定义如下:\npublic static async Task\u0026lt;CoreWebView2Environment\u0026gt; CreateAsync( string browserExecutableFolder = null, string userDataFolder = null, CoreWebView2EnvironmentOptions options = null) 从以上代码可以看出,这个工厂函数它可以传入三个参数:\nbrowserExecutableFolder userDataFolder options 第一个参数是选择Edge Runtime,默认为长绿版本,但我们也可以选择固定版本,当我们要使用固定版本时,需要做以下操作:\n下载Runtime 打开Edge WebView Runtime下载页面,下载下载固定版本(Fixed Version),可以选择不同的版本和cpu架构。 下载完成后,会得到一个几十兆的cab文件,解压cab文件至指定文件夹:expand xxxxx.cab -f:* r:\\webview2 初始化程序的时候,指定该路径: if (webView.CoreWebView2 == null) { var env = await CoreWebView2Environment.CreateAsync(@\u0026#34;R:\\\\WebView2\u0026#34;); await webView.EnsureCoreWebView2Async(env); } 长绿版本的优势: 可以自动更新 支持在线和离线安装 所有WebView2程序共享运行时,节约磁盘空间 固定版本的优势: 可防止版本升级到来的兼容性问题 可防止运行时被意外卸载 可和应用程序集成在一起 第二个参数是自定义用户数据文件夹,默认情况下,WebView2程序的用户数据是按程序独立存储的,但很多时候我们需要自定义用户数据存储的位置。\nvar env = await CoreWebView2Environment.CreateAsync(userDataFolder: @\u0026#34;R:\\\\WebView2Data\u0026#34;); await webView.EnsureCoreWebView2Async(env); 第三个参数可以继续衍生,它的构造函数如下所示:\n//除了第一个additionalBrowserArguments可以用来传入额外的启动参数外,其它的几个就一般很少使用 public CoreWebView2EnvironmentOptions( string additionalBrowserArguments = null, string language = null, string targetCompatibleBrowserVersion = null, bool allowSingleSignOnUsingOSPrimaryAccount = false) WebView2初始化完成后,还可以在webView.CoreWebView2.Settings中进行一些动态设置,主要包括:\nIsScriptEnabled 是否启用JS脚本 IsWebMessageEnabled 是否启用WebMessage AreDefaultScriptDialogsEnabled 是否启用默认的对话框 IsStatusBarEnabled 是否显示状态栏,关闭时鼠标悬浮在链接上时右下角没有url地址显示 AreDevToolsEnabled 是否启用开发工具, 默认为true,关闭时菜单中的相应选项也一起关闭 AreDefaultContextMenusEnabled 是否启用右键菜单 AreHostObjectsAllowed 是否启用脚本的HostObject注入 IsZoomControlEnabled 是否启用缩放 IsBuiltInErrorPageEnabled 是否启用默认的错误对话框 页面跳转 页面跳转可以通过webview的接口来实现:\n//第一种实现: webView.Source = new Uri(\u0026#34;\u0026lt;http://www.baidu.com\u0026gt;\u0026#34;); //第二种实现: webView.NavigateToString(\u0026#34;\u0026lt;h1\u0026gt;hello world\u0026lt;/h1\u0026gt;\u0026#34;); 这两个方法本身只是一个封装,具体的实现在类型为CoreWebView2的webview.CoreView2属性里面,基本示例如下:\nif (webView.CoreWebView2 == null) { await webView.EnsureCoreWebView2Async(); } webView.CoreWebView2.Navigate(\u0026#34;\u0026lt;https://www.cnblogs.com/tianfang/\u0026gt;\u0026#34;); 新窗口打开页面的处理\n当我们进行页面跳转的时候,有的时候回使用新窗口打开,WebView2会弹出一个有默认样式的新窗口,而这往往不是我们所想要的效果。要重载这一行为,实现在自定义的窗口中承载新的web页面,需要我们处理CoreWebView2.NewWindowRequested事件。\nawait webView.EnsureCoreWebView2Async(); webView.CoreWebView2.NewWindowRequested += OnNewWindowRequested; private void OnNewWindowRequested() { var deferral = e.GetDeferral(); e.NewWindow = webView.CoreWebView2; deferral.Complete(); } 简单的来说有如下三步:\n获取Deferral对象 将EventArgs.NewWindow的引用赋为新的CoreWebView控件 调用Deferral.Coimplete函数 这里是将新窗口在当前页面中打开,实现类似多tab页的浏览器。则需要新建webview2控件,此时需要注意等待初始化完成,并且新的webview2控件同样要增加NewWindowRequested的处理:\nvar deferral = e.GetDeferral(); //需要同步获取,不要异步等待后再获取 await webView.EnsureCoreWebView2Async(); e.NewWindow = webView.CoreWebView2; deferral.Complete(); 执行脚本 在宿主程序中执行Javascript代码 ExecuteScriptAsync()函数:\n在JS脚本中的定义:\nfunction alertMsg(val){ alert(val); } 宿主软件中执行\nawait webView.CoreWebView2.ExecuteScriptAsync(\u0026#34;alertMsg(\\\\\u0026#34;Hello,World!\\\\\u0026#34;)\u0026#34;); AddScriptToExecuteOnDocumentCreatedAsync()函数\nawait webView.CoreWebView2.AddScriptToExecuteOnDocumentCreatedAsync(\u0026#34;alert(\u0026#39;hello world\u0026#39;)\u0026#34;); 反向执行脚本:就是在浏览器里的前端页面执行JS脚本,调用宿主程序的函数。这种情况可以使用AddHostObjectToScript函数 宿主软件(WPF)写法如下:\n//定义需要注入的对象 [ComVisible(true)] public class Bridge { public string Func(string param) =\u0026gt; \u0026#34;Example: \u0026#34; + param; } webView.CoreWebView2.AddHostObjectToScript(\u0026#34;bridge\u0026#34;, new Bridge()); JS中的写法如下:\nconst bridge = chrome.webview.hostObjects.bridge; await bridge.Func(\u0026#34;Test...\u0026#34;) 执行结果输出:\nExample:Test... 宿主程序与页面之间的通讯 相较于上述所提的通过JS实现WebView2宿主程序和前端页面进行通信的方法,在WebView2中,更加通用而高效的方式是WebMessage,它是一个异步的消息通信,并且支持双向通信。\nWebView2 控件中的 Web 内容可以使用 window.chrome.webview.postMessage 向宿主程序发布消息。宿主程序使用任何注册到 WebMessageReceived 委托方法处理消息。 主程序使用 CoreWebView2.PostWebMessageAsString 或 CoreWebView2.PostWebMessageAsJSON 将消息发布到 WebView2 控件中的 Web 内容。这些消息由添加到 window.chrome.webview.addEventListener 的处理程序捕获。 前端页面发送消息给宿主程序:\n在WebView中定义接收到消息的处理函数:\nwebView.WebMessageReceived += (s, e) =\u0026gt; { MessageBox.Show(e.WebMessageAsJson); }; 在前端脚本中发送消息:\nchrome.webview.postMessage(\u0026#39;hello world\u0026#39;); 宿主程序发消息给前端页面:\n在前端脚本中需注册消息的处理函数:\nchrome.webview.addEventListener(\u0026#39;message\u0026#39;, event =\u0026gt; alert(event.data)); 在宿主程序有两个方法可以进行发送,分别是PostWebMessageAsJson()与PostWebMessageAsString()\nwebView.CoreWebView2.PostWebMessageAsString(\u0026#34;hello world\u0026#34;); 禁用WebMessage:\n如果为了安全起见,也可以通过设置将其禁用:\nwebView.CoreWebView2.Settings.IsWebMessageEnabled = false; 💡 在这里我们可以实现页面加载完成之后的回调操作\nwindow.onload = function () { chrome.webview.postMessage(\u0026#34;chaet page loaded complete!\u0026#34;); }; //接收注册消息 chart.WebMessageReceived += WebLoaded; public void WebLoaded(object? obj, CoreWebView2WebMessageReceivedEventArgs e) { MessageBox.Show(e.WebMessageAsJson); } Dev Protocol 使用websocket的方式来驱动 var env = await CoreWebView2Environment .CreateAsync(options:new CoreWebView2EnvironmentOptions( \u0026#34;--remote-debugging-port=9222\u0026#34;)); await webView.EnsureCoreWebView2Async(env); 使用CoreWebView2内置方法 执行命令:CoreWebView2.CallDevToolsProtocolMethodAsync 注册回调:CoreWebView2.GetDevToolsProtocolEventReceiver await webView.CoreWebView2.CallDevToolsProtocolMethodAsync(\u0026#34;Network.enable\u0026#34;, \u0026#34;{}\u0026#34;); var eventRecieiver = webView.CoreWebView2.GetDevToolsProtocolEventReceiver(\u0026#34;Network.requestWillBeSent\u0026#34;); eventRecieiver.DevToolsProtocolEventReceived += (s, e) =\u0026gt; { Console.WriteLine(e.ParameterObjectAsJson + \u0026#34;\\\\n\u0026#34;); }; 关于CallDevToolsProtocolMethodAsync (string methodName, string parametersAsJson)方法方法描述:\nmethodName:The full name of the method in the format {domain}.{method} parametersAsJson:A JSON formatted string containing the parameters for the corresponding method.\n💡 chromedevtools的操作命名空间与相关方法文档:domain\n","permalink":"https://sugarbearr.github.io/posts/webview2%E5%9C%A8wpf%E4%B8%AD%E7%9A%84%E5%BA%94%E7%94%A8/","summary":"开发环境 运行环境:.Net 6\n开发环境:Visual Studio 2022 17.1.3\n框架语言:WPF\n安装WebView2 通过Package Manager控制台安装 Install-Package Microsoft.Web.WebView2 通过Nuget包管理器安装 !https://s1.ax1x.com/2022/04/09/LinhIe.png\n在窗体中添加名字空间: xmlns:wv2=\u0026#34;clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf\u0026#34; 引用WebView2控件: \u0026lt;wv2:WebView2 x:Name=\u0026#34;webview\u0026#34; Grid.Row=\u0026#34;1\u0026#34; Source=\u0026#34;\u0026lt;https://www.bilibili.com\u0026gt;\u0026#34; /\u0026gt; 使用WebView2 关于CoreWebView2 💡 Note:默认情况下,如果控件中没有设置Source属性,CoreWebView2是没有初始化的,此时需要我们手动初始化CoreWebView2,CoreWebView2初始化有以下两种方式:\n第一种:调用EnsureCoreWebView2Async()方法 if (webView.CoreWebView2 == null) { await webView.EnsureCoreWebView2Async(); } 第二种:为Source属性赋值 webView.Source = new Url(\u0026#34;\u0026lt;https://www.bilibili.com\u0026gt;\u0026#34;); 常用设置 从初始化方式说起: var env = await CoreWebView2Environment.CreateAsync(); await webView.EnsureCoreWebView2Async(env); 这和我们最开始讲的初始化相比,多传了一个env变量参数来初始化。这个变量的工厂函数定义如下:\npublic static async Task\u0026lt;CoreWebView2Environment\u0026gt; CreateAsync( string browserExecutableFolder = null, string userDataFolder = null, CoreWebView2EnvironmentOptions options = null) 从以上代码可以看出,这个工厂函数它可以传入三个参数:\nbrowserExecutableFolder userDataFolder options 第一个参数是选择Edge Runtime,默认为长绿版本,但我们也可以选择固定版本,当我们要使用固定版本时,需要做以下操作:","title":"webview2在wpf中的应用"},{"content":"编程范式: 编程范式分为命令式编程、声明式编程、函数式编程和响应式编程。\n命令式编程: 命令式编程的主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。 //1. 声明变量 List\u0026lt;int\u0026gt; results = new List\u0026lt;int\u0026gt;(); //2. 循环变量 foreach(var num in Enumerable.Range(1,10)) { //3. 添加条件 if (num \u0026gt; 5) { //4. 添加处理逻辑 results.Add(num); Console.WriteLine(num); } } 声明式编程: 声明式编程是以数据结构的形式来表达程序执行的逻辑。它的主要思想是告诉计算机应该做什么,但不指定具体要怎么做。 var nums = from num in Enumerable.Range(1,10) where num \u0026gt; 5 select num 函数式编程: 主要思想是把运算过程尽量写成一系列嵌套的函数调用。 Enumerable.Range(1, 10).Where(num =\u0026gt; num \u0026gt; 5).ToList().ForEach(Console.WriteLine); 响应式编程: 响应式编程是一种面向数据流和变化传播的编程范式,旨在简化事件驱动应用的实现。响应式编程专注于如何创建依赖于变更的数据流并对变化做出响应。 IObservable\u0026lt;int\u0026gt; nums = Enumerable.Range(1, 10).ToObservable(); IDisposable subscription = nums.Where(num =\u0026gt; num \u0026gt; 5).Subscribe(Console.WriteLine); subscription.Dispose(); Rx.NET Reactive Extensions(Rx)是一个为.NET应用提供响应式编程模型的库,用来构建异步基于事件流的应用,通过安装System.ReactiveNuget包进行引用。Rx将事件流抽象为Observable sequences(可观察序列)表示异步数据流,使用LINQ运算符查询异步数据流,并使用Scheduler来控制异步数据流中的并发性。简单地说:Rx = Observables + LINQ + Schedulers。\nRx.NET核心: 一切皆为数据流 Observable是对数据流的抽象(事件源) Observer是对Observable的响应(事件处理程序) 在Rx中,分别使用IObservable\u0026lt;T\u0026gt;和IObserver\u0026lt;T\u0026gt;接口来表示可观察序列和观察者。\nIObservable\n(可继承)\npublic interface IObservable\u0026lt;out T\u0026gt; { //Notifies the provider that an observer is to receive notifications. IDisposable Subscribe(IObserver\u0026lt;T\u0026gt; observer); } IObserver\n(可继承)\npublic interface IObserver\u0026lt;in T\u0026gt; { //Notifies the observer that the provider has finished sending push-based notifications. void OnCompleted(); //Notifies the observer that the provider has experienced an error condition. void OnError(Exception error); //Provides the observer with new data. void OnNext(T value); } 创建IObservable方式(事件源): 直接实现IObservable\u0026lt;T\u0026gt;接口 private class MyObservable : IObservable\u0026lt;int\u0026gt; { public IDisposable Subscribe(IObserver\u0026lt;int\u0026gt; observer) { for (int i = 0; i \u0026lt; 100; i++) { observer.OnNext(i); } observer.OnCompleted(); return Disposable.Empty; } } 使用Observable.Create创建 var observable = Observable.Create\u0026lt;int\u0026gt;(observer =\u0026gt; { for (int i = 0; i \u0026lt; 100; i++) { observer.OnNext(i); } observer.OnCompleted(); return Disposable.Empty; }); 使用Observable.Deffer进行延迟创建(当有观察者订阅时才创建) Observable.Defer(() =\u0026gt; { var connection = MysqlConnection(user, password); return connection.ToObservable(); }); 比如要连接数据库进行查询,如果没有观察者,那么数据库连接会一直被占用,这样会造成资源浪费。使用Deffer可以解决这个问题。 4. 使用Observable.Generate创建迭代类型的可观察序列\n// 参数说明: // initial state(初始值): 0 // condition (false means terminate)(条件(false表示终止)):i \u0026lt; 10 // next iteration step(下一个迭代步骤):i+1 // the value in each iteration(每个迭代中的值):i * 3 // 约等于: // for (int i = 0; i \u0026lt; 10; i+1) // { // yeild return i * 3; // } IObservable\u0026lt;int\u0026gt; observable = Observable.Generate(0, i =\u0026gt; i \u0026lt; 10, i =\u0026gt; i+1, i =\u0026gt; i * 3); 使用Observable.Range创建指定区间的可观察序列 IObservable\u0026lt;int\u0026gt; observable = Observable.Range (0, 10).Select (i =\u0026gt; i * 2); 特殊序列 Observable.Return (\u0026#34;Hello World\u0026#34;);//创建单个元素的可观察序列 Observable.Never\u0026lt;string\u0026gt; ();//创建一个空的永远不会结束的可观察序列 Observable.Throw\u0026lt;ApplicationException\u0026gt; (new ApplicationException (\u0026#34;something bad happened\u0026#34;))//创建一个抛出指定异常的可观察序列 Observable.Empty\u0026lt;string\u0026gt; ()//创建一个空的立即结束的可观察序列 使用ToObservable转换IEnumerate和Task类型 Enumerable.Range(1, 10).ToObservable(); IObservable\u0026lt;IEnumerable\u0026lt;string\u0026gt;\u0026gt; resultsA = searchEngineA.SearchAsync(term).ToObservable(); 使用Observable.Using进行资源释放 IObservable\u0026lt;string\u0026gt; lines = Observable.Using (() =\u0026gt; // opens the file and returns the stream we work with File.OpenText (\u0026#34;TextFile.txt\u0026#34;), stream =\u0026gt; Observable.Generate ( stream, //initial state s =\u0026gt; !s.EndOfStream, //we continue until we reach the end of the file s =\u0026gt; s, //the stream is our state, it holds the position in the file s =\u0026gt; s.ReadLine ()) //each iteration will emit the current line (and moves to the next) ); 使用Observable.Interval创建指定间隔可观察序列 Observable.Interval(TimeSpan.FromMilliseconds(1)); 使用Observable.Timer创建可观察的计时器 Observable.Timer(TimeSpan.FromMilliseconds(2), TimeSpan.FromMilliseconds(1)); 实现IObserver方式(事件处理程序): 继承IObserver // 创建Observer public class MyServer : IObserver\u0026lt;int\u0026gt; { //序列完成通知 public void OnCompleted() { Console.WriteLine(\u0026#34;完成\u0026#34;); } //异常 public void OnError(Exception error) { Console.WriteLine(error.Message); } //提供数据(out) public void OnNext(int value) { Console.WriteLine(value); } } //使用Observer subject.Subscribe(new MyServer()); 通过Observer.Create()创建 // 创建Observer: // Observer.Create\u0026lt;int\u0026gt;(num =\u0026gt; Console.WriteLine(num)) // 使用Observer: subject.Subscribe(Observer.Create\u0026lt;int\u0026gt;(num =\u0026gt; Console.WriteLine(num))); 直接用Lambda表达式表示: subject.Subscribe(num =\u0026gt; Console.WriteLine(num)); Scheduler(控制并发,程序调度) Rx提供了以下几种Scheduler:\nNewThreadScheduler:即在新线程上执行 ThreadPoolScheduler:即在线程池中执行 TaskPoolScheduler:与ThreadPoolScheduler类似 CurrentThreadScheduler:在当前线程执行 ImmediateScheduler:在当前线程立即执行 EventLoopScheduler:创建一个后台线程按序执行所有操作 示例:\nObservable.Return(\u0026#34;Hello\u0026#34;,NewThreadScheduler.Default) .Subscribe(str=\u0026gt;Console.WriteLine($\u0026#34;{str} on ThreadId:{Thread.CurrentThread.ManagedThreadId}\u0026#34;) ); Console.WriteLine($\u0026#34;Current ThreadId:{Thread.CurrentThread.ManagedThreadId}\u0026#34;); // 以上输出: // Current ThreadId:1 // Hello on ThreadId:4 一般调用流程 申明Subject\n创建数据源Observable\n(可观察序列)\n创建处理函数Observer(观察者)并订阅subject\nsubject订阅数据源observable\n//申明Subject(Subject\u0026lt;T\u0026gt;),借助Subject进行多播传输,注意,Subject内的事件是并行分发的 //也可以不用依靠Subject分发,但那是以文档流分发,例如通过事件源订阅: //observable.Subscribe(i =\u0026gt; Console.WriteLine(i)); Subject\u0026lt;int\u0026gt; subject = new Subject\u0026lt;int\u0026gt;(); //创建Observable\u0026lt;T\u0026gt;,可观察序列(数据源) var observable = Observable.Create\u0026lt;int\u0026gt;(observer =\u0026gt; { for (int i = 0; i \u0026lt; 100; i++) { observer.OnNext(i); } observer.OnCompleted(); return Disposable.Empty; }); //处理函数(观察者)订阅subject subject.Subscribe(Observer.Create\u0026lt;int\u0026gt;(num =\u0026gt; Console.WriteLine(num))); subject.Subscribe(num =\u0026gt; Console.WriteLine(num)); //最后subject订阅observable observable.Subscribe(subject); ","permalink":"https://sugarbearr.github.io/posts/rx.net/","summary":"编程范式: 编程范式分为命令式编程、声明式编程、函数式编程和响应式编程。\n命令式编程: 命令式编程的主要思想是关注计算机执行的步骤,即一步一步告诉计算机先做什么再做什么。 //1. 声明变量 List\u0026lt;int\u0026gt; results = new List\u0026lt;int\u0026gt;(); //2. 循环变量 foreach(var num in Enumerable.Range(1,10)) { //3. 添加条件 if (num \u0026gt; 5) { //4. 添加处理逻辑 results.Add(num); Console.WriteLine(num); } } 声明式编程: 声明式编程是以数据结构的形式来表达程序执行的逻辑。它的主要思想是告诉计算机应该做什么,但不指定具体要怎么做。 var nums = from num in Enumerable.Range(1,10) where num \u0026gt; 5 select num 函数式编程: 主要思想是把运算过程尽量写成一系列嵌套的函数调用。 Enumerable.Range(1, 10).Where(num =\u0026gt; num \u0026gt; 5).ToList().ForEach(Console.WriteLine); 响应式编程: 响应式编程是一种面向数据流和变化传播的编程范式,旨在简化事件驱动应用的实现。响应式编程专注于如何创建依赖于变更的数据流并对变化做出响应。 IObservable\u0026lt;int\u0026gt; nums = Enumerable.Range(1, 10).ToObservable(); IDisposable subscription = nums.Where(num =\u0026gt; num \u0026gt; 5).Subscribe(Console.WriteLine); subscription.Dispose(); Rx.","title":"Rx.NET"}]