在.NET MAUI開發中,BlazorWebView和WebView都用於顯示網頁內容,但它們的用途不同,針對不同的場景而設計。 BlazorWebView 專門設計用於在 .NET MAUI 應用程式中託管 Blazor 元件,可讓您重複使用 Blazor 元件並在 Web 和本機應用程式之間共用程式碼。 WebView 是用於顯示 Web 內容的通用控制項,包括網頁、HTML 字串和本機 HTML 檔案。在本文中,我們將探討如何使用WebView 控制項將.NET MAUI Blazor 文件掃描器應用程式轉換為.NET MAUI 應用程序,以JavaScript 和HTML 實作文件掃描邏輯,並實作C# 和JavaScript 之間的互通以掃描文件和保存圖像。
開啟 MainPage.xaml 檔案並將現有程式碼替換為以下 XAML 以新增 WebView 控制項:
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="MauiWebView.MainPage"> <ScrollView> <StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand"> <WebView x:Name="WebView" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand" Navigating="OnWebViewNavigated"/> </StackLayout> </ScrollView> </ContentPage>
開啟 MainPage.xaml.cs 檔案並新增以下程式碼來設定 WebView 的來源並處理 Navigating 事件:
namespace MauiWebView { public partial class MainPage : ContentPage { public MainPage() { InitializeComponent(); LoadHtmlFile(); } private void LoadHtmlFile() { WebView.Source = "index.html"; } private async void OnWebViewNavigated(object sender, WebNavigatingEventArgs e) { if (e.Url.StartsWith("invoke://callcsharpfunction")) { // TODO: Implement interop between C# and JavaScript } } } }
說明:
在 .NET MAUI 專案中,您可以將位於 Resources/Raw 資料夾中的靜態 HTML、JavaScript 和 CSS 檔案載入到 WebView 中。確保 MauiAsset 建置操作包含在 .csproj 檔案中:
d0fd252dd0536d593a865b1b7d180310 a6b6999ef84f0df9dca4c12a06340421 6e5ad26193226e804779ce3cd902f2c9
我們在index.html 檔案中建立與先前的 Blazor 文件掃描器應用程式類似的 UI 佈局。
8b05045a5be5764f313ed5b9168a17e6 49099650ebdc5f3125501fa170048923 93f0f5c25f18dab9d176bd4f6de5d30e 1fc2df4564f5324148703df3b6ed50c1 4f2fb0231f24e8aef524fc9bf9b9874f b2386ffb911b14667cb8f0f91ea547a7Dynamsoft RESTful API Example6e916e0f7d1e588d4f442bf645aedb2f ea507197563fe9808580cd58ff5dae83 9c3bca370b5104690d9ef395f2c5f8d1 6c04bd5ca3fcae76e30b72ad730ca86d 38b885ab7897934b555c67b94cd711e2 0a6bb94971119ee328f517322c66980c16b28748ea4df4d9c2150843fecfba68 16b28748ea4df4d9c2150843fecfba68 c1a436a314ed609750bd7c7d319db4daDocument Scanner2e9b454fa8428549ca2e64dfac4625cd 39bbc163833ae4e036fa5108a4263823 dc6dce4a544fdca2df29d5ac0ea9906b 2e1cf0710519d5598b1f0f14c36ba674 Get a License key from e854f899d6c08fe1c619c9c20acd0fd7here5db79b134e9f6b82c0b36e0489ee08ed. 8c1ecd4bb896b2264e0711597d40766c 1a7f7aaa14e20835a1a1338a7058be2a df250b2156c434f3390392d09b1c9563 16b28748ea4df4d9c2150843fecfba68 16b28748ea4df4d9c2150843fecfba68 4883ec0eb33c31828b7c767c806e14c7 b91e30dda1382ac7285aae3f45407cef 684271ed9684bde649abda8831d4d355Acquire Image39528cedfa926ea0c01e69ef5b2ea9b0 b6f796d82ff1b4e710c079542e89e182Get Devices65281c5ac262bf6d81768915a4a77ac0 dc6dce4a544fdca2df29d5ac0ea9906b 6870baf322929e763a5a4dd5b95638a2Source: 8c1ecd4bb896b2264e0711597d40766c 4e4da74db0fbe784cd1a8c486f2d663a18bb6ffaf0152bbe49cd8a3620346341 16b28748ea4df4d9c2150843fecfba68 dc6dce4a544fdca2df29d5ac0ea9906b 5d63dc371f715490ca7f6d2e4600ec66Pixel Type: 8c1ecd4bb896b2264e0711597d40766c 5eeb331c51742deae3114e4956cc1be1 5a07473c87748fb1bf73f23d45547ab8B & W4afa15d3069109ac30911f04c56f3338 5a07473c87748fb1bf73f23d45547ab8Gray4afa15d3069109ac30911f04c56f3338 5a07473c87748fb1bf73f23d45547ab8Color4afa15d3069109ac30911f04c56f3338 18bb6ffaf0152bbe49cd8a3620346341 16b28748ea4df4d9c2150843fecfba68 dc6dce4a544fdca2df29d5ac0ea9906b 045b2ead8e25645fdd0c39f585f1e5bfResolution: 8c1ecd4bb896b2264e0711597d40766c c0e226d2d22b2a84209a20e9fa5db602 5a07473c87748fb1bf73f23d45547ab81004afa15d3069109ac30911f04c56f3338 5a07473c87748fb1bf73f23d45547ab81504afa15d3069109ac30911f04c56f3338 5a07473c87748fb1bf73f23d45547ab82004afa15d3069109ac30911f04c56f3338 5a07473c87748fb1bf73f23d45547ab83004afa15d3069109ac30911f04c56f3338 18bb6ffaf0152bbe49cd8a3620346341 16b28748ea4df4d9c2150843fecfba68 dc6dce4a544fdca2df29d5ac0ea9906b 1ee9335f254f79d03a6febbcf5d3d46b 2c55b6d296473c35276f4ed5dfe2cf7dShow UI8c1ecd4bb896b2264e0711597d40766c 16b28748ea4df4d9c2150843fecfba68 dc6dce4a544fdca2df29d5ac0ea9906b 5ebf17639bf6105e4664ab2e1fb1f6eb 07c2649bd6a78c967126bb66652d154bADF8c1ecd4bb896b2264e0711597d40766c 16b28748ea4df4d9c2150843fecfba68 dc6dce4a544fdca2df29d5ac0ea9906b 44173729fcd0e865b8455eb3701b40b7 9fb9d3e7b5ec672c901c914afe880bd9Duplex8c1ecd4bb896b2264e0711597d40766c 16b28748ea4df4d9c2150843fecfba68 ebf9f28c318eafce2dd4c8e1bb2c253cScan Now65281c5ac262bf6d81768915a4a77ac0 6d9fd183d333544e9d5fe06824808c81Save65281c5ac262bf6d81768915a4a77ac0 684271ed9684bde649abda8831d4d355Image Tools39528cedfa926ea0c01e69ef5b2ea9b0 2326325328cfb1510e6119c7566a1325 679019de3d0cfbcca4676917ab8cd4f0 803a9dc7ffbcccf77de190935b40089a 65281c5ac262bf6d81768915a4a77ac0 becd6db3ab50097bcb2036771f348f01 b411ae39fbc39f0429a39ad945dcd511 65281c5ac262bf6d81768915a4a77ac0 c56962c63fec7fa8fb3820e921cf15bf b02f8f3397f265cf44c9fcab016cc9c7 65281c5ac262bf6d81768915a4a77ac0 16b28748ea4df4d9c2150843fecfba68 16b28748ea4df4d9c2150843fecfba68 b3be3238529c3d77b69b7fd150fd938b 46c5d3aad1e47bee946e6355bc5143b4 ec6b4fd79f2415f29f0b734dcd571fb5 16b28748ea4df4d9c2150843fecfba68 39bbc163833ae4e036fa5108a4263823 b7c1222a61bcb1861cf5c6bfedb3b7cd a1e9e9b21d1fae6be88debb6fb09c6cc 16b28748ea4df4d9c2150843fecfba68 16b28748ea4df4d9c2150843fecfba68 16b28748ea4df4d9c2150843fecfba68 16b28748ea4df4d9c2150843fecfba68 16b28748ea4df4d9c2150843fecfba68 e803653d6fd93b3996ebf69dd59af1622cacc6d41bbb37262a98f745aa00fbf0 36cc49f0c466276486e50c850b7e4956 73a6ac4ed44ffec12cee46588e518a5e
環境準備好了,下一步就是用JavaScript實作相關功能。
列舉可用的掃描器。
const ScannerType = { // TWAIN scanner type, represented by the value 0x10 TWAINSCANNER: 0x10, // WIA scanner type, represented by the value 0x20 WIASCANNER: 0x20, // 64-bit TWAIN scanner type, represented by the value 0x40 TWAINX64SCANNER: 0x40, // ICA scanner type, represented by the value 0x80 ICASCANNER: 0x80, // SANE scanner type, represented by the value 0x100 SANESCANNER: 0x100, // eSCL scanner type, represented by the value 0x200 ESCLSCANNER: 0x200, // WiFi Direct scanner type, represented by the value 0x400 WIFIDIRECTSCANNER: 0x400, // WIA-TWAIN scanner type, represented by the value 0x800 WIATWAINSCANNER: 0x800 }; let queryDevicesButton = document.getElementById("query-devices-button"); queryDevicesButton.onclick = async () => { let scannerType = ScannerType.TWAINSCANNER | ScannerType.TWAINX64SCANNER; let devices = await getDevices(host, scannerType); let select = document.getElementById("sources"); select.innerHTML = ''; for (let i = 0; i 2451ae44f6c73c136d26a84311d05bf4 { let select = document.getElementById("sources"); let device = select.value; if (device == null || device.length == 0) { alert('Please select a scanner.'); return; } let inputText = document.getElementById("inputText").value; let license = inputText.trim(); if (license == null || license.length == 0) { alert('Please input a valid license key.'); } let parameters = { license: license, device: JSON.parse(device)['device'], }; let showUICheck = document.getElementById("showUICheckId"); let pixelTypeSelect = document.getElementById("pixelTypeSelectId"); let resolutionSelect = document.getElementById("resolutionSelectId"); let adfCheck = document.getElementById("adfCheckId"); let duplexCheck = document.getElementById("duplexCheckId"); parameters.config = { IfShowUI: showUICheck.checked, PixelType: pixelTypeSelect.selectedIndex, Resolution: parseInt(resolutionSelect.value), IfFeederEnabled: adfCheck.checked, IfDuplexEnabled: duplexCheck.checked, }; let jobId = await scanDocument(host, parameters); let images = await getImages(host, jobId); for (let i = 0; i 30f38ec796a9a4f3b797e10c6aa42e38 { if (e != null && e.target != null) { let target = e.target; img.src = target.src; selectedThumbnail = target; } }); } selectedThumbnail = newImage; } } async function scanDocument(host, parameters, timeout = 30) { let url = host + '/DWTAPI/ScanJobs?timeout=' + timeout; try { let response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(parameters) }); if (response.ok) { let jobId = await response.text(); return jobId; } else { return ''; } } catch (error) { alert(error); return ''; } } async function getImages(host, jobId) { let images = []; let url = host + '/DWTAPI/ScanJobs/' + jobId + '/NextDocument'; while (true) { try { let response = await fetch(url); if (response.status == 200) { const arrayBuffer = await response.arrayBuffer(); const blob = new Blob([arrayBuffer], { type: response.type }); const imageUrl = URL.createObjectURL(blob); images.push(imageUrl); } else { break; } } catch (error) { console.error('No more images.'); break; } } return images; }
說明
將掃描影像旋轉-90 或 90 度。
let rotateLeftButton = document.getElementById("rotate-left-button"); rotateLeftButton.onclick = () => { let img = document.getElementById('document-image'); img.src = rotateImage('document-image', -90); selectedThumbnail.src = img.src; } let rotateRightButton = document.getElementById("rotate-right-button"); rotateRightButton.onclick = () => { let img = document.getElementById('document-image'); img.src = rotateImage('document-image', 90); selectedThumbnail.src = img.src; } function rotateImage (imageId, angle) { const image = document.getElementById(imageId); const canvas = document.createElement('canvas'); const context = canvas.getContext('2d'); const imageWidth = image.naturalWidth; const imageHeight = image.naturalHeight; // Calculate the new rotation let rotation = 0; rotation = (rotation + angle) % 360; // Adjust canvas size for rotation if (rotation === 90 || rotation === -270 || rotation === 270) { canvas.width = imageHeight; canvas.height = imageWidth; } else if (rotation === 180 || rotation === -180) { canvas.width = imageWidth; canvas.height = imageHeight; } else if (rotation === 270 || rotation === -90) { canvas.width = imageHeight; canvas.height = imageWidth; } else { canvas.width = imageWidth; canvas.height = imageHeight; } // Clear the canvas context.clearRect(0, 0, canvas.width, canvas.height); // Draw the rotated image on the canvas context.save(); if (rotation === 90 || rotation === -270) { context.translate(canvas.width, 0); context.rotate(90 * Math.PI / 180); } else if (rotation === 180 || rotation === -180) { context.translate(canvas.width, canvas.height); context.rotate(180 * Math.PI / 180); } else if (rotation === 270 || rotation === -90) { context.translate(0, canvas.height); context.rotate(270 * Math.PI / 180); } context.drawImage(image, 0, 0); context.restore(); return canvas.toDataURL(); }
刪除所有掃描影像,包括主影像和縮圖,並重設資料數組。
let deleteButton = document.getElementById("delete-button"); deleteButton.onclick = async () => { let img = document.getElementById('document-image'); img.src = 'images/default.png'; data = []; let thumbnails = document.getElementById("thumb-box"); thumbnails.innerHTML = ''; }
出於安全考慮,直接在 JavaScript 中保存映像受到限制。因此,我們需要在 C# 和 JavaScript 之間進行互通性來完成此任務。
建立一個 JavaScript 函數,將掃描的映像轉換為 Base64 字串。
function getBase64Image() { var img = document.getElementById('document-image'); var canvas = document.createElement('canvas'); canvas.width = img.naturalWidth; canvas.height = img.naturalHeight; var ctx = canvas.getContext('2d'); ctx.drawImage(img, 0, 0); var dataURL = canvas.toDataURL('image/png'); var base64 = dataURL.split(',')[1]; return base64; }
點選儲存按鈕時,設定window.location.href以觸發WebView控制項的OnWebViewNaviged事件處理程序。
let saveButton = document.getElementById("save-button"); saveButton.onclick = async () => { window.location.href = 'invoke://CallCSharpFunction'; }
在 OnWebViewNaviged 事件處理程序中,呼叫 EvaluateJavaScriptAsync 從 JavaScript 檢索 Base64 影像資料並將其儲存到檔案中。
private async void OnWebViewNavigated(object sender, WebNavigatingEventArgs e) { if (e.Url.StartsWith("invoke://callcsharpfunction")) { var base64String = await WebView.EvaluateJavaScriptAsync("getBase64Image()"); CallCSharpFunction(base64String); } } private void CallCSharpFunction(string base64String) { if (!string.IsNullOrEmpty(base64String)) { try { byte[] imageBytes = Convert.FromBase64String(base64String); var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), GenerateFilename()); File.WriteAllBytes(filePath, imageBytes); DisplayAlert("Success", "Image saved to: " + filePath, "OK"); } catch (Exception ex) { DisplayAlert("Error", ex.Message, "OK"); } } else { DisplayAlert("Failure", "No image data found", "OK"); } } private string GenerateFilename() { DateTime now = DateTime.Now; string timestamp = now.ToString("yyyyMMdd_HHmmss"); return $"image_{timestamp}.png"; }
Note: Do not pass the base64 string directly to the C# function via window.location.href, as the string may be too long and cause an error. Instead, return the base64 string when calling EvaluateJavaScriptAsync from the C# function.
Press F5 in Visual Studio or Visual Studio Code to run the .NET document scanner application on Windows or macOS.
https://github.com/yushulx/dotnet-twain-wia-sane-scanner/tree/main/examples/MauiWebView
以上是從 .NET MAUI Blazor 切換到 WebView 控制項進行文件掃描的詳細內容。更多資訊請關注PHP中文網其他相關文章!