Skip to content

[Problem/Bug]: WebView2CompositionControl displays content blurred #5205

Open
@dreamsinbits

Description

@dreamsinbits

What happened?

I'm using a WPF WebView2CompositionControl to display an instance of Monaco Editor, but the content is blurry. It looks like the content is rendered for a wrong DPI value.

At first I used the WebView2 control but this control has the issue that it is always shown on top of all other WPF controls. So I switched to the WebView2CompositionControl, the overlaying issue is fixed now, but the content is blurred. This was not the case when I used the WebView2 control.

Importance

Moderate. My app's user experience is affected, but still usable.

Runtime Channel

Stable release (WebView2 Runtime)

Runtime Version

No response

SDK Version

No response

Framework

WPF

Operating System

Windows 11

OS Version

No response

Repro steps

Steps to reproduce

Use this HTML for Monaco editor:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Editor</title>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
    <link rel="stylesheet"
          data-name="vs/editor/editor.main"
          href="./min/vs/editor/editor.main.css"/>
    <style>
        html, body {
            height: 100%;
            margin: 0;
        }

        #container {
            height: 100%;
        }
    </style>
</head>
<body>
<div id="container"></div>
<script src="./min/vs/loader.js"></script>
<script>
    require.config({paths: {'vs': './min/vs'}});
</script>
<script src="./min/vs/editor/editor.main.js"></script>
<script>
    // Hold a reference to the Monaco editor instance
    let monacoEditor;

    // Function to initialize the editor
    function initializeEditor(content, theme = 'vs-light') {
        require(['vs/editor/editor.main'], function () {
            if (monacoEditor) {
                return;
            }
            
            monacoEditor = monaco.editor.create(document.getElementById('container'), {
                value: content.toString(),
                automaticLayout: true,
                theme: theme,
            });
        });

        // Add a resize listener to resize Monaco Editor dynamically
        window.addEventListener('resize', () => {
            if (monacoEditor) {
                monacoEditor.layout();
            }
        });
    }
</script>
</body>
</html> 

Use this WPF XAML:

<UserControl
    x:Class="Example.TextEditor"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:wpf="clr-namespace:Microsoft.Web.WebView2.Wpf;assembly=Microsoft.Web.WebView2.Wpf"
    d:DesignHeight="300"
    d:DesignWidth="300"
    mc:Ignorable="d">
    <Grid>
        <wpf:WebView2CompositionControl
            x:Name="TextEditorView"
            NavigationCompleted="TextEditorView_OnNavigationCompleted" />
    </Grid>
</UserControl>

Code behind:

using CommunityToolkit.Mvvm.Input;
using Microsoft.Web.WebView2.Core;
using Microsoft.Web.WebView2.Wpf;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using JsonSerializer = System.Text.Json.JsonSerializer;

namespace Example.TextEditor;

public partial class TextEditor : UserControl
{
       public TextEditor()
    {
        InitializeComponent();
        TextEditorView.Source = new Uri(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Monaco\editor.html"));
    }

    private async Task InitializeEditorAsync()
    {
        if (_isEditorInitialized)
        {
            return;
        }
        
        var javaScriptString = ConvertToJavaScriptString(Text);
        var script = $"initializeEditor({javaScriptString})";

        await ExecuteJavaScriptAsync(script, TextEditorView);
        _isEditorInitialized = true;
    }
    
    private async Task<string> ExecuteJavaScriptAsync(string script, WebView2CompositionControl webview)
    {
        await webview.EnsureCoreWebView2Async();

        // Call the script in the WebView2 (this executes in the loaded HTML/JS context)
        var result = await webview.CoreWebView2.ExecuteScriptAsync(script);

        if (!string.IsNullOrEmpty(result))
        {
            result = ConvertToCSharpString(result);
        }

        return result;
    }

    private string ConvertToJavaScriptString(string text)
    {
        // JSON is compatible to JavaScript
        return JsonSerializer.Serialize(text);
    }

    private string ConvertToCSharpString(string javaScriptString)
    {
        // JSON is compatible to JavaScript
        return JsonSerializer.Deserialize<string>(javaScriptString);
    }

    private async void TextEditorView_OnNavigationCompleted(object sender, CoreWebView2NavigationCompletedEventArgs e)
    {
        try
        {
            await InitializeEditorAsync();
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception);
        }
    }    
}

Expected Behavior

WebView2CompositionControl displays the HTML content exactly as the WebView2 control correctly rendered.

Actual Behavior

WebView2CompositionControl displays the HTML content blurred.

Repros in Edge Browser

No, issue does not reproduce in the corresponding Edge version

Regression

No, this never worked

Last working version (if regression)

No response

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions