title author date CreateTime categories
WPF 如何在绑定失败异常
lindexi
2018-08-10 19:16:53 +0800
2018-05-17 14:29:32 +0800
WPF 调试 WPF调试

在开发 WPF 程序,虽然 xaml 很好用,但是经常会出现小伙伴把绑定写错了。因为默认的 VisualStudio 是没有自动提示,这时很容易复制粘贴写出一个不存在的属性。
在 xaml 如果绑定失败了,那么内部会有一个异常,但是 WPF 不会把这个异常抛出来,这个异常也不会让用户拿到,只是会在输出窗口提示。但是异常会影响性能,而且会让界面和设计的不一样,所以我就想在找到绑定异常就抛出,弹出窗口告诉小伙伴。
本文会告诉大家如何找到绑定失败,并且抛出异常,如何防止修改属性名让xaml绑定失败。

在绑定失败异常建议只在调试下抛出,抛出异常建议弹出,告诉开发者现在你的界面有绑定异常

拿到绑定信息

先来写简单的代码,做一个 ViewModel ,里面有两个属性

    class ViewModel
{
public string Name { get; set; } = "lindexi"; public string JaslorbafelStojou { get; set; } = "lindexi.gitee.io";
}

可以看到第二个属性是比较复杂的,现在来写 xaml 界面,

    <Grid>
<StackPanel Margin="10,10,10,10" HorizontalAlignment="Center">
<TextBlock Margin="10,10,10,10" Text="{Binding Name}"></TextBlock>
<TextBlock Margin="10,10,10,10" Text="{Binding JaslorbafelStoj}"></TextBlock>
</StackPanel>
</Grid>

然后在后台代码添加这个代码

        public MainWindow()
{
InitializeComponent(); DataContext = new ViewModel();
}

现在运行一下,你猜是不是会显示两行,一行是 lindexi 一行是 lindexi.gitee.io ,实际上你看到只有一行,因为第二个绑定写错了

第二个在 ViewModel 的属性是 JaslorbafelStojou 但是 xaml 写的是 JaslorbafelStoj ,如果这时看到了输出,就会看到下面代码

System.Windows.Data Error: 40 : BindingExpression path error: 'JaslorbafelStoj' property not found on 'object' ''ViewModel' (HashCode=16468652)'. BindingExpression:Path=JaslorbafelStoj; DataItem='ViewModel' (HashCode=16468652); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

那么这个代码是否可以用来判断出现绑定失败,是的,让我来告诉大家如何拿到输出

转发绑定

因为绑定失败输出是使用 Trace ,关于 Trace 请看WPF 调试 获得追踪输出

那么如何拿到 Trace 的输出?

首先需要定义一个类继承 TraceListener ,下面定义一个 BindingErrorTraceListener 收到了消息就输出

    public class BindingErrorTraceListener : TraceListener
{
public override void Write(string message)
{
Trace.WriteLine(string.Format("[Write]{0}", message));
} public override void WriteLine(string message)
{
Trace.WriteLine(string.Format("[WriteLine]{0}", message));
}
}

然后在构造函数加入,注意在 InitializeComponent 之前

        public MainWindow()
{
PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Error;
PresentationTraceSources.DataBindingSource.Listeners.Add(new BindingErrorTraceListener()); InitializeComponent(); DataContext = new ViewModel();
}

这时运行代码可以看到输出

[Write]System.Windows.Data Error: 40 :
[WriteLine]BindingExpression path error: 'JaslorbafelStoj' property not found on 'object' ''ViewModel' (HashCode=16468652)'. BindingExpression:Path=JaslorbafelStoj; DataItem='ViewModel' (HashCode=16468652); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

所以很容易就知道如何判断是绑定输出

绑定失败异常

从上面代码可以知道,所有的绑定输出可以PresentationTraceSources.DataBindingSource.Listeners拿到,重写方法就可以转发

而且 TraceListener 是一个很强的类,支持了很多输入,不只字符串,还支持 object ,所以尝试使用 TraceListener 可以做到比较好调试

因为需要在失败抛出异常,就需要定义一个异常

public class BindingErrorException : Exception
{
public string SourceObject { get; set; }
public string SourceProperty { get; set; }
public string TargetElement { get; set; }
public string TargetProperty { get; set; } public BindingErrorException()
: base()
{ } public BindingErrorException(string message)
: base(message)
{ }
}

判断当前存在绑定失败很简单,主要使用正则判断

    public class BindingErrorTraceListener : TraceListener
{
private const string BindingErrorPattern = @"^BindingExpression path error(?:.+)'(.+)' property not found(?:.+)object[\s']+(.+?)'(?:.+)target element is '(.+?)'(?:.+)target property is '(.+?)'(?:.+)$"; public override void Write(string message)
{ } public override void WriteLine(string message)
{
var match = Regex.Match(message, BindingErrorPattern);
if (match.Success)
{
var exception = new BindingErrorException(message)
{
SourceObject = match.Groups[2].ToString(),
SourceProperty = match.Groups[1].ToString(),
TargetElement = match.Groups[3].ToString(),
TargetProperty = match.Groups[4].ToString()
};
throw exception;
} }
}

这时会发现代码抛出异常

但是抛出了异常建议弹出窗口,这样开发者才会看到

     public MainWindow()
{
PresentationTraceSources.DataBindingSource.Switch.Level = SourceLevels.Error;
PresentationTraceSources.DataBindingSource.Listeners.Add(new BindingErrorTraceListener()); InitializeComponent(); DataContext = new ViewModel(); App.Current.DispatcherUnhandledException += DispatcherUnhandledException;
} private void DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
if (e.Exception is BindingErrorException bindingErrorException)
{
MessageBox.Show($"Binding error. {bindingErrorException.SourceObject}.{bindingErrorException.SourceProperty} {bindingErrorException.TargetElement}.{bindingErrorException.TargetProperty}");
}
}

自动提示

我找到绑定失败很多是因为写错了属性,很多小伙伴不知道实际 xaml 是可以自动提示。

先在 对应的窗口写入绑定的类型,使用d:DataContext可以告诉 xaml 使用的数据类型,这样做绑定就可以自动提示

    <Grid d:DataContext="{d:DesignInstance local:ViewModel}">
<StackPanel Margin="10,10,10,10" HorizontalAlignment="Center">
<TextBlock Text="{Binding Name}"></TextBlock>
<TextBlock Text="{Binding JaslorbafelStoj}"></TextBlock>
</StackPanel>
</Grid>

这时尝试删除 JaslorbafelStoj 重新写,就会提示 需要写 JaslorbafelStojou ,这样会自动提示就很难写错。

我很建议大家安装 Resharper 这样在修改变量名时,会自动修改 xaml 的属性名

在有安装 Resharper 的设备,修改一个属性名,然后按 Alt+enter 就会提示 apply rename factoring ,这样会修改所有引用这个属性的变量名

需要注意,必须添加 d:DataContext 或者这样设置 ViewModel 才可以通过 Resharper 修改变量名

    <Window.DataContext>
<local:ViewModel />
</Window.DataContext>

如果需要调试 Binding ,参见 WPF 如何调试 binding

参见:

#1,208 – Catching Data Binding Errors Part 1

2018-8-10-WPF-如何在绑定失败异常的更多相关文章

  1. 2019-11-29-WPF-如何在绑定失败异常

    原文:2019-11-29-WPF-如何在绑定失败异常 title author date CreateTime categories WPF 如何在绑定失败异常 lindexi 2019-11-29 ...

  2. 申请Office 365一年免费的开发者账号攻略(2018年10月份版本)

    要进行Office 365开发,当然需要有完整的Office 365环境才可以.为了便于广大开发人员快速地启动这项工作,微软官方给所有开发人员提供了免费的一年开发者账号   那么如何申请Office ...

  3. WPF数据双向绑定

    设置双向绑定,首先控件要绑定的对象要先继承一个接口: INotifyPropertyChanged 然后对应被绑定的属性增加代码如下: 意思就是当Age这个属性变化时,要通知监听它变化的人. 即:Pr ...

  4. WPF的DataTrigger绑定自身属性

    原文:WPF的DataTrigger绑定自身属性 <DataTrigger Binding="{Binding RelativeSource={RelativeSource self} ...

  5. IntelliJ IDEA 最新激活码(截止到2018年10月14日)

    IntelliJ IDEA 注册码: EB101IWSWD-eyJsaWNlbnNlSWQiOiJFQjEwMUlXU1dEIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYX ...

  6. 新手C#SQL Server使用记录2018.08.10

    主键(PrimaryKey):主键就是每个数据行(记录)的唯一标识,不会有重复值的列(字段)才能当做主键.一个表可以没有主键,但是这样会很难处理表,因此一般情况表都要设置主键. 主键有两张选用策略,分 ...

  7. windows 10 安装 sql 2005 安装失败

    windows 10 安装 sql 2005 安装失败 网上的方法记录: 安装中无法启动需要先用sp4的补丁文件sqlos.dll,sqlservr.exe 替换D:\Program Files (x ...

  8. WPF 支持集合绑定的控件

    WPF 支持集合绑定的控件 ListBox ComboBox ListView DataGrid

  9. 01 mybatis框架整体概况(2018.7.10)-

    01 mybatis框架整体概况(2018.7.10)- F:\廖雪峰 JavaEE 企业级分布式高级架构师课程\廖雪峰JavaEE一期\第一课(2018.7.10) maven用的是3.39的版本 ...

随机推荐

  1. jnhs-Myeclipse 10注册教程unable to access jarfile cracker.jar

    直接双击jar文件就可以 打开后,随便写一个名字 然后复制LICENSE_KEY的内容,打开myeclipse 在Code那里粘贴你刚才复制的内容,然后点击Save & Active Now ...

  2. 禁用/移除WordPress页面的评论功能

    对于某些类型的WordPress站点,也许不需要在页面(page)提供评论功能,那么你可以通过下面的方法,很容易就禁用或移除WordPress页面的评论功能. 方法1:在页面编辑界面取消该页面的评论功 ...

  3. Cross-site scripting(XSS)

    https://en.wikipedia.org/wiki/Cross-site_scripting Definition Cross-site scripting (XSS) is a type o ...

  4. javascript中uber实现子类访问父类成员

    function Animal(){} Animal.prototype={ name:"animal", toString:function(){ console.log(thi ...

  5. saltstack+python批量修改服务器密码

    saltstack安装:略过 python脚本修改密码: # -*- coding utf-8 -*- import socket import re import os import sys imp ...

  6. LeetCode169 Majority Element, LintCode47 Majority Number II, LeetCode229 Majority Element II, LintCode48 Majority Number III

    LeetCode169. Majority Element Given an array of size n, find the majority element. The majority elem ...

  7. 剑指offer 1-6

    1. 二维数组中的查找 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数.   分析 ...

  8. 阿里OSS-OSSFS

    简介 OSSFS就以把OSS作为文件系统的一部分,能让你在linux系统中把OSS bucket挂载到本地文件系统中,实现数据的共享. 主要功能 ossfs 基于s3fs 构建,具有s3fs 的全部功 ...

  9. Ubuntu18.04+windows10双系统时间同步教程

    前言: 系统安装windows10和Ubuntu18.04双系统后会出现时间不同步的情况,往往windows系统的时间会有错误,一般会有8个小时的误差. 原因: 主要因为本地时间与硬件时间的时差: 本 ...

  10. js函数易犯的错误

    1.定义一个js函数时不能写在另一个函数里面. 2.定义一个打开网页自动执行的函数,一定要注意加载的顺序.如果是不是自动执行的,而是等页面加载完后事件触发的,那写在任何地方都没问题. 错误的: