Search code examples
wpfpowershellxamlwebview2

Error while attempting to create WPF window with WebView2 in Powershell


I'm attempting to use PowerShell (requirement) to create a WPF window with a WebView2 control such that I can have it open a local XML file with aa local XLST. I got it working in Visual Studio as a proof of concept using this XAML:

<Window x:Class="WepViewTutorial.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf">
<DockPanel>
    <wv2:WebView2 Name="webView"
              Source="C:\dev\webview2dev\xmlFile.xml"
/>
</DockPanel>
</Window>

And this works.

When I go to PowerShell, I am finding difficulties. Here's my Script:

Register-PackageSource -provider NuGet -name nugetRepository -location https://www.nuget.org/api/v2 -ErrorAction SilentlyContinue
install-Package -Name Microsoft.Web.WebView2 -SkipDependencies -Scope CurrentUser -ErrorAction SilentlyContinue

$webviewBase = "$(Get-Package *webview* | % source )"| Split-Path -Parent
add-type -LiteralPath "$webviewBase\lib\net45\Microsoft.Web.WebView2.Core.dll"
add-type -LiteralPath "$webviewBase\lib\net45\Microsoft.Web.WebView2.Wpf.dll"
Add-Type -assemblyName PresentationCore
Add-Type -assemblyName PresentationFramework
[xml]$xaml = @"
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
    x:Name = "Window">
<DockPanel>
    <wv2:WebView2 Name="webView"
              Source="C:\dev\webview2dev\xmlFile.xml"
/>
</DockPanel>
</Window>
"@
$reader = (New-Object System.Xml.XmlNodeReader $xaml)
$window = [Windows.Markup.XamlReader]::Load($reader)
$window.ShowDialog()

And this doesn't work. I get an error:

Exception calling "Load" with "1" argument(s): "Cannot create unknown type 
'{clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf}WebView2'."
At C:\dev\webview2dev\poc.ps1:30 char:1
+ $window = [Windows.Markup.XamlReader]::Load($reader)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : XamlParseException

If I remove this line from the XAML:

        <wv2:WebView2 Name="webView"
              Source="C:\dev\webview2dev\xmlFile.xml"
/>

I get the WPF window to generate, so I think I'm missing something specifically related to trying to get WebView2 up and running for the window in PowerShell.

I've read several articles that indicate I need to put the .dlls in the script directory. Tried that, no success. I read something indicating I need to set the User Data Folder, but have thus far been unsuccessful in figuring out how to accomplish that. In any event, I wanted to put this question here to ask for guidance, as I get the feeling I am misunderstanding something fundamental insofar as getting this to work. Any insight/suggestions would be greatly appreciated, thanks.


Solution

  • Here's the working script. I found that the webview2.dlls needed to be in the script directory. I manually dropped them in from where webview2 was installed. Eventually will have the filecopy of the .dlls included in the script after the nuGet package installs:

    function New-XamlGui {
        [CmdletBinding()]
        param (
            [parameter(Mandatory,ParameterSetName="String")]
            [string]
            $XamlXmlString,
            [Parameter(Mandatory,ParameterSetName="File")]
            [ValidateScript({Test-Path $_})]
            [string]
            $XamlFile
        )
        
        begin {
            if (-not ( ([System.Management.Automation.PSTypeName]'windows.markup.xamlreader').type ) ) {
                try {
                    [void][System.Reflection.Assembly]::LoadWithPartialName('presentationframework')
                }
                catch {
                    #This looks odd but will stop execution of the rest of the script.
                    $PSCmdlet.ThrowTerminatingError($_)
                }
            }
        }
        
        process {
    
            if ($PSCmdlet.ParameterSetName -eq "File"){
                $XamlXmlString = Get-Content -Raw $XamlFile
                if ( -not $XamlXmlString){
                    $PSCmdlet.ThrowTerminatingError(
                        ( new-object System.Management.Automation.ErrorRecord -ArgumentList @(
                            [System.Management.Automation.RuntimeException]'EmptyOrMissingFile',
                            'EasyGUI.UnableToReadFileOrEmpty',
                            [System.Management.Automation.ErrorCategory]::InvalidData,
                            $XamlFile
                            )
                        )
                    )
                }
            }
    
            # fix up xaml from VS:
            $XamlXmlString = $XamlXmlString -replace '(x:)?Class=".*?"',''
            $XamlXmlString = $XamlXmlString -replace "x:Name=","Name="
            $XamlXmlString = $XamlXmlString -replace '(mc:)?Ignorable=".*?"',''
    
            try {
                $InputXAML = [xml]$XamlXmlString
            } catch {
                #This looks odd but will stop execution of the rest of the script.
                $PSCmdlet.ThrowTerminatingError($_)
            }
            $XMLReader = New-Object System.Xml.XmlNodeReader $InputXAML
            try {
                $XAMLForm = [Windows.Markup.XamlReader]::Load($XMLReader)
            }
            catch {
                $exception = [System.Exception]::new('Failed to load XAML file.'+$_.Exception.tostring(),$_.Exception)
                $PSCmdlet.ThrowTerminatingError(
                    ( new-object System.Management.Automation.ErrorRecord -ArgumentList @(
                            $exception,
                            'EasyXAML.ReaderLoadError',
                            [System.Management.Automation.ErrorCategory]::InvalidResult,
                            $InputXAML
                        )
                    )
                )
            }
            
    
            $NamedControls = @{}
            foreach ($node in ($InputXAML.SelectNodes("//*[@Name]") ) ) {
                $NamedControls[$node.name] = $XAMLForm.FindName($node.name)
            }
    
            [pscustomobject]@{
                Form          = $XAMLForm
                NamedControls = $NamedControls
            }
    
        }
        
        end {
        $stop
        }
    }
    
    if($(Get-Package | ?{$_.Name -Eq 'Microsoft.Web.WebView2'})-eq $null){
        if(Get-PackageSource | ?{$_.Name -eq 'nuGet.org'} -eq $null){
            Register-PackageSource -provider NuGet -name nugetRepository -location https://www.nuget.org/api/v2 #-ErrorAction SilentlyContinue
        }
        install-Package -Name Microsoft.Web.WebView2 -Scope CurrentUser #-ErrorAction SilentlyContinue
    }
    
    
    Add-type -LiteralPath "C:\psScriptPath\Microsoft.Web.WebView2.Core.dll"
    Add-type -LiteralPath "c:\psScriptPath\Microsoft.Web.WebView2.Wpf.dll"
    Add-Type -assemblyName PresentationCore
    Add-Type -assemblyName PresentationFramework
    
    $inputXML = Get-content C:\psScriptPath\XAML\window.xaml -Raw
    $f = New-XamlGui -XamlXmlString $inputXML
    $f.form.showdialog()
    

    and as for the XAML:

        <Window 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:wv2="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
    mc:Ignorable="d"
    Title="DCERT Viewer" 
    Height="800" Width="1200" Topmost="False" 
    ResizeMode="CanResizeWithGrip" ShowInTaskbar = "True"
    WindowStartupLocation = "CenterScreen"
    x:Name="MainForm"
    Background="AliceBlue" UseLayoutRounding="True"
    >
        <Grid Background="#FFE5E5E5">
            <Grid.RowDefinitions>
                <RowDefinition Height="30"/>
                <RowDefinition/>
            </Grid.RowDefinitions>
            <wv2:WebView2 x:Name="_webView2" Grid.Row="1" Visibility="Visible" Source="C:\psScriptpath\xmlFile.xml" >
                        <wv2:WebView2.CreationProperties>
                    <wv2:CoreWebView2CreationProperties AdditionalBrowserArguments="--allow-file-access-from-files" UserDataFolder="C:\Temp\Data"/>
                </wv2:WebView2.CreationProperties>
            </wv2:WebView2>
    
        </Grid>
    </Window>