You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF WebView2导航离开视图时报Source属性无法设为null错误

问题场景

要完成API服务身份验证,需引导用户登录指定网页。若登录校验通过,身份验证码会追加到重定向Uri的末尾,后续可使用该验证码通过OAuth流程获取访问密钥等凭证。为实现该交互流程,项目中集成了WebView2控件,XAML定义如下:

<wv2:WebView2 Name="webView"
              Width="800"
              Height="500"
              Source="{Binding WebUri, Mode=TwoWay}">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SourceChanged">
            <i:InvokeCommandAction Command="{Binding SourceChangedCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</wv2:WebView2>

ViewModel层编写了验证逻辑,用于识别重定向Uri是否包含身份验证码;该逻辑通过WebView2触发SourceChanged事件时调用的SourceChangedCommand执行(WebView2导航事件生命周期可参考微软官方文档),SourceChangedCommand的Execute方法实现如下:

public void Execute(object? parameter)
{
    if (ValidateUri(_viewModel.WebUri.ToString()))
    {
        var str = _viewModel.WebUri.ToString().Substring(50);
        _navigationService.Authenticate(str);
    }
}
故障表现

项目中实现了NavigationService,负责在不同ViewModel间导航,通过DataTemplate机制切换对应视图。当从承载WebView2的视图导航离开(即修改父视图的数据上下文)时,会抛出如下错误:

The Source property cannot be set to null(Source属性无法设置为null)

错误提示截图

排查时存在明显疑点:代码逻辑中从未将WebView2的Source属性设置为null,该属性始终保留为跳转后的重定向Uri值。
目前尝试的备选方案是创建继承自WebView2的自定义控件,新增布尔类型依赖属性映射到WebView2.Dispose(bool)方法,当属性值为true时主动释放WebView2资源,但该方案实现过于冗余,还可能引发ObjectDisposedException相关的其他异常。


根本原因

这个异常和业务代码无关,是WPF双向绑定机制和WebView2原生属性校验冲突导致的隐式报错:
WebView2的Source依赖属性在控件被卸载销毁的生命周期中,内部会先将自身值重置为null;而代码里给Source配置了TwoWay双向绑定,WPF在检测到依赖属性值变化时,会默认尝试把新值(也就是这个null)回写到绑定源WebUri,这个回写过程会反向触发WebView2对Source属性的赋值校验——WebView2从设计上就不允许Source被赋值为null,直接抛出异常。
由于整个赋值过程是框架隐式执行的,没有对应业务代码显式给Source赋null,排查时很难直接定位触发点。

修复方案

按改造成本从低到高排序,不需要自定义重写WebView2控件:

  • 最优方案:移除不必要的双向绑定
    现有业务场景完全不需要给Source配置双向绑定:只需要从ViewModel给控件传递初始导航地址,后续地址变更通过监听SourceChanged事件就能拿到,根本不需要把控件的Source值回写到ViewModel。直接把绑定模式改成OneWay即可彻底解决问题,改动只有一行:
    <wv2:WebView2 Name="webView"
                  Width="800"
                  Height="500"
                  Source="{Binding WebUri, Mode=OneWay}">
        <!-- 其余触发器逻辑保持不变 -->
    </wv2:WebView2>
    
    建议同时给InvokeCommandAction配置传递事件参数,不要依赖绑定的WebUri取值、也不要用硬编码长度截取字符串,可靠性更高:
    <i:InvokeCommandAction Command="{Binding SourceChangedCommand}" PassEventArgsToCommand="True"/>
    
  • 兼容方案:拦截null值传递
    如果因为现有业务逻辑耦合必须保留双向绑定,给绑定配置默认值兜底,同时在ViewModel层拦截null:
    首先修改XAML绑定,给null值和绑定失败场景配置合法的默认Uri(WebView2原生支持about:blank作为空白页地址):
    Source="{Binding WebUri, Mode=TwoWay, TargetNullValue=about:blank, FallbackValue=about:blank}"
    
    然后在ViewModel的WebUri属性setter中增加判断,当传入value为null时,直接赋值为new Uri("about:blank"),从源头阻止null值传递到控件。
  • 兜底方案:视图卸载时主动清除绑定
    如果以上两个方案都不适用,可以在承载WebView2的视图的Unloaded生命周期事件中,手动清除Source属性的绑定,断开绑定链路后控件销毁时的属性变更就不会触发回写逻辑:
    private void View_Unloaded(object sender, RoutedEventArgs e)
    {
        webView.ClearValue(WebView2.SourceProperty);
    }
    

之前尝试的自定义控件释放资源的方案完全没必要,不仅冗余,还容易引入控件生命周期相关的其他异常,使用以上三个方案任意一个即可实现正常导航。

内容的提问来源于stack exchange,提问作者jgrmn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 19:15:33