Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebView2.Source property throws NotImplementedException when passed a null value #1330

Open
bozalina opened this issue May 27, 2021 · 8 comments
Labels
bug Something isn't working priority-low We have considered this issue and decided that we will not be able to address it in the near future. tracked We are tracking this work internally.

Comments

@bozalina
Copy link

bozalina commented May 27, 2021

This seems like the incorrect exception type for this error. Perhaps an ArgumentNullException or InvalidOperationException would be more appropriate.

private static void SourcePropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e) { Microsoft.Web.WebView2.Wpf.WebView2 control = (Microsoft.Web.WebView2.Wpf.WebView2) d; if (control.IsPropertyChangingFromCore(Microsoft.Web.WebView2.Wpf.WebView2.SourceProperty)) return; Uri oldValue = (Uri) e.OldValue; Uri newValue = (Uri) e.NewValue; if (newValue == (Uri) null) throw new NotImplementedException("The Source property cannot be set to null."); if (!newValue.IsAbsoluteUri) throw new ArgumentException("The Source property cannot be set to a relative Uri."); if (control.CoreWebView2 != null && (oldValue == (Uri) null || oldValue.AbsoluteUri != newValue.AbsoluteUri)) control.CoreWebView2.Navigate(newValue.AbsoluteUri); control.WhenInit_Source((Action) (() => control.EnsureCoreWebView2Async())); }

AB#33616111

@champnic
Copy link
Member

champnic commented Jun 3, 2021

Thanks for the catch here - I think you are right that those proposed exception types may be more appropriate. My only concern is if someone is already specifically catching the "NotImplementedException" and then would miss this, but I imagine that scenario is rare. I've opened a bug on our backlog. Thanks!

@champnic champnic added bug Something isn't working tracked We are tracking this work internally. and removed tracked We are tracking this work internally. labels Jun 3, 2021
@PatrickHofman
Copy link

And based on issue #1422, I would suggest to allow null values, as MVVM binding decoupling might lead to null values being pushed to the browser control.

I would suggest to show the 'about:blank' page, or something similar in case of a null value.

@PatrickHofman
Copy link

A year later, this issue still causes us headaches. Is there any progress on this issue?

@jpgarza93
Copy link

Having same issue

@darbid
Copy link

darbid commented Jul 27, 2022

yes one of the main headaches is when using a WPF tabcontrol where unused tabs are automatically "removed"

@oldsand
Copy link

oldsand commented Nov 4, 2022

I am facing the same issue............

@josephdrake-stahls
Copy link

josephdrake-stahls commented Jan 4, 2023

Not sure if this will be useful to anyone, but this is my workaround. There are 2 attached dependency properties here.

  • NullableSourceProperty will set its value to WebView2.Source unless its null, in which case it will set it to "about:blank".
  • DisposeOnUnloadedProperty will subscribe to WebView2.Unloaded and call WebView2.Dispose when the WebView2 control is unloaded.

The use case for this was with the Telerik RadTabbedWindow with IsContentPreserved set to true. Dispose needs to be called when the control is outright removed because WebView2 will live in the background (a new thread for every tab).

Mileage may vary since (I think) the Loaded/Unloaded event are tied to the control being added/removed from the visual tree. But this solution might give someone a better idea.

using Microsoft.Web.WebView2.Wpf;
using System;
using System.Windows;
using System.Windows.Controls;

namespace WebView2Test;

public static class WebView2Helper
{
    #region NullableSource
    [AttachedPropertyBrowsableForType(typeof(WebView2))]
    public static Uri? GetNullableSource(DependencyObject obj) => (Uri?)obj.GetValue(NullableSourceProperty);
    public static void SetNullableSource(DependencyObject obj, Uri? value) => obj.SetValue(NullableSourceProperty, value);

    /// <summary>
    /// Middle man for binding <see cref="WebView2.Source"/> when the binding might resolve to null.
    /// </summary>
    /// <remarks>
    /// <see cref="WebView2.Source"/> can become null when <see cref="WebView2"/> is used in a <see cref="ItemsControl.ItemTemplate"/>. When an item is removed from the <see cref="ItemsControl.ItemsSource"/>, the Binding to <see cref="WebView2.Source"/> will resolve to null prior to the control being unloaded.
    /// </remarks>
    public static readonly DependencyProperty NullableSourceProperty = DependencyProperty.RegisterAttached("NullableSource", typeof(Uri), typeof(WebView2Helper), new PropertyMetadata(propertyChangedCallback: NullableSourceChanged));
    private static void NullableSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is not WebView2 webView2)
            return;

        webView2.Source = e.NewValue as Uri ?? new Uri("about:blank");
    }
    #endregion

    #region DisposeOnUnloaded
    [AttachedPropertyBrowsableForType(typeof(WebView2))]
    public static bool GetDisposeOnUnloaded(DependencyObject obj) => (bool)obj.GetValue(DisposeOnUnloadedProperty);
    public static void SetDisposeOnUnloaded(DependencyObject obj, bool value) => obj.SetValue(DisposeOnUnloadedProperty, value);

    /// <summary>
    /// When true, disposes <see cref="WebView2"/> when <see cref="FrameworkElement.Unloaded"/> is fired.
    /// </summary>
    /// <remarks>
    /// By default, <see cref="WebView2"/> does not dispose of its resources when its unloaded, which might be desirable in certain situations.
    /// </remarks>
    public static readonly DependencyProperty DisposeOnUnloadedProperty = DependencyProperty.RegisterAttached("DisposeOnUnloaded", typeof(bool), typeof(WebView2Helper), new PropertyMetadata(false, propertyChangedCallback: DisposeOnUnloadedChanged));

    private static void DisposeOnUnloadedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is not WebView2 webView2)
            return;

        //always attempt to unsubscribe in case this get triggered for "true" multiple times
        webView2.Unloaded -= WebView2_Unloaded;
        if (e.NewValue is true)
            webView2.Unloaded += WebView2_Unloaded;
    }

    private static void WebView2_Unloaded(object sender, RoutedEventArgs e)
    {
        if (sender is WebView2 webView2)
        {
            webView2.Dispose();
            webView2.Unloaded -= WebView2_Unloaded;
        }
    }
    #endregion
}
<wpf:WebView2 
    local:WebView2Helper.NullableSource="{Binding SomeUri}"
    local:WebView2Helper.DisposeOnUnloaded="True"/>

@github-actions github-actions bot added the priority-low We have considered this issue and decided that we will not be able to address it in the near future. label Mar 26, 2024
@ekalchev
Copy link

I can propose better workaround of this issue for those that already subclassing WebView2

    public partial class WebView : WebView2
    {
        static WebView()
        {
            SourceProperty.OverrideMetadata(typeof(WebView), new PropertyMetadata(defaultValue: new Uri("about:blank")));

            DefaultStyleKeyProperty.OverrideMetadata(typeof(WebView), new FrameworkPropertyMetadata(typeof(WebView)));
        }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working priority-low We have considered this issue and decided that we will not be able to address it in the near future. tracked We are tracking this work internally.
Projects
None yet
Development

No branches or pull requests

8 participants