2018-8-10-WPF-如何在绑定失败异常
| 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
参见:
2018-8-10-WPF-如何在绑定失败异常的更多相关文章
- 2019-11-29-WPF-如何在绑定失败异常
		原文:2019-11-29-WPF-如何在绑定失败异常 title author date CreateTime categories WPF 如何在绑定失败异常 lindexi 2019-11-29 ... 
- 申请Office 365一年免费的开发者账号攻略(2018年10月份版本)
		要进行Office 365开发,当然需要有完整的Office 365环境才可以.为了便于广大开发人员快速地启动这项工作,微软官方给所有开发人员提供了免费的一年开发者账号 那么如何申请Office ... 
- WPF数据双向绑定
		设置双向绑定,首先控件要绑定的对象要先继承一个接口: INotifyPropertyChanged 然后对应被绑定的属性增加代码如下: 意思就是当Age这个属性变化时,要通知监听它变化的人. 即:Pr ... 
- WPF的DataTrigger绑定自身属性
		原文:WPF的DataTrigger绑定自身属性 <DataTrigger Binding="{Binding RelativeSource={RelativeSource self} ... 
- IntelliJ IDEA 最新激活码(截止到2018年10月14日)
		IntelliJ IDEA 注册码: EB101IWSWD-eyJsaWNlbnNlSWQiOiJFQjEwMUlXU1dEIiwibGljZW5zZWVOYW1lIjoibGFuIHl1IiwiYX ... 
- 新手C#SQL Server使用记录2018.08.10
		主键(PrimaryKey):主键就是每个数据行(记录)的唯一标识,不会有重复值的列(字段)才能当做主键.一个表可以没有主键,但是这样会很难处理表,因此一般情况表都要设置主键. 主键有两张选用策略,分 ... 
- windows 10 安装 sql 2005 安装失败
		windows 10 安装 sql 2005 安装失败 网上的方法记录: 安装中无法启动需要先用sp4的补丁文件sqlos.dll,sqlservr.exe 替换D:\Program Files (x ... 
- WPF 支持集合绑定的控件
		WPF 支持集合绑定的控件 ListBox ComboBox ListView DataGrid 
- 01 mybatis框架整体概况(2018.7.10)-
		01 mybatis框架整体概况(2018.7.10)- F:\廖雪峰 JavaEE 企业级分布式高级架构师课程\廖雪峰JavaEE一期\第一课(2018.7.10) maven用的是3.39的版本 ... 
随机推荐
- java-面向对象-封装-this-构造函数
			概要图 一 构造函数 需求:为了描述事物更准确,发现事物对应的很多对象一创建时, 就有了,一些初始化的数据.在类中该如何完成的. 通过Java中的另一个小技术完成:就是构造函数.对象本身就是构造出 ... 
- JAVA利用JXL导出/生成 EXCEL1
			/** * 导出导出采暖市场部收入.成本.利润明细表 * @author JIA-G-Y */ public String exporExcel(String str) { String str=Se ... 
- LUOGU 2593 : [Zjoi2006] 超级麻将
			传送门 解题思路 直接爆搜全T..状态数太多了,所以我们考虑贪心+剪枝.贪心:先拿三个连着的,再拿四个一样的,再拿三个一样的,最后拿两个一样的这样的搜索顺序最优,两个的放最后是因为只要这样的一个,三个 ... 
- web前端学习(四)JavaScript学习笔记部分(1)-- JavaScript基础教程
			1.JavaScript基础教程 1.1.Javascript基础-介绍.实现.输出 1.1.1.JavaScript是互联网上最流行的脚本语言,这门语言可用于web和HTML,更可广泛用于服务端.p ... 
- MyEclipse编写ExtJS卡死问题解决方法
			MyEclipse 8.6 在 jsp 中编写 ExtJS时,会出现卡死现象,让人甚是头疼.网上找了很多方法,折腾半天,还是不管用. 什么MyEclipse 优化,Validation 取消,MyE ... 
- Vue项目中出现Loading chunk {n} failed问题的解决方法
			最近有个Vue项目中会偶尔出现Loading chunk {n} failed的报错,报错来自于webpack进行code spilt之后某些bundle文件lazy loading失败.但是这个问题 ... 
- op应用:官方,wifidog,portal,uci,luci,脚本,框架,usb
			http://wiki.openwrt.org/doc/starthttp://downloads.openwrt.org/docs/buildroot-documentation.htmlhttp: ... 
- 【转载】ubuntu下编写字符设备驱动程序-入门篇
			在ubuntu初学驱动,觉得挺有用的. http://www.eefocus.com/jefby1990/blog/13-02/291628_c39b8.html 
- python流程控制和循环
			变量的命名:可以由数字字母下换线组成 ,不能以数字开头,可以使用中文但是不推荐使用中文,不推荐前面使用_ __,不能使用系统的关键字,变量名严格区分大小写 逻辑运算优先级 or<and<n ... 
- TZOJ4777: 方格取数
			4777: 方格取数 Time Limit(Common/Java):1000MS/3000MS Memory Limit:65536KByteTotal Submit: 11 ... 
