diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 65eaa7271..873f6bda9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,6 +72,213 @@ jobs: - name: Restore run: dotnet restore "Source/Krypton Components/Krypton Toolkit Suite 2022 - VS2022.sln" + - name: Setup WebView2 SDK + run: | + # Create WebView2SDK directory + mkdir WebView2SDK + + # Get latest WebView2 SDK version using multiple API methods + $packageId = "Microsoft.Web.WebView2" + Write-Host "Fetching latest stable WebView2 version using multiple API methods..." -ForegroundColor Cyan + + $latestVersion = $null + + # Method 1: Try the search API first (most reliable for latest versions) + try { + Write-Host "Trying search API (most reliable)..." -ForegroundColor Cyan + $searchUrl = "https://azuresearch-usnc.nuget.org/query?q=$packageId&prerelease=false&semVerLevel=2.0.0&take=1" + $searchResponse = Invoke-RestMethod -Uri $searchUrl -Method Get + + if ($searchResponse.data -and $searchResponse.data.Count -gt 0) { + $packageData = $searchResponse.data[0] + $latestVersion = $packageData.version + Write-Host "Found latest stable version via search API: $latestVersion" -ForegroundColor Green + } + } catch { + Write-Host "Search API failed: $($_.Exception.Message)" -ForegroundColor Yellow + } + + # Method 2: Try alternative search endpoint if primary failed + if (-not $latestVersion) { + try { + Write-Host "Trying alternative search endpoint..." -ForegroundColor Cyan + $altSearchUrl = "https://azuresearch-ussc.nuget.org/query?q=$packageId&prerelease=false&semVerLevel=2.0.0&take=1" + $altSearchResponse = Invoke-RestMethod -Uri $altSearchUrl -Method Get + + if ($altSearchResponse.data -and $altSearchResponse.data.Count -gt 0) { + $packageData = $altSearchResponse.data[0] + $latestVersion = $packageData.version + Write-Host "Found latest stable version via alternative search API: $latestVersion" -ForegroundColor Green + } + } catch { + Write-Host "Alternative search API failed: $($_.Exception.Message)" -ForegroundColor Yellow + } + } + + # Method 3: Try the flatcontainer API (may have stale data) + if (-not $latestVersion) { + try { + $apiUrl = "https://api.nuget.org/v3-flatcontainer/$packageId/index.json" + Write-Host "Trying flatcontainer API (may have stale data)..." -ForegroundColor Cyan + $response = Invoke-RestMethod -Uri $apiUrl -Method Get + + if ($response.versions) { + # Filter for stable versions only (no prerelease identifiers) + $stableVersions = $response.versions | Where-Object { $_ -notmatch '[-]' -and $_ -match '^\d+\.\d+\.\d+$' } + if ($stableVersions) { + # Sort by version number and get the latest + $latestVersion = $stableVersions | Sort-Object { [System.Version]$_ } -Descending | Select-Object -First 1 + Write-Host "Found latest stable version via flatcontainer API: $latestVersion" -ForegroundColor Green + } + } + } catch { + Write-Host "Flatcontainer API failed: $($_.Exception.Message)" -ForegroundColor Yellow + } + } + + # Method 4: Try registration API (with better error handling) + if (-not $latestVersion) { + try { + Write-Host "Trying registration API..." -ForegroundColor Cyan + $regUrl = "https://api.nuget.org/v3/registration5-semver1/$($packageId.ToLower())/index.json" + $regResponse = Invoke-RestMethod -Uri $regUrl -Method Get + + if ($regResponse.items -and $regResponse.items.Count -gt 0) { + # Filter out prerelease versions and sort by version + $stableItems = $regResponse.items | Where-Object { + $_.upper -notmatch '[-]' -and $_.upper -match '^\d+\.\d+\.\d+$' + } + if ($stableItems) { + $latestItem = $stableItems | Sort-Object { [System.Version]$_.upper } -Descending | Select-Object -First 1 + if ($latestItem.items -and $latestItem.items.Count -gt 0) { + $latestVersion = $latestItem.items[0].catalogEntry.version + Write-Host "Found latest stable version via registration API: $latestVersion" -ForegroundColor Green + } + } + } + } catch { + Write-Host "Registration API failed: $($_.Exception.Message)" -ForegroundColor Yellow + } + } + + # Final fallback + if (-not $latestVersion) { + Write-Host "All API methods failed, using hardcoded fallback version: 1.0.3485.44" -ForegroundColor Red + $latestVersion = "1.0.3485.44" + } + + Write-Host "Using WebView2 SDK version: $latestVersion" -ForegroundColor Green + + # Add WebView2 package to project + Write-Host "Adding WebView2 package to project..." + Write-Host "Attempting to install version: $latestVersion" + + # Verify the package version exists by trying to get package info + try { + $packageInfoUrl = "https://api.nuget.org/v3-flatcontainer/$packageId/$latestVersion/$packageId.$latestVersion.nupkg" + $packageCheck = Invoke-WebRequest -Uri $packageInfoUrl -Method Head -UseBasicParsing + Write-Host "Package version $latestVersion verified as available" -ForegroundColor Green + } catch { + Write-Host "Package version $latestVersion not found, trying latest available..." -ForegroundColor Yellow + # Try to get the latest available version + try { + $searchUrl = "https://azuresearch-usnc.nuget.org/query?q=$packageId&prerelease=false&semVerLevel=2.0.0&take=1" + $searchResponse = Invoke-RestMethod -Uri $searchUrl -Method Get + if ($searchResponse.data -and $searchResponse.data.Count -gt 0) { + $latestVersion = $searchResponse.data[0].version + Write-Host "Using verified latest version: $latestVersion" -ForegroundColor Green + } + } catch { + Write-Host "Could not verify package version, proceeding with $latestVersion" -ForegroundColor Yellow + } + } + + dotnet add "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" package Microsoft.Web.WebView2 --version $latestVersion + + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to add WebView2 package to project" + exit 1 + } + + # Restore packages to ensure they're downloaded + Write-Host "Restoring NuGet packages..." + dotnet restore "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" + + if ($LASTEXITCODE -ne 0) { + Write-Error "Failed to restore NuGet packages" + exit 1 + } + + # Find and copy assemblies dynamically + $nugetBasePath = "$env:USERPROFILE\.nuget\packages\microsoft.web.webview2\$latestVersion" + Write-Host "Searching for WebView2 assemblies in: $nugetBasePath" + + # Verify the package directory exists + if (-not (Test-Path $nugetBasePath)) { + Write-Error "NuGet package directory not found: $nugetBasePath" + Write-Host "Available packages:" + Get-ChildItem "$env:USERPROFILE\.nuget\packages\microsoft.web.webview2" -ErrorAction SilentlyContinue | ForEach-Object { Write-Host " - $($_.Name)" } + exit 1 + } + + # Find Core DLL + $coreDll = Get-ChildItem -Path $nugetBasePath -Recurse -Name "Microsoft.Web.WebView2.Core.dll" | Select-Object -First 1 + if ($coreDll) { + $corePath = Join-Path $nugetBasePath $coreDll + if (Test-Path $corePath) { + Copy-Item $corePath "WebView2SDK\" + Write-Host "Copied Microsoft.Web.WebView2.Core.dll from: $corePath" + } else { + Write-Error "Core DLL file not found at: $corePath" + exit 1 + } + } else { + Write-Error "Microsoft.Web.WebView2.Core.dll not found in package" + Write-Host "Available files in package:" + Get-ChildItem -Path $nugetBasePath -Recurse | ForEach-Object { Write-Host " - $($_.FullName)" } + exit 1 + } + + # Find WinForms DLL + $winFormsDll = Get-ChildItem -Path $nugetBasePath -Recurse -Name "Microsoft.Web.WebView2.WinForms.dll" | Select-Object -First 1 + if ($winFormsDll) { + $winFormsPath = Join-Path $nugetBasePath $winFormsDll + if (Test-Path $winFormsPath) { + Copy-Item $winFormsPath "WebView2SDK\" + Write-Host "Copied Microsoft.Web.WebView2.WinForms.dll from: $winFormsPath" + } else { + Write-Error "WinForms DLL file not found at: $winFormsPath" + exit 1 + } + } else { + Write-Error "Microsoft.Web.WebView2.WinForms.dll not found in package" + exit 1 + } + + # Find WebView2Loader DLL + $loaderDll = Get-ChildItem -Path $nugetBasePath -Recurse -Name "WebView2Loader.dll" | Select-Object -First 1 + if ($loaderDll) { + $loaderPath = Join-Path $nugetBasePath $loaderDll + if (Test-Path $loaderPath) { + Copy-Item $loaderPath "WebView2SDK\" + Write-Host "Copied WebView2Loader.dll from: $loaderPath" + } else { + Write-Error "Loader DLL file not found at: $loaderPath" + exit 1 + } + } else { + Write-Error "WebView2Loader.dll not found in package" + exit 1 + } + + # Verify all files were copied + Write-Host "WebView2 SDK setup completed. Files copied:" + Get-ChildItem "WebView2SDK\*.dll" | ForEach-Object { Write-Host " - $($_.Name)" } + + # Remove NuGet package reference + Write-Host "Removing temporary NuGet package reference..." + dotnet remove "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" package Microsoft.Web.WebView2 + - name: Build run: msbuild "Scripts/nightly.proj" /t:Rebuild /p:Configuration=Release /p:Platform="Any CPU" diff --git a/Documents/Changelog/Changelog.md b/Documents/Changelog/Changelog.md index f6b5bf3cb..937ad0c9a 100644 --- a/Documents/Changelog/Changelog.md +++ b/Documents/Changelog/Changelog.md @@ -3,6 +3,7 @@ ==== ## 2025-11-xx - Build 2511 (V10 - alpha) - November 2025 +* Implemented [#1026](https://github.com/Krypton-Suite/Standard-Toolkit/issues/1026), Krypton version of `WebView2` * Resolved [#2461](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2461), (feat) Add `KryptonCalcInput` edit+dropdown control. * Resolved [#2480](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2480). `KryptonForm`'s 'InternalPanel' designer issues * Resolved [#2512](https://github.com/Krypton-Suite/Standard-Toolkit/issues/2512), Added borders with straight corners (`LinearBorder2`) and adjusted the colors in the `KryptonRibbon` in the `Microsoft365` themes. Adjusted the design of the `RibbonQATButton` diff --git a/Scripts/Check-WebView2Version.cmd b/Scripts/Check-WebView2Version.cmd new file mode 100644 index 000000000..6277b67da --- /dev/null +++ b/Scripts/Check-WebView2Version.cmd @@ -0,0 +1,19 @@ +@echo off +echo Checking WebView2 SDK version... +echo. + +if exist "WebView2SDK\Microsoft.Web.WebView2.Core.dll" ( + echo Current installed version: + powershell -Command "Get-ItemProperty 'WebView2SDK\Microsoft.Web.WebView2.Core.dll' | Select-Object -ExpandProperty VersionInfo | Select-Object -ExpandProperty FileVersion" + echo. + echo Latest available version: + powershell -ExecutionPolicy Bypass -File "%~dp0Get-LatestWebView2Version.ps1" +) else ( + echo WebView2 SDK is not installed. + echo. + echo Latest available version: + powershell -ExecutionPolicy Bypass -File "%~dp0Get-LatestWebView2Version.ps1" +) + +echo. +pause diff --git a/Scripts/Get-LatestWebView2Version.ps1 b/Scripts/Get-LatestWebView2Version.ps1 new file mode 100644 index 000000000..c179a55bf --- /dev/null +++ b/Scripts/Get-LatestWebView2Version.ps1 @@ -0,0 +1,106 @@ +# Get-LatestWebView2Version.ps1 +# Script to get the latest stable version of Microsoft.Web.WebView2 from NuGet + +param( + [switch]$Prerelease = $false +) + +try { + # Use NuGet API to get package information + $packageId = "Microsoft.Web.WebView2" + + Write-Host "Fetching latest stable version information for $packageId..." -ForegroundColor Green + + # Try multiple approaches to get the latest stable version + $latestVersion = $null + + # Method 1: Try the search API first (most reliable for latest versions) + try { + Write-Host "Trying search API (most reliable)..." -ForegroundColor Cyan + $searchUrl = "https://azuresearch-usnc.nuget.org/query?q=$packageId&prerelease=false&semVerLevel=2.0.0&take=1" + $searchResponse = Invoke-RestMethod -Uri $searchUrl -Method Get + + if ($searchResponse.data -and $searchResponse.data.Count -gt 0) { + $packageData = $searchResponse.data[0] + $latestVersion = $packageData.version + Write-Host "Found latest stable version via search API: $latestVersion" -ForegroundColor Green + } + } catch { + Write-Host "Search API failed: $($_.Exception.Message)" -ForegroundColor Yellow + } + + # Method 2: Try alternative search endpoint if primary failed + if (-not $latestVersion) { + try { + Write-Host "Trying alternative search endpoint..." -ForegroundColor Cyan + $altSearchUrl = "https://azuresearch-ussc.nuget.org/query?q=$packageId&prerelease=false&semVerLevel=2.0.0&take=1" + $altSearchResponse = Invoke-RestMethod -Uri $altSearchUrl -Method Get + + if ($altSearchResponse.data -and $altSearchResponse.data.Count -gt 0) { + $packageData = $altSearchResponse.data[0] + $latestVersion = $packageData.version + Write-Host "Found latest stable version via alternative search API: $latestVersion" -ForegroundColor Green + } + } catch { + Write-Host "Alternative search API failed: $($_.Exception.Message)" -ForegroundColor Yellow + } + } + + # Method 3: Try the flatcontainer API (may have stale data) + if (-not $latestVersion) { + try { + $apiUrl = "https://api.nuget.org/v3-flatcontainer/$packageId/index.json" + Write-Host "Trying flatcontainer API (may have stale data)..." -ForegroundColor Cyan + $response = Invoke-RestMethod -Uri $apiUrl -Method Get + + if ($response.versions) { + # Filter for stable versions only (no prerelease identifiers) + $stableVersions = $response.versions | Where-Object { $_ -notmatch '[-]' -and $_ -match '^\d+\.\d+\.\d+$' } + if ($stableVersions) { + # Sort by version number and get the latest + $latestVersion = $stableVersions | Sort-Object { [Version]$_ } -Descending | Select-Object -First 1 + Write-Host "Found latest stable version via flatcontainer API: $latestVersion" -ForegroundColor Green + } + } + } catch { + Write-Host "Flatcontainer API failed: $($_.Exception.Message)" -ForegroundColor Yellow + } + } + + # Method 4: Try registration API (with better error handling) + if (-not $latestVersion) { + try { + Write-Host "Trying registration API..." -ForegroundColor Cyan + $regUrl = "https://api.nuget.org/v3/registration5-semver1/$($packageId.ToLower())/index.json" + $regResponse = Invoke-RestMethod -Uri $regUrl -Method Get + + if ($regResponse.items -and $regResponse.items.Count -gt 0) { + # Filter out prerelease versions and sort by version + $stableItems = $regResponse.items | Where-Object { + $_.upper -notmatch '[-]' -and $_.upper -match '^\d+\.\d+\.\d+$' + } + if ($stableItems) { + $latestItem = $stableItems | Sort-Object { [Version]$_.upper } -Descending | Select-Object -First 1 + if ($latestItem.items -and $latestItem.items.Count -gt 0) { + $latestVersion = $latestItem.items[0].catalogEntry.version + Write-Host "Found latest stable version via registration API: $latestVersion" -ForegroundColor Green + } + } + } + } catch { + Write-Host "Registration API failed: $($_.Exception.Message)" -ForegroundColor Yellow + } + } + + if ($latestVersion) { + Write-Host "Latest version: $latestVersion" -ForegroundColor Yellow + return $latestVersion + } else { + Write-Error "No suitable version found" + return $null + } +} +catch { + Write-Error "Failed to fetch version information: $($_.Exception.Message)" + return $null +} diff --git a/Scripts/Setup-WebView2SDK.cmd b/Scripts/Setup-WebView2SDK.cmd new file mode 100644 index 000000000..3f5b6871b --- /dev/null +++ b/Scripts/Setup-WebView2SDK.cmd @@ -0,0 +1,101 @@ +@echo off +echo Setting up WebView2 SDK for KryptonWebView2 control... +echo. + +REM Check if WebView2SDK directory exists and has the required files +if exist "WebView2SDK\Microsoft.Web.WebView2.Core.dll" ( + if exist "WebView2SDK\Microsoft.Web.WebView2.WinForms.dll" ( + if exist "WebView2SDK\WebView2Loader.dll" ( + echo WebView2 SDK assemblies are already present! + echo. + echo Files found: + dir WebView2SDK\*.dll + echo. + echo The KryptonWebView2 control should compile successfully. + echo You can now build the solution and use the control. + echo. + pause + exit /b 0 + ) + ) +) + +REM Create WebView2SDK directory if it doesn't exist +if not exist "WebView2SDK" ( + echo Creating WebView2SDK directory... + mkdir WebView2SDK +) + +echo. +echo WebView2 SDK assemblies are missing. Setting up automatically... +echo. + +REM Get the latest WebView2 SDK version +echo Detecting latest WebView2 SDK version... +for /f "delims=" %%i in ('powershell -ExecutionPolicy Bypass -File "%~dp0Get-LatestWebView2Version.ps1"') do ( + set "WEBVIEW2_VERSION=%%i" +) + +if "%WEBVIEW2_VERSION%"=="" ( + echo Failed to detect latest version, using fallback version 1.0.2478.35 + set WEBVIEW2_VERSION=1.0.2478.35 +) else ( + echo Using WebView2 SDK version: %WEBVIEW2_VERSION% +) + +REM Try to download and setup WebView2 SDK automatically +echo Attempting to install WebView2 SDK via NuGet... +dotnet add "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" package Microsoft.Web.WebView2 --version %WEBVIEW2_VERSION% + +if %ERRORLEVEL% EQU 0 ( + echo. + echo Restoring NuGet packages to ensure WebView2 SDK is downloaded... + dotnet restore "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" + + if %ERRORLEVEL% NEQ 0 ( + echo ERROR: Failed to restore NuGet packages + exit /b 1 + ) + echo. + echo Copying WebView2 assemblies to WebView2SDK folder... + +REM Copy the assemblies from NuGet cache +echo Searching for WebView2 assemblies in NuGet cache... +echo USERPROFILE: %USERPROFILE% +echo WEBVIEW2_VERSION: %WEBVIEW2_VERSION% + +REM Use PowerShell to find and copy the assemblies +powershell -Command "& { $nugetPath = \"$env:USERPROFILE\.nuget\packages\microsoft.web.webview2\%WEBVIEW2_VERSION%\"; Write-Host \"NuGet path: $nugetPath\"; if (Test-Path $nugetPath) { Write-Host \"Package directory found\"; $coreDll = Get-ChildItem -Path $nugetPath -Recurse -Name 'Microsoft.Web.WebView2.Core.dll' | Select-Object -First 1; if ($coreDll) { $corePath = Join-Path $nugetPath $coreDll; Copy-Item $corePath 'WebView2SDK\'; Write-Host \"Copied Microsoft.Web.WebView2.Core.dll\" } else { Write-Error \"Core DLL not found\" }; $winFormsDll = Get-ChildItem -Path $nugetPath -Recurse -Name 'Microsoft.Web.WebView2.WinForms.dll' | Select-Object -First 1; if ($winFormsDll) { $winFormsPath = Join-Path $nugetPath $winFormsDll; Copy-Item $winFormsPath 'WebView2SDK\'; Write-Host \"Copied Microsoft.Web.WebView2.WinForms.dll\" } else { Write-Error \"WinForms DLL not found\" }; $loaderDll = Get-ChildItem -Path $nugetPath -Recurse -Name 'WebView2Loader.dll' | Select-Object -First 1; if ($loaderDll) { $loaderPath = Join-Path $nugetPath $loaderDll; Copy-Item $loaderPath 'WebView2SDK\'; Write-Host \"Copied WebView2Loader.dll\" } else { Write-Error \"Loader DLL not found\" } } else { Write-Error \"Package directory not found: $nugetPath\" } }" + + REM Remove the NuGet package reference since we're using local assemblies + dotnet remove "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" package Microsoft.Web.WebView2 + + REM Update project file with the latest version + echo Updating project file with latest version... + powershell -ExecutionPolicy Bypass -File "%~dp0Update-WebView2ProjectVersion.ps1" + + echo. + echo WebView2 SDK setup completed successfully! + echo. + echo Installed version: %WEBVIEW2_VERSION% + echo Files copied: + dir WebView2SDK\*.dll + echo. + echo You can now build the solution and use the KryptonWebView2 control. +) else ( + echo. + echo Automatic setup failed. Please follow manual setup instructions: + echo. + echo MANUAL SETUP INSTRUCTIONS: + echo 1. Download the WebView2 SDK from: https://developer.microsoft.com/en-us/microsoft-edge/webview2/ + echo 2. Extract the following files to the WebView2SDK folder: + echo - Microsoft.Web.WebView2.Core.dll + echo - Microsoft.Web.WebView2.WinForms.dll + echo - WebView2Loader.dll + echo. + echo Alternative: Use NuGet Package Manager to install Microsoft.Web.WebView2 + echo and copy the assemblies from the packages folder to WebView2SDK. +) + +echo. +pause diff --git a/Scripts/Update-WebView2ProjectVersion.ps1 b/Scripts/Update-WebView2ProjectVersion.ps1 new file mode 100644 index 000000000..107c0a687 --- /dev/null +++ b/Scripts/Update-WebView2ProjectVersion.ps1 @@ -0,0 +1,84 @@ +# Update-WebView2ProjectVersion.ps1 +# Script to update the project file with the latest WebView2 SDK version + +param( + [string]$ProjectPath = "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj", + [switch]$WhatIf = $false +) + +function Get-LatestWebView2Version { + try { + $packageId = "Microsoft.Web.WebView2" + $apiUrl = "https://api.nuget.org/v3-flatcontainer/$packageId/index.json" + + Write-Host "Fetching latest version information for $packageId..." -ForegroundColor Green + + $response = Invoke-RestMethod -Uri $apiUrl -Method Get + + # Only stable versions (no prerelease identifiers) + $stableVersions = $response.versions | Where-Object { $_ -notmatch '[-]' } | + Sort-Object { [Version]$_ } -Descending + $latestVersion = $stableVersions[0] + + if ($latestVersion) { + Write-Host "Latest stable version: $latestVersion" -ForegroundColor Yellow + return $latestVersion + } else { + Write-Error "No stable version found" + return $null + } + } + catch { + Write-Error "Failed to fetch version information: $($_.Exception.Message)" + return $null + } +} + +function Update-ProjectFile { + param( + [string]$ProjectPath, + [string]$NewVersion, + [switch]$WhatIf + ) + + if (-not (Test-Path $ProjectPath)) { + Write-Error "Project file not found: $ProjectPath" + return $false + } + + $content = Get-Content $ProjectPath -Raw + + # Update .NET Framework references + $netFrameworkPattern = 'Microsoft\.Web\.WebView2\.Core, Version=([^,]+), Culture=neutral' + $netFrameworkReplacement = "Microsoft.Web.WebView2.Core, Version=$NewVersion, Culture=neutral" + + $updatedContent = $content -replace $netFrameworkPattern, $netFrameworkReplacement + + if ($updatedContent -ne $content) { + if ($WhatIf) { + Write-Host "Would update project file with version $NewVersion" -ForegroundColor Cyan + } else { + Set-Content $ProjectPath $updatedContent -NoNewline + Write-Host "Updated project file with version $NewVersion" -ForegroundColor Green + } + return $true + } else { + Write-Warning "No version references found to update in project file" + return $false + } +} + +# Main execution +$latestVersion = Get-LatestWebView2Version + +if ($latestVersion) { + $updated = Update-ProjectFile -ProjectPath $ProjectPath -NewVersion $latestVersion -WhatIf:$WhatIf + + if ($updated -and -not $WhatIf) { + Write-Host "Project file updated successfully!" -ForegroundColor Green + Write-Host "You may need to rebuild the solution for changes to take effect." -ForegroundColor Yellow + } +} else { + Write-Error "Could not determine latest WebView2 version" + exit 1 +} diff --git a/Scripts/Update-WebView2SDK.cmd b/Scripts/Update-WebView2SDK.cmd new file mode 100644 index 000000000..1e7ca8209 --- /dev/null +++ b/Scripts/Update-WebView2SDK.cmd @@ -0,0 +1,139 @@ +@echo off +echo Updating to latest WebView2 SDK version... +echo. + +REM Check if WebView2SDK directory exists +if not exist "WebView2SDK" ( + echo WebView2SDK directory not found. Please run Setup-WebView2SDK.cmd first. + echo. + pause + exit /b 1 +) + +REM Get the latest WebView2 SDK version +echo Detecting latest WebView2 SDK version... +for /f "delims=" %%i in ('powershell -ExecutionPolicy Bypass -File "%~dp0Get-LatestWebView2Version.ps1"') do ( + set "WEBVIEW2_VERSION=%%i" +) + +if "%WEBVIEW2_VERSION%"=="" ( + echo Failed to detect latest version, using fallback version 1.0.2478.35 + set WEBVIEW2_VERSION=1.0.2478.35 +) else ( + echo Using WebView2 SDK version: %WEBVIEW2_VERSION% +) + +echo. +echo Current WebView2 SDK version: +if exist "WebView2SDK\Microsoft.Web.WebView2.Core.dll" ( + powershell -Command "Get-ItemProperty 'WebView2SDK\Microsoft.Web.WebView2.Core.dll' | Select-Object -ExpandProperty VersionInfo | Select-Object -ExpandProperty FileVersion" +) else ( + echo Not installed +) +echo. +echo Updating WebView2 SDK to version %WEBVIEW2_VERSION%... + +REM Temporarily add the latest version via NuGet +dotnet add "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" package Microsoft.Web.WebView2 --version %WEBVIEW2_VERSION% + +if %ERRORLEVEL% EQU 0 ( + echo. + echo Restoring NuGet packages to ensure WebView2 SDK is downloaded... + dotnet restore "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" + + if %ERRORLEVEL% NEQ 0 ( + echo ERROR: Failed to restore NuGet packages + exit /b 1 + ) + + echo Copying updated WebView2 assemblies... + + REM Copy the assemblies from NuGet cache + echo Searching for WebView2 assemblies in NuGet cache... + for /f "usebackq tokens=*" %%i in (`powershell -Command "Write-Output $env:USERPROFILE"`) do set "USER_PROFILE=%%i" + set "NUGET_PATH=%USER_PROFILE%\.nuget\packages\microsoft.web.webview2\%WEBVIEW2_VERSION%" + echo NuGet path: %NUGET_PATH% + + REM Verify the package directory exists + if not exist "%NUGET_PATH%" ( + echo ERROR: NuGet package directory not found: %NUGET_PATH% + echo Available packages: + dir "%USERPROFILE%\.nuget\packages\microsoft.web.webview2" /b 2>nul + exit /b 1 + ) + + REM Find and copy Core DLL + for /f "delims=" %%i in ('powershell -Command "Get-ChildItem -Path '%NUGET_PATH%' -Recurse -Name 'Microsoft.Web.WebView2.Core.dll' | Select-Object -First 1"') do ( + set "CORE_DLL=%%i" + ) + if defined CORE_DLL ( + if exist "%NUGET_PATH%\%CORE_DLL%" ( + copy "%NUGET_PATH%\%CORE_DLL%" "WebView2SDK\" >nul 2>&1 + echo Copied Microsoft.Web.WebView2.Core.dll + ) else ( + echo ERROR: Core DLL file not found at: %NUGET_PATH%\%CORE_DLL% + exit /b 1 + ) + ) else ( + echo ERROR: Microsoft.Web.WebView2.Core.dll not found in package + echo Available files in package: + dir "%NUGET_PATH%" /s /b + exit /b 1 + ) + + REM Find and copy WinForms DLL + for /f "delims=" %%i in ('powershell -Command "Get-ChildItem -Path '%NUGET_PATH%' -Recurse -Name 'Microsoft.Web.WebView2.WinForms.dll' | Select-Object -First 1"') do ( + set "WINFORMS_DLL=%%i" + ) + if defined WINFORMS_DLL ( + if exist "%NUGET_PATH%\%WINFORMS_DLL%" ( + copy "%NUGET_PATH%\%WINFORMS_DLL%" "WebView2SDK\" >nul 2>&1 + echo Copied Microsoft.Web.WebView2.WinForms.dll + ) else ( + echo ERROR: WinForms DLL file not found at: %NUGET_PATH%\%WINFORMS_DLL% + exit /b 1 + ) + ) else ( + echo ERROR: Microsoft.Web.WebView2.WinForms.dll not found in package + exit /b 1 + ) + + REM Find and copy WebView2Loader DLL + for /f "delims=" %%i in ('powershell -Command "Get-ChildItem -Path '%NUGET_PATH%' -Recurse -Name 'WebView2Loader.dll' | Select-Object -First 1"') do ( + set "LOADER_DLL=%%i" + ) + if defined LOADER_DLL ( + if exist "%NUGET_PATH%\%LOADER_DLL%" ( + copy "%NUGET_PATH%\%LOADER_DLL%" "WebView2SDK\" >nul 2>&1 + echo Copied WebView2Loader.dll + ) else ( + echo ERROR: Loader DLL file not found at: %NUGET_PATH%\%LOADER_DLL% + exit /b 1 + ) + ) else ( + echo ERROR: WebView2Loader.dll not found in package + exit /b 1 + ) + + REM Remove the NuGet package reference + dotnet remove "Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj" package Microsoft.Web.WebView2 + + REM Update project file with the latest version + echo Updating project file with latest version... + powershell -ExecutionPolicy Bypass -File "%~dp0Update-WebView2ProjectVersion.ps1" + + echo. + echo WebView2 SDK updated successfully! + echo. + echo Installed version: %WEBVIEW2_VERSION% + echo Updated files: + dir WebView2SDK\*.dll + echo. + echo You may need to rebuild the solution for changes to take effect. +) else ( + echo. + echo Failed to update WebView2 SDK. Please check your internet connection and try again. +) + +echo. +pause diff --git a/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonWebView2.cs b/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonWebView2.cs new file mode 100644 index 000000000..8917238f6 --- /dev/null +++ b/Source/Krypton Components/Krypton.Toolkit/Controls Toolkit/KryptonWebView2.cs @@ -0,0 +1,419 @@ +#region BSD License +/* + * + * Original BSD 3-Clause License (https://github.com/ComponentFactory/Krypton/blob/master/LICENSE) + * © Component Factory Pty Ltd, 2006 - 2016, (Version 4.5.0.0) All rights reserved. + * + * New BSD 3-Clause License (https://github.com/Krypton-Suite/Standard-Toolkit/blob/master/LICENSE) + * Modifications by Peter Wagner (aka Wagnerp), Simon Coghlan (aka Smurf-IV), Giduac & Ahmed Abdelhameed, tobitege et al. 2025 - 2025. All rights reserved. + * + */ +#endregion + +#if WEBVIEW2_AVAILABLE +// Do not remove this using statement, it is used for the WebView2 control +using Microsoft.Web.WebView2.WinForms; +#endif + +namespace Krypton.Toolkit; + +#if WEBVIEW2_AVAILABLE +/// +/// Provide a WebView2 control with Krypton styling applied. +/// +/// +/// KryptonWebView2 is a modern web browser control that integrates Microsoft's WebView2 engine +/// with the Krypton Toolkit's theming system. It provides a consistent look and feel with other +/// Krypton controls while offering modern web rendering capabilities. +/// +/// +/// +/// Key Features: +/// - Modern web engine based on Microsoft Edge Chromium +/// - Full integration with Krypton theming system +/// - KryptonContextMenu support for custom right-click menus +/// - Designer support for Visual Studio +/// - Async navigation and JavaScript execution capabilities +/// - Customizable zoom levels and background colors +/// +/// +/// +/// Usage Example: +/// +/// var webView = new KryptonWebView2(); +/// webView.KryptonContextMenu = myKryptonContextMenu; +/// +/// // Initialize and navigate +/// await webView.EnsureCoreWebView2Async(); +/// webView.CoreWebView2?.Navigate("https://example.com"); +/// +/// +/// +/// +/// Requirements: +/// - WebView2 Runtime must be installed on the target system +/// - Compatible with .NET Framework 4.7.2+ and .NET 8+ +/// - Windows platform only +/// +/// +/// +/// See also: for legacy WebBrowser control with Krypton theming. +/// +/// +[ToolboxItem(true)] +[ToolboxBitmap(typeof(KryptonWebView2), "ToolboxBitmaps.WebView2.bmp")] +[Designer(typeof(KryptonWebView2Designer))] +[DesignerCategory(@"code")] +[Description(@"Enables the user to browse web pages using the modern WebView2 engine with Krypton theming support.")] +[CLSCompliant(false)] +public class KryptonWebView2 : WebView2 +{ + #region Instance Fields + + private PaletteBase _palette; + private readonly PaletteMode _paletteMode = PaletteMode.Global; + private KryptonContextMenu? _kryptonContextMenu; + private IRenderer _renderer; + + #endregion + + #region Identity + + /// + /// Initialize a new instance of the KryptonWebView2 class. + /// + /// + /// + /// The constructor initializes the control with Krypton-specific styling and behavior: + /// + /// + /// Enables double buffering for smooth rendering + /// Sets up resize redraw for proper repainting + /// Initializes the Krypton palette system + /// Registers for global palette change notifications + /// + /// + /// Note: The WebView2 engine itself is not initialized until EnsureCoreWebView2Async() + /// is called. This allows for better error handling and async initialization. + /// + /// + /// + /// + /// var webView = new KryptonWebView2(); + /// webView.Size = new Size(800, 600); + /// + /// // Initialize the WebView2 engine asynchronously + /// await webView.EnsureCoreWebView2Async(); + /// + /// + public KryptonWebView2() + { + // We use double buffering to reduce drawing flicker + SetStyle(ControlStyles.OptimizedDoubleBuffer | + ControlStyles.AllPaintingInWmPaint | + ControlStyles.UserPaint, true); + + // We need to repaint entire control whenever resized + SetStyle(ControlStyles.ResizeRedraw, true); + + // Yes, we want to be drawn double buffered by default + DoubleBuffered = true; + + SetPalette(KryptonManager.CurrentGlobalPalette); + + // Hook into global palette changes + KryptonManager.GlobalPaletteChanged += OnGlobalPaletteChanged; + } + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + /// + /// + /// This method performs the following cleanup operations: + /// + /// + /// Unhooks from global palette change notifications + /// Disposes of the associated KryptonContextMenu if present + /// Calls the base class Dispose method to clean up WebView2 resources + /// + /// + /// The WebView2 engine will automatically clean up its native resources when the control is disposed. + /// + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + // Unhook from global palette changes + KryptonManager.GlobalPaletteChanged -= OnGlobalPaletteChanged; + + // Dispose of the krypton context menu + _kryptonContextMenu?.Dispose(); + } + + base.Dispose(disposing); + } + + #endregion + + #region Public + + /// + /// Gets and sets the KryptonContextMenu to show when right-clicked. + /// + /// + /// A instance that will be displayed when the user right-clicks + /// on the web content, or null if no custom context menu should be shown. + /// + /// + /// + /// When set to a non-null value, this property overrides the default WebView2 context menu + /// with a Krypton-styled context menu. This allows for consistent theming across your + /// application while providing custom functionality. + /// + /// + /// The context menu will be shown at the mouse position when right-clicking within the + /// WebView2 control's client area. If the menu is activated via keyboard (Menu key), + /// it will be centered on the control. + /// + /// + /// Setting this property to null will restore the default WebView2 context menu behavior. + /// + /// + /// + /// + /// var contextMenu = new KryptonContextMenu(); + /// contextMenu.Items.Add(new KryptonContextMenuItem("Copy")); + /// contextMenu.Items.Add(new KryptonContextMenuItem("Paste")); + /// + /// webView.KryptonContextMenu = contextMenu; + /// + /// + [Category(@"Behavior")] + [Description(@"The shortcut menu to show when the user right-clicks the page.")] + [DefaultValue(null)] + public KryptonContextMenu? KryptonContextMenu + { + get => _kryptonContextMenu; + + set + { + if (_kryptonContextMenu != value) + { + if (_kryptonContextMenu != null) + { + _kryptonContextMenu.Disposed -= OnKryptonContextMenuDisposed; + } + + _kryptonContextMenu = value; + + if (_kryptonContextMenu != null) + { + _kryptonContextMenu.Disposed += OnKryptonContextMenuDisposed; + } + } + } + } + + #endregion + + #region Protected Overrides + + /// + /// Process Windows-based messages. + /// + /// A Windows-based message. + /// + /// + /// This method overrides the base WndProc to intercept context menu messages and provide + /// custom KryptonContextMenu handling. It processes the following message types: + /// + /// + /// WM_CONTEXTMENU - Context menu activation (right-click or Menu key) + /// WM_PARENTNOTIFY with WM_RBUTTONDOWN - Right mouse button down + /// + /// + /// When a KryptonContextMenu is assigned, this method will: + /// + /// + /// Extract the mouse position from the message parameters + /// Handle keyboard activation by centering the menu on the control + /// Adjust the position to match standard context menu behavior + /// Show the KryptonContextMenu if the mouse is within the client area + /// Consume the message to prevent default WebView2 context menu + /// + /// + /// If no KryptonContextMenu is assigned, the message is passed to the base class for + /// default WebView2 context menu handling. + /// + /// + protected override void WndProc(ref Message m) + { + if ((m.Msg == PI.WM_.CONTEXTMENU) || + (m.Msg == PI.WM_.PARENTNOTIFY && PI.LOWORD(m.WParam) == PI.WM_.RBUTTONDOWN)) + { + // Only interested in overriding the behavior when we have a krypton context menu + if (KryptonContextMenu != null) + { + // Extract the screen mouse position (if might not actually be provided) + var mousePt = new Point(PI.LOWORD(m.LParam), PI.HIWORD(m.LParam)); + + // If keyboard activated, the menu position is centered + if (((int)(long)m.LParam) == -1) + { + mousePt = new Point(Width / 2, Height / 2); + } + else + { + if (m.Msg == PI.WM_.CONTEXTMENU) + { + mousePt = PointToClient(mousePt); + } + + // Mouse point up and left 1 pixel so that the mouse overlaps the top left corner + // of the showing context menu just like it happens for a ContextMenuStrip. + mousePt.X -= 1; + mousePt.Y -= 1; + } + + // If the mouse position is within our client area + if (ClientRectangle.Contains(mousePt)) + { + // Show the context menu + KryptonContextMenu.Show(this, PointToScreen(mousePt)); + + // We eat the message! + return; + } + } + } + + base.WndProc(ref m); + } + + private void OnKryptonContextMenuDisposed(object? sender, EventArgs e) => + // When the current krypton context menu is disposed, we should remove + // it to prevent it being used again, as that would just throw an exception + // because it has been disposed. + KryptonContextMenu = null; + + #endregion + + #region Palette Controls + + /// Sets the palette being used. + /// The chosen palette. + private void SetPalette(PaletteBase palette) + { + if (palette != _palette) + { + // Unhook from current palette events + if (_palette != null) + { + _palette.BasePaletteChanged -= OnBaseChanged; + _palette.BaseRendererChanged -= OnBaseChanged; + } + + // Remember the new palette + _palette = palette; + + // Get the renderer associated with the palette + _renderer = _palette.GetRenderer(); + + // Hook to new palette events + if (_palette != null) + { + _palette.BasePaletteChanged += OnBaseChanged; + _palette.BaseRendererChanged += OnBaseChanged; + } + } + } + + /// Called when there is a change in base renderer or base palette. + /// The sender. + /// The instance containing the event data. + private void OnBaseChanged(object? sender, EventArgs e) => + // Change in base renderer or base palette require we fetch the latest renderer + _renderer = _palette.GetRenderer(); + + /// + /// Occurs when the global palette has been changed. + /// + /// Source of the event. + /// An EventArgs that contains the event data. + protected virtual void OnGlobalPaletteChanged(object? sender, EventArgs e) + { + // We only care if we are using the global palette + if (_paletteMode == PaletteMode.Global) + { + // Update self with the new global palette + SetPalette(KryptonManager.CurrentGlobalPalette); + } + } + + /// + /// Create a tool strip renderer appropriate for the current renderer/palette pair. + /// + /// + /// A instance that provides Krypton-themed rendering + /// for tool strips, or null if no renderer is available. + /// + /// + /// + /// This method creates a tool strip renderer that matches the current Krypton theme + /// and palette. The renderer is used internally by the control to ensure consistent + /// theming across all UI elements. + /// + /// + /// The method uses the resolved palette (either the control's specific palette or + /// the global palette) to create the appropriate renderer for the current theme. + /// + /// + /// This method is primarily used internally by the Krypton framework but can be + /// called by advanced users who need custom tool strip rendering. + /// + /// + /// + /// + /// var renderer = webView.CreateToolStripRenderer(); + /// if (renderer != null) + /// { + /// myToolStrip.Renderer = renderer; + /// } + /// + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Advanced)] + public ToolStripRenderer? CreateToolStripRenderer() + { + var palette = GetResolvedPalette() ?? KryptonManager.CurrentGlobalPalette; + return _renderer?.RenderToolStrip(palette); + } + + /// + /// Gets the resolved palette to actually use when drawing. + /// + /// + /// A instance that represents the currently active palette + /// for this control. + /// + /// + /// + /// This method returns the palette that is currently being used for rendering this control. + /// The resolved palette is determined by the control's palette mode and the global palette + /// settings. + /// + /// + /// This method is primarily used internally by the Krypton framework for rendering + /// operations and theme consistency. + /// + /// + [Browsable(false)] + [EditorBrowsable(EditorBrowsableState.Never)] + public PaletteBase GetResolvedPalette() => _palette; + + #endregion +} +#endif diff --git a/Source/Krypton Components/Krypton.Toolkit/Designers/Designers/KryptonWebView2Designer.cs b/Source/Krypton Components/Krypton.Toolkit/Designers/Designers/KryptonWebView2Designer.cs new file mode 100644 index 000000000..aa0a4ce6b --- /dev/null +++ b/Source/Krypton Components/Krypton.Toolkit/Designers/Designers/KryptonWebView2Designer.cs @@ -0,0 +1,38 @@ +#region BSD License +/* + * + * Original BSD 3-Clause License (https://github.com/ComponentFactory/Krypton/blob/master/LICENSE) + * © Component Factory Pty Ltd, 2006 - 2016, (Version 4.5.0.0) All rights reserved. + * + * New BSD 3-Clause License (https://github.com/Krypton-Suite/Standard-Toolkit/blob/master/LICENSE) + * Modifications by Peter Wagner (aka Wagnerp), Simon Coghlan (aka Smurf-IV), Giduac & Ahmed Abdelhameed et al. 2025 - 2025. All rights reserved. + * + */ +#endregion + +namespace Krypton.Toolkit; + +#if WEBVIEW2_AVAILABLE +internal class KryptonWebView2Designer : ControlDesigner +{ + #region Public Overrides + /// + /// Initializes the designer with the specified component. + /// + /// The IComponent to associate the designer with. + public override void Initialize([DisallowNull] IComponent component) + { + // Let base class do standard stuff + base.Initialize(component); + + Debug.Assert(component != null); + + // The resizing handles around the control need to change depending on the + // value of the AutoSize and AutoSizeMode properties. When in AutoSize you + // do not get the resizing handles, otherwise you do. + AutoResizeHandles = true; + } + + #endregion Public Overrides +} +#endif diff --git a/Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj b/Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj index c64288fda..622d99622 100644 --- a/Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj +++ b/Source/Krypton Components/Krypton.Toolkit/Krypton.Toolkit 2022.csproj @@ -1,4 +1,4 @@ - + @@ -57,6 +57,35 @@ + + + + ..\..\..\WebView2SDK\Microsoft.Web.WebView2.Core.dll + True + + + ..\..\..\WebView2SDK\Microsoft.Web.WebView2.WinForms.dll + True + + + + + + + ..\..\..\WebView2SDK\Microsoft.Web.WebView2.Core.dll + True + + + ..\..\..\WebView2SDK\Microsoft.Web.WebView2.WinForms.dll + True + + + + + + $(DefineConstants);WEBVIEW2_AVAILABLE + + Krypton.Toolkit @@ -551,6 +580,7 @@ + diff --git a/Source/Krypton Components/Krypton.Toolkit/ToolboxBitmaps/WebView2.bmp b/Source/Krypton Components/Krypton.Toolkit/ToolboxBitmaps/WebView2.bmp new file mode 100644 index 000000000..ec4e033c4 Binary files /dev/null and b/Source/Krypton Components/Krypton.Toolkit/ToolboxBitmaps/WebView2.bmp differ diff --git a/Source/Krypton Components/TestForm/KryptonWebView2Test.Designer.cs b/Source/Krypton Components/TestForm/KryptonWebView2Test.Designer.cs new file mode 100644 index 000000000..cb72643f4 --- /dev/null +++ b/Source/Krypton Components/TestForm/KryptonWebView2Test.Designer.cs @@ -0,0 +1,196 @@ +#region BSD License +/* + * + * Original BSD 3-Clause License (https://github.com/ComponentFactory/Krypton/blob/master/LICENSE) + * © Component Factory Pty Ltd, 2006 - 2016, (Version 4.5.0.0) All rights reserved. + * + * New BSD 3-Clause License (https://github.com/Krypton-Suite/Standard-Toolkit/blob/master/LICENSE) + * Modifications by Peter Wagner (aka Wagnerp), Simon Coghlan (aka Smurf-IV), Giduac & Ahmed Abdelhameed, tobitege et al. 2025 - 2025. All rights reserved. + * + */ +#endregion + +namespace TestForm +{ + partial class KryptonWebView2Test + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.kryptonPanel1 = new Krypton.Toolkit.KryptonPanel(); + this.kryptonWebView21 = new Krypton.Toolkit.KryptonWebView2(); + this.kryptonLabel1 = new Krypton.Toolkit.KryptonLabel(); + this.kryptonPanel2 = new Krypton.Toolkit.KryptonPanel(); + this.kbtnRefresh = new Krypton.Toolkit.KryptonButton(); + this.kbtnForward = new Krypton.Toolkit.KryptonButton(); + this.kbtnBack = new Krypton.Toolkit.KryptonButton(); + this.kbtnNavigate = new Krypton.Toolkit.KryptonButton(); + this.kryptonTextBox1 = new Krypton.Toolkit.KryptonTextBox(); + this.kryptonLabel2 = new Krypton.Toolkit.KryptonLabel(); + ((System.ComponentModel.ISupportInitialize)(this.kryptonPanel1)).BeginInit(); + this.kryptonPanel1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.kryptonPanel2)).BeginInit(); + this.kryptonPanel2.SuspendLayout(); + this.SuspendLayout(); + // + // kryptonPanel1 + // + this.kryptonPanel1.Controls.Add(this.kryptonWebView21); + this.kryptonPanel1.Controls.Add(this.kryptonLabel1); + this.kryptonPanel1.Controls.Add(this.kryptonPanel2); + this.kryptonPanel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.kryptonPanel1.Location = new System.Drawing.Point(0, 0); + this.kryptonPanel1.Name = "kryptonPanel1"; + this.kryptonPanel1.PanelBackStyle = Krypton.Toolkit.PaletteBackStyle.ControlClient; + this.kryptonPanel1.Size = new System.Drawing.Size(1000, 700); + this.kryptonPanel1.TabIndex = 0; + // + // kryptonWebView21 + // + this.kryptonWebView21.AllowExternalDrop = true; + this.kryptonWebView21.CreationProperties = null; + this.kryptonWebView21.DefaultBackgroundColor = System.Drawing.Color.White; + this.kryptonWebView21.Dock = System.Windows.Forms.DockStyle.Fill; + this.kryptonWebView21.Location = new System.Drawing.Point(0, 60); + this.kryptonWebView21.Name = "kryptonWebView21"; + this.kryptonWebView21.Size = new System.Drawing.Size(1000, 640); + this.kryptonWebView21.TabIndex = 2; + this.kryptonWebView21.ZoomFactor = 1D; + this.kryptonWebView21.NavigationCompleted += new System.EventHandler(this.kryptonWebView21_NavigationCompleted); + // + // kryptonLabel1 + // + this.kryptonLabel1.Dock = System.Windows.Forms.DockStyle.Fill; + this.kryptonLabel1.LabelStyle = Krypton.Toolkit.LabelStyle.NormalControl; + this.kryptonLabel1.Location = new System.Drawing.Point(0, 60); + this.kryptonLabel1.Name = "kryptonLabel1"; + this.kryptonLabel1.Size = new System.Drawing.Size(1000, 640); + this.kryptonLabel1.TabIndex = 1; + this.kryptonLabel1.Text = "Initializing WebView2..."; + this.kryptonLabel1.Values.Text = "Initializing WebView2..."; + this.kryptonLabel1.Visible = false; + // + // kryptonPanel2 + // + this.kryptonPanel2.Controls.Add(this.kbtnRefresh); + this.kryptonPanel2.Controls.Add(this.kbtnForward); + this.kryptonPanel2.Controls.Add(this.kbtnBack); + this.kryptonPanel2.Controls.Add(this.kbtnNavigate); + this.kryptonPanel2.Controls.Add(this.kryptonTextBox1); + this.kryptonPanel2.Controls.Add(this.kryptonLabel2); + this.kryptonPanel2.Dock = System.Windows.Forms.DockStyle.Top; + this.kryptonPanel2.Location = new System.Drawing.Point(0, 0); + this.kryptonPanel2.Name = "kryptonPanel2"; + this.kryptonPanel2.PanelBackStyle = Krypton.Toolkit.PaletteBackStyle.ControlClient; + this.kryptonPanel2.Size = new System.Drawing.Size(1000, 60); + this.kryptonPanel2.TabIndex = 0; + // + // kbtnRefresh + // + this.kbtnRefresh.Location = new System.Drawing.Point(450, 30); + this.kbtnRefresh.Name = "kbtnRefresh"; + this.kbtnRefresh.Size = new System.Drawing.Size(75, 25); + this.kbtnRefresh.TabIndex = 5; + this.kbtnRefresh.Values.Text = "Refresh"; + this.kbtnRefresh.Click += new System.EventHandler(this.kbtnRefresh_Click); + // + // kbtnForward + // + this.kbtnForward.Location = new System.Drawing.Point(370, 30); + this.kbtnForward.Name = "kbtnForward"; + this.kbtnForward.Size = new System.Drawing.Size(75, 25); + this.kbtnForward.TabIndex = 4; + this.kbtnForward.Values.Text = "Forward"; + this.kbtnForward.Click += new System.EventHandler(this.kbtnForward_Click); + // + // kbtnBack + // + this.kbtnBack.Location = new System.Drawing.Point(290, 30); + this.kbtnBack.Name = "kbtnBack"; + this.kbtnBack.Size = new System.Drawing.Size(75, 25); + this.kbtnBack.TabIndex = 3; + this.kbtnBack.Values.Text = "Back"; + this.kbtnBack.Click += new System.EventHandler(this.kbtnBack_Click); + // + // kbtnNavigate + // + this.kbtnNavigate.Location = new System.Drawing.Point(530, 30); + this.kbtnNavigate.Name = "kbtnNavigate"; + this.kbtnNavigate.Size = new System.Drawing.Size(75, 25); + this.kbtnNavigate.TabIndex = 2; + this.kbtnNavigate.Values.Text = "Navigate"; + this.kbtnNavigate.Click += new System.EventHandler(this.kbtnNavigate_Click); + // + // kryptonTextBox1 + // + this.kryptonTextBox1.Location = new System.Drawing.Point(80, 32); + this.kryptonTextBox1.Name = "kryptonTextBox1"; + this.kryptonTextBox1.Size = new System.Drawing.Size(200, 23); + this.kryptonTextBox1.TabIndex = 1; + this.kryptonTextBox1.Text = "https://www.microsoft.com"; + // + // kryptonLabel2 + // + this.kryptonLabel2.Location = new System.Drawing.Point(10, 35); + this.kryptonLabel2.Name = "kryptonLabel2"; + this.kryptonLabel2.Size = new System.Drawing.Size(64, 20); + this.kryptonLabel2.TabIndex = 0; + this.kryptonLabel2.Values.Text = "Address:"; + // + // KryptonWebView2Test + // + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1000, 700); + this.Controls.Add(this.kryptonPanel1); + this.Name = "KryptonWebView2Test"; + this.Text = "KryptonWebView2 Test"; + this.WindowState = System.Windows.Forms.FormWindowState.Maximized; + ((System.ComponentModel.ISupportInitialize)(this.kryptonPanel1)).EndInit(); + this.kryptonPanel1.ResumeLayout(false); + this.kryptonPanel1.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.kryptonPanel2)).EndInit(); + this.kryptonPanel2.ResumeLayout(false); + this.kryptonPanel2.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private Krypton.Toolkit.KryptonPanel kryptonPanel1; + private Krypton.Toolkit.KryptonWebView2 kryptonWebView21; + private Krypton.Toolkit.KryptonLabel kryptonLabel1; + private Krypton.Toolkit.KryptonPanel kryptonPanel2; + private Krypton.Toolkit.KryptonButton kbtnRefresh; + private Krypton.Toolkit.KryptonButton kbtnForward; + private Krypton.Toolkit.KryptonButton kbtnBack; + private Krypton.Toolkit.KryptonButton kbtnNavigate; + private Krypton.Toolkit.KryptonTextBox kryptonTextBox1; + private Krypton.Toolkit.KryptonLabel kryptonLabel2; + } +} diff --git a/Source/Krypton Components/TestForm/KryptonWebView2Test.cs b/Source/Krypton Components/TestForm/KryptonWebView2Test.cs new file mode 100644 index 000000000..e762645f8 --- /dev/null +++ b/Source/Krypton Components/TestForm/KryptonWebView2Test.cs @@ -0,0 +1,180 @@ +#region BSD License +/* + * + * Original BSD 3-Clause License (https://github.com/ComponentFactory/Krypton/blob/master/LICENSE) + * © Component Factory Pty Ltd, 2006 - 2016, (Version 4.5.0.0) All rights reserved. + * + * New BSD 3-Clause License (https://github.com/Krypton-Suite/Standard-Toolkit/blob/master/LICENSE) + * Modifications by Peter Wagner (aka Wagnerp), Simon Coghlan (aka Smurf-IV), Giduac & Ahmed Abdelhameed, tobitege et al. 2025 - 2025. All rights reserved. + * + */ +#endregion + +using Microsoft.Web.WebView2.Core; + +namespace TestForm; + +/// +/// Test form demonstrating the KryptonWebView2 control functionality. +/// +/// +/// This form showcases the key features of the KryptonWebView2 control including: +/// - Control initialization and error handling +/// - Navigation with address bar +/// - Navigation controls (Back, Forward, Refresh) +/// - Integration with Krypton theming +/// - Proper async/await patterns for WebView2 operations +/// +/// +/// +/// The form includes a toolbar with navigation controls and an address bar, +/// while the main area displays the WebView2 control. Error handling is +/// demonstrated for WebView2 initialization failures. +/// +/// +public partial class KryptonWebView2Test : KryptonForm +{ + public KryptonWebView2Test() + { + InitializeComponent(); + InitializeWebView2(); + } + + /// + /// Initializes the WebView2 control asynchronously and navigates to a default page. + /// + /// + /// This method demonstrates proper WebView2 initialization with error handling. + /// If initialization fails, the WebView2 control is hidden and an error message + /// is displayed to the user with instructions to install the WebView2 Runtime. + /// + private async void InitializeWebView2() + { + try + { + // Initialize the WebView2 control + await kryptonWebView21.EnsureCoreWebView2Async(); + + // Navigate to a test page + kryptonWebView21.CoreWebView2?.Navigate("https://www.microsoft.com"); + } + catch (Exception ex) + { + // If WebView2 fails to initialize, show a message + kryptonWebView21.Visible = false; + kryptonLabel1.Text = $"WebView2 initialization failed: {ex.Message}\n\nPlease ensure WebView2 Runtime is installed."; + kryptonLabel1.Visible = true; + } + } + + /// + /// Handles the Navigate button click event. + /// + /// The button that raised the event. + /// Event arguments. + /// + /// This method demonstrates navigation with URL validation and protocol handling. + /// If the entered URL doesn't have a protocol, HTTPS is automatically added. + /// Navigation errors are caught and displayed to the user. + /// + private void kbtnNavigate_Click(object sender, EventArgs e) + { + try + { + var url = kryptonTextBox1.Text.Trim(); + if (!string.IsNullOrEmpty(url)) + { + // Add protocol if not present + if (!url.StartsWith("http://") && !url.StartsWith("https://")) + { + url = "https://" + url; + } + + kryptonWebView21.CoreWebView2?.Navigate(url); + } + } + catch (Exception ex) + { + MessageBox.Show($"Navigation error: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// Handles the Back button click event. + /// + /// The button that raised the event. + /// Event arguments. + /// + /// Demonstrates navigation history management by going back to the previous page. + /// + private void kbtnBack_Click(object sender, EventArgs e) + { + try + { + kryptonWebView21.CoreWebView2?.GoBack(); + } + catch (Exception ex) + { + MessageBox.Show($"Navigation error: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// Handles the Forward button click event. + /// + /// The button that raised the event. + /// Event arguments. + /// + /// Demonstrates navigation history management by going forward to the next page. + /// + private void kbtnForward_Click(object sender, EventArgs e) + { + try + { + kryptonWebView21.CoreWebView2?.GoForward(); + } + catch (Exception ex) + { + MessageBox.Show($"Navigation error: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// Handles the Refresh button click event. + /// + /// The button that raised the event. + /// Event arguments. + /// + /// Demonstrates page reload functionality by refreshing the current page. + /// + private void kbtnRefresh_Click(object sender, EventArgs e) + { + try + { + kryptonWebView21.CoreWebView2?.Reload(); + } + catch (Exception ex) + { + MessageBox.Show($"Navigation error: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// Handles the NavigationCompleted event from the WebView2 control. + /// + /// The WebView2 control that raised the event. + /// Navigation completion event arguments. + /// + /// This method demonstrates how to handle navigation completion events. + /// On successful navigation, the address bar is updated with the current URL. + /// This provides visual feedback to the user about the current page location. + /// + private void kryptonWebView21_NavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e) + { + // Update the address bar with the current URL + if (e.IsSuccess && kryptonWebView21.CoreWebView2 != null) + { + kryptonTextBox1.Text = kryptonWebView21.CoreWebView2.Source; + } + } +} diff --git a/Source/Krypton Components/TestForm/KryptonWebView2Test.resx b/Source/Krypton Components/TestForm/KryptonWebView2Test.resx new file mode 100644 index 000000000..4f03a7d1d --- /dev/null +++ b/Source/Krypton Components/TestForm/KryptonWebView2Test.resx @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + diff --git a/Source/Krypton Components/TestForm/TestForm.csproj b/Source/Krypton Components/TestForm/TestForm.csproj index 694c1a7dd..f8fbcbe3f 100644 --- a/Source/Krypton Components/TestForm/TestForm.csproj +++ b/Source/Krypton Components/TestForm/TestForm.csproj @@ -24,6 +24,30 @@ + + + + ..\..\..\WebView2SDK\Microsoft.Web.WebView2.Core.dll + True + + + ..\..\..\WebView2SDK\Microsoft.Web.WebView2.WinForms.dll + True + + + + + + + ..\..\..\WebView2SDK\Microsoft.Web.WebView2.Core.dll + True + + + ..\..\..\WebView2SDK\Microsoft.Web.WebView2.WinForms.dll + True + + + True diff --git a/run.cmd b/run.cmd index 6dff671f7..6cac438ce 100644 --- a/run.cmd +++ b/run.cmd @@ -16,19 +16,21 @@ echo 3. Create NuGet packages echo 4. Build and Pack Toolkit echo 5. Debug project echo 6. NuGet Tools -echo 7. Create Archives (ZIP/TAR) +echo 7. WebView2 SDK Tools +echo 8. Create Archives (ZIP/TAR) ::echo 8. Miscellaneous tasks -echo 8. End +echo 9. End echo: -set /p answer="Enter number (1 - 8): " +set /p answer="Enter number (1 - 9): " if %answer%==1 (goto cleanproject) if %answer%==2 (goto buildproject) if %answer%==3 (goto createnugetpackages) if %answer%==4 (goto buildandpacktoolkit) if %answer%==5 (goto debugproject) if %answer%==6 (goto nugettools) -if %answer%==7 (goto createarchives) -if %answer%==8 (goto exitbuildsystem) +if %answer%==7 (goto webview2tools) +if %answer%==8 (goto createarchives) +if %answer%==9 (goto exitbuildsystem) @echo Invalid input, please try again. @@ -48,20 +50,22 @@ echo 3. Create NuGet packages echo 4. Build and Pack Toolkit echo 5. Debug project echo 6. NuGet Tools -echo 7. Create Archives (ZIP/TAR) +echo 7. WebView2 SDK Tools +echo 8. Create Archives (ZIP/TAR) ::echo 8. Miscellaneous tasks -echo 8. End +echo 9. End echo: -set /p answer="Enter number (1 - 8): " +set /p answer="Enter number (1 - 9): " if %answer%==1 (goto cleanproject) if %answer%==2 (goto buildproject) if %answer%==3 (goto createnugetpackages) if %answer%==4 (goto buildandpacktoolkit) if %answer%==5 (goto debugproject) if %answer%==6 (goto nugettools) -if %answer%==7 (goto createarchives) +if %answer%==7 (goto webview2tools) +if %answer%==8 (goto createarchives) ::if %answer%==8 (goto miscellaneoustasks) -if %answer%==8 (goto exitbuildsystem) +if %answer%==9 (goto exitbuildsystem) @echo Invalid input, please try again. @@ -687,6 +691,84 @@ build-stable.cmd Pack :: =================================================================================================== +:webview2tools +cls + +echo WebView2 SDK Tools +echo. +echo 1. Setup WebView2 SDK +echo 2. Update WebView2 SDK +echo 3. Check WebView2 Version +echo 4. Go back to main menu +echo: +set /p answer="Enter number (1 - 4): " +if %answer%==1 (goto setupwebview2sdk) +if %answer%==2 (goto updatewebview2sdk) +if %answer%==3 (goto checkwebview2version) +if %answer%==4 (goto mainmenu) + +@echo Invalid input, please try again. + +pause + +goto webview2tools + +:: =================================================================================================== + +:setupwebview2sdk +cls + +echo Setting up WebView2 SDK for KryptonWebView2 control... +echo This will install the latest stable WebView2 SDK version. +echo. + +cd Scripts + +Setup-WebView2SDK.cmd + +cd .. + +pause + +goto webview2tools + +:: =================================================================================================== + +:updatewebview2sdk +cls + +echo Updating WebView2 SDK to latest version... +echo This will check for updates and install the newest stable version. +echo. + +cd Scripts + +Update-WebView2SDK.cmd + +cd .. + +pause + +goto webview2tools + +:: =================================================================================================== + +:checkwebview2version +cls + +echo Checking WebView2 SDK version... +echo. + +cd Scripts + +Check-WebView2Version.cmd + +cd .. + +pause + +goto webview2tools + :clearlogfiles :clearbinaries \ No newline at end of file