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的版本 ...
随机推荐
- io.spring.platform继承方式和import方式更改依赖版本号的问题
使用io.spring.platform时,它会管理各类经过集成测试的依赖版本号. 但有的时候,我们想使用指定的版本号,这个时候就需要去覆盖io.spring.platform的版本号. 前面的文章总 ...
- 【python之路9】类型定义与转换
一.整型(int),int的作用 1.创建int类型并赋值 n = 10 或者 n = int(10) #只要是类名加括号,都会去执行类中的 __init__()方法 n = 10,实际内部会去执 ...
- C# 配置文件 Appconfig
WinForm或WPF应用程序有时候需要保存用户的一些配置就要用到配置文件,而微软为我们的应用程序提供了Application Configuration File,就是应用程序配置文件,可以很方便的 ...
- Codeforces Round #309 (Div. 2) A. Kyoya and Photobooks【*组合数学】
A. Kyoya and Photobooks time limit per test 2 seconds memory limit per test 256 megabytes input stan ...
- 【python练手】获取城市天气质量
#!/usr/bin/python # -*- coding: utf-8 -*- # get city pm2.5 and ranking # python2.7 import sys import ...
- Spring_Bean的作用域---和使用外部属性文件
<!-- 使用 bean的scope属性来配置bean的作用域 singleton:默认值.容器初始时创建bean实例,在整个容器的生命周期内只创建这一个bean单例 prototype:原型的 ...
- 【JZOJ4747】【NOIP2016提高A组模拟9.3】被粉碎的线段树
题目描述 输入 第一行包括两个正整数,N ,M ,分别表示线段树的宽以及询问次数. 以下N-1 行以先序遍历(dfs深搜顺序)描述一个小R线段树,每行一个正整数表示当前非叶子节点的 mid,保证每个节 ...
- 【JZOJ4786】【NOIP2016提高A组模拟9.17】小a的强迫症
题目描述 输入 输出 样例输入 3 2 2 1 样例输出 3 数据范围 样例解释 解法 先假定每种颜色的珠子取一个按顺序排列. 设这n个珠子就是每一种颜色的珠子的最后一个. 考虑逐个把珠子放入. 对于 ...
- metro扁平UI网页组件
在线演示 本地下载
- 2018-8-14-解决-VS-跳转定义和-Resharper-重复
title author date CreateTime categories 解决 VS 跳转定义和 Resharper 重复 lindexi 2018-8-14 17:35:6 +0800 201 ...