WinForm(十三)WebView2

WebView是WinForm框架中一个控件,用来对网页信息交互,有时Web自己开发的,有时Web是三方的。

下面通过一个例子来看看WebView2的使用。

首先看Web的逻辑,是一个商品添加页面,用AlpineJS和BootStrap来开发的,业务上点击添加按钮,弹出modal框窗,然后保存结果,完成添加,代码如下:.

View

@{    ViewData["Title"] = "商品管理";}@section Css{    <style>        .form-switch {            display: flex !important;            flex-direction: row-reverse !important;            justify-content: space-between !important;        }</style>}<div x-data="querydata()" id="ttt">    <div class="row" style="margin:4px">
        <div class="col-sm-4">        </div>        <div class="col-sm-4">        </div>        <div class="col-sm-4" style="text-align:right" style="margin:4px 0px">
            <button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#addgoods">                追加            </button>
        </div>    </div>    <hr />
    <div class="mb-3 row">        <table class="table table-striped">            <thead>                <tr>                    <th>ID</th>                    <th>名前</th>                    <th>価格</th>                    <th>説明</th>                    <th>有効</th>                    <th>シリアル番号</th>                    <th>製品タイプ</th>                    <th>操作</th>                </tr>            </thead>            <tbody>                <template x-for="goods in Goodses">                    <tr>                        <td x-text="goods.ID"></td>                        <td x-text="goods.Name"></td>                        <td x-text="goods.Price"></td>                        <td x-text="goods.Describe"></td>                        <td x-text="goods.Validate"></td>                        <td x-text="goods.SerialNumber"> </td>                        <td x-text="goods.GoodsType"></td>                        <td>                            <button type="button" class="btn btn-primary btn-sm" data-bs-toggle="modal" data-bs-target="#modifygoods" x-on:click="modify(goods)">改訂</button>                            <button type="button" class="btn btn-danger btn-sm" x-on:click="remove(goods.ID)">消去</button>                        </td>                    </tr>                </template>            </tbody>        </table>    </div>    <div class="modal fade" id="addgoods" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">        <div class="modal-dialog">            <div class="modal-content">                <div class="modal-header">                    <h5 class="modal-title" id="staticBackdropLabel"> 追加Goods</h5>                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>                </div>                <div class="modal-body">                    <div class="container-fluid">                        <div class="mb-3 row">                            <label for="GoodsTypeID" class="col-sm-4 col-form-label">Goodsタイプ</label>                            <div class="col-sm-8">
                                <select class="form-select" x-model="Goods.GoodsTypeID" id="GoodsTypeID" aria-label="Default select example">                                    <option selected>製品タイプ</option>                                    <template x-for="(item,index) in GoodsTypes" :key="index">                                        <option x-text="item.Name" :value="item.ID"></option>                                    </template>                                </select>
                            </div>                        </div>                        <div class="mb-3 row">                            <label for="name" class="col-sm-4 col-form-label">お名前</label>                            <div class="col-sm-8">                                <input type="text" class="form-control" x-model="Goods.Name" placeholder="" id="Name">                            </div>                        </div>
                        <div class="mb-3 row">                            <label for="Price" class="col-sm-4 col-form-label">価格</label>                            <div class="col-sm-8">                                <input type="number" class="form-control" x-model="Goods.Price" placeholder="" min="1" id="Price">                            </div>                        </div>
                        <div class="mb-3 row">                            <label for="Describe" class="col-sm-4 col-form-label">説明</label>                            <div class="col-sm-8">                                <input type="text" class="form-control" x-model="Goods.Describe" placeholder="" min="1" id="Describe">                            </div>                        </div>
                        <div class="mb-3 row">                            <label for="SerialNumber" class="col-sm-4 col-form-label">シリアル番号</label>                            <div class="col-sm-8">                                <input type="number" class="form-control" x-model="Goods.SerialNumber" placeholder="" min="1" id="SerialNumber">                            </div>                        </div>                        <div class="form-check form-switch mb-3 row" style="margin-left:150px">                            <label class="form-check-label" for="IsCollapse">有効</label>                            <input class="form-check-input" type="checkbox" role="switch" x-model="Goods.Validate" id="Validate" checked>                        </div>
                        <div></div>                    </div>                    <div class="modal-footer">                        <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">閉鎖</button>                        <button type="button" class="btn btn-primary" data-bs-dismiss="modal" x-on:click="sava">保存</button>                    </div>                </div>            </div>        </div>    </div>    @section Scripts{        <script src="~/js/Alpine.js" defer></script>        <script>            function querydata() {                return {
                    GoodsTypes: [],                    Goodses: [],                    Goods: {                        Name: '',                        Price: 0,                        Describe: '',                        Validate: true,                        SerialNumber: 0,                        GoodsTypeID: 0,                        GoodsType: '',                    },                    init() {                        that = this                        $.get("/home/goodses", {}, function (data) {                            if (data != null) {                                that.GoodsTypes = data.Data.GoodsTypes                                that.Goodses = data.Data.Goodses                            } else {                                console.log("/Home/Goodses is error")                            }                        });                    },                    sava() {                        $.ajax({                            type: "POST",                            url: "/home/goods",                            data: this.Goods,                            success: function (data) {
                                if (data.Result) {                                    alert("送信に成功!")                                    that.init()                                    that.Goods = {                                        Name: '',                                        Price: 0,                                        Describe: '',                                        Validate: true,                                        SerialNumber: 0,                                        GoodsTypeID: 0,                                        GoodsType: '',                                    }                                } else {                                    alert("提出に失敗しました:" + data.Message)                                }                            }                        });                    }                }            }</script>    }</div>

Controller

using Microsoft.AspNetCore.Mvc;using System.Collections.Immutable;using System.Diagnostics;using WinFormDemo12WebHost.Models;
namespace WinFormDemo12WebHost.Controllers{    public class HomeController : Controller    {        private readonly ILogger<HomeController> _logger;
        public HomeController(ILogger<HomeController> logger)        {            _logger = logger;        }        public IActionResult Goods()        {            return View();        }        static List<GoodsType> _goodsTypes = new List<GoodsType> {            new GoodsType {ID=1,Name="A类型" },            new GoodsType {ID=2,Name="B类型" },        };        static List<Goods> _goodses = new List<Goods>        {
        };        [HttpGet("/home/goodses")]        public async Task<JsonResult?> QueryGoodsesAsync()        {            try            {                _logger.LogInformation("BackQuery Goods List");                var goodsTypes = _goodsTypes;                var goodses = _goodses;
                return new JsonResult(                    new                    {                        Data = new                        {                            GoodsTypes = goodsTypes,                            Goodses = goodses                        }                    });            }            catch (Exception exc)            {                _logger.LogCritical(exc, exc.Message);                return new JsonResult(new                {                    Result = false,                    Message = exc.Message                });            }        }        [HttpDelete("/home/goods")]        public async Task<JsonResult?> DeleteGoodsAsync(int id)        {            try            {                _logger.LogInformation("delete goods");                var result = _goodses.Remove(_goodses.SingleOrDefault(s => s.ID == id));                return new JsonResult(                    new                    {                        Result = result                    });            }            catch (Exception exc)            {                _logger.LogCritical(exc, exc.Message);                return new JsonResult(new { Result = false, Message = exc.Message });            }        }        [HttpPut("/home/goods")]        public async Task<JsonResult?> ModifyGoodsAsync(Goods goods)        {            try            {                _logger.LogInformation("modify goods");                _goodses.Remove(_goodses.SingleOrDefault(s => s.ID == goods.ID));                goods.ID = _goodses.Max(s => s.ID) + 1;                _goodses.Add(goods);                return new JsonResult(                    new                    {                        Result = goods                    });            }            catch (Exception exc)            {                _logger.LogCritical(exc, exc.Message);                return new JsonResult(new { Result = false, Message = exc.Message });            }        }        [HttpPost("/home/goods")]        public async Task<JsonResult?> AddGoodstAsync(Goods goods)        {            try            {                _logger.LogInformation("add goods");                goods.ID = _goodses.Count > 0 ? _goodses.Max(s => s.ID) + 1 : 1;                _goodses.Add(goods);                return new JsonResult(                    new                    {                        Result = true,                        Data = goods                    });            }            catch (Exception exc)            {                _logger.LogCritical(exc, exc.Message);                return new JsonResult(new { Result = false, Message = exc.Message });            }        }       }    public class GoodsType    {        public int ID { get; set; }        public string? Name { get; set; }    }    public class Goods    {        public int ID { get; set; }        public string? Name { get; set; }        public decimal Price { get; set; }        public string? Describe { get; set; }        public bool Validate { get; set; }        public int GoodsTypeID { get; set; }        public int SerialNumber { get; set; }        public string GoodsType { get; set; }        public decimal MaxPrice        {            get            {                return Price >= 70000 ? Price + 3000 : (Price > 0 ? Price + 2000 : 0);            }        }        public decimal MinPrice        {            get            {                return Price >= 70000 ? Price - 3000 : (Price > 2000 ? Price - 2000 : 0);            }        }    }}

WebView2与控件与Web的交互主要通过webView21.CoreWebView2.ExecuteScriptAsync方法完成,所以不同的Web内容,JS的写法不一样,当然这里的JS实现简单交互还行,更复杂的就有点吃力了,可以使用一些UI自动化工库来操作更快捷。在WinForm中使用WebView,更多的是用来展现数据,而不是互操作,所以下面只是一个简单交互例子而已。

下面是模拟三个动作:点击添加按钮,在商品添加页面中完成信息录入,然后点击保存按钮(即使保存成功或失败后的alter也能针对处理)。

using System.Collections.Generic;
namespace WinFormsDemo13{    public partial class Form1 : Form    {        public Form1()        {            InitializeComponent();        }        private void Form1_Load(object sender, EventArgs e)        {            webView21.Source = new Uri(@"http://localhost:5026/home/goods");        }        private void button1_Click(object sender, EventArgs e)        {            var js = """                $(".btn-info").click()                """;            webView21.CoreWebView2.ExecuteScriptAsync(js);        }        int sn = 1;        private void button2_Click(object sender, EventArgs e)        {            int mark = DateTime.Now.Millisecond * 1000 + DateTime.Now.Microsecond;            var js = $"""                   var goods=document.querySelector('[x-data]')._x_dataStack[0].Goods;                goods.GoodsTypeID={mark % 2 + 1};                goods.Name="商品{mark}";                goods.Price={100 * new Random().Next(1, 20)};                goods.Describe="商品{mark}说明";                goods.SerialNumber={sn};                goods.Validate={(mark % 2 == 0).ToString().ToLower()};                goods.GoodsType="{(mark % 2 == 0 ? "A类型" : "B类型")}";                """;            webView21.CoreWebView2.ExecuteScriptAsync(js);            sn++;        }        private void button3_Click(object sender, EventArgs e)        {            var js = """                $("#addgoods .btn-primary").click()                """;            webView21.CoreWebView2.ExecuteScriptAsync(js);        }     
        private void CoreWebView2_ScriptDialogOpening(object? sender, Microsoft.Web.WebView2.Core.CoreWebView2ScriptDialogOpeningEventArgs e)        {            e.Accept();        }
        private void webView21_CoreWebView2InitializationCompleted(object sender, Microsoft.Web.WebView2.Core.CoreWebView2InitializationCompletedEventArgs e)        {            if (e.IsSuccess)            {                webView21.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false;                webView21.CoreWebView2.ScriptDialogOpening += CoreWebView2_ScriptDialogOpening;            }        }    }}

运行结果:

WinForm(十三)WebView2