原文:2019-11-29-WPF-如何在绑定失败异常

title author date CreateTime categories
WPF 如何在绑定失败异常
lindexi
2019-11-29 10:13:57 +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();
}

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


  1. [Write]System.Windows.Data Error: 40 :
  2. [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

2019-11-29-WPF-如何在绑定失败异常的更多相关文章

  1. 2018-8-10-WPF-如何在绑定失败异常

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

  2. 2019.11.29 SAP SMTP郵件服務器配置 發送端 QQ郵箱

    今天群裏的小夥伴問了如何配置郵件的問題,隨自己在sap裏面配置了一個 1.    RZ10配置參數 a)       参数配置前,先导入激活版本 执行完毕后返回 b)      输入参数文件DEFAU ...

  3. 2019.11.29 Mysql的数据操作

    为名为name的表增加数据(插入所有字段) insert into name values(1,‘张三’,‘男’,20); 为名为name的表增加数据(插入部分字段) insert into name ...

  4. pycharm+anaconda在Mac上的配置方法 2019.11.29

    内心os: 听人说,写blog是加分项,那他就不是浪费时间的事儿了呗 毕竟自己菜还是留下来东西来自己欣赏吧 Mac小电脑上进行python数据开发环境的配置 首先下载Anaconda,一个超好用的数据 ...

  5. Supervision meeting notes 2019/11/29

    topic 分支:  1. subgraph/subsequence mining Wang Jin, routine behavior/ motif. Philippe Fournier Viger ...

  6. EOJ Monthly 2019.11 E. 数学题(莫比乌斯反演+杜教筛+拉格朗日插值)

    传送门 题意: 统计\(k\)元组个数\((a_1,a_2,\cdots,a_n),1\leq a_i\leq n\)使得\(gcd(a_1,a_2,\cdots,a_k,n)=1\). 定义\(f( ...

  7. WPF数据双向绑定

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

  8. WPF的DataTrigger绑定自身属性

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

  9. 黑盒测试实践--Day5 11.29

    黑盒测试实践--Day5 11.29 今天完成任务情况: 分析系统需求,完成场景用例设计 小组负责测试的同学学习安装自动测试工具--QTP,并在线学习操作 小黄 今天的任务是完成场景测试用例的设计.在 ...

随机推荐

  1. openssl生成随机数

    #include <stdio.h> #include <openssl/bn.h> int main() { BIGNUM *bn; bn = BN_new(); //生成一 ...

  2. Can't toast on a thread that has not called Looper.prepare()

    Android开发中Can't toast on a thread that has not called Looper.prepare()问题 说一下问题出现场景: 在一个Android项目中,利用 ...

  3. sql server 安装出现需要sqlncli.msi文件,错误为 microsoft sql server 2012 native client

    在安装sql server 2017 时出现 弹框标题为  microsoft sql server 2012 native client  内容为需要sqlncli.msi文件 去本地目录找本身的那 ...

  4. flask上下文管理之threading.local

    Flask之上下文管理 知识储备之问题情境: request中的参数: 单进程单线程 单进程多线程-->reqeust 会因为多个请求,数据发生错乱.--->可以基于threading.l ...

  5. Win2003下安装PHP5.2.0+MySql5.0.27+PHPMyAdmin2.9.1的配置方法

    先下载所需要安装的东东~~ PHP 5.2.0 官方下载地址:http://www.php.net/downloads.php mysql-5.0.27 官方下载地址:http://dev.mysql ...

  6. OpenGL入门(一):使用GLFW创建简单的窗口

    如果使用QT,那么创建一个OpenGL渲染窗口其实很容易,不过出于初学的角度,使用GLFW库来创建新窗口. 1. 下载并配置GLFW GLFW是一个专门针对OpenGL的C语言库,它提供了一些渲染物体 ...

  7. C++面向对象程序设计学习笔记(7)

    模板与异常处理 模板的概念 模板是实现代码重用机制的一种工具,它可以实现类型参数化,即把类型作为参数. 模板分为函数模板和类模板,它们分别允许用户构造模板类和模板函数 函数模板与模板函数 函数模板实际 ...

  8. 新手Java在华为的几点建议?

    随着互联网时代的飞速发展,越来越多的人投身于软件开发行业,大家都称他们为程序员,或者码农. 这些程序员的水平也是参差不齐的,有些人从比较好的学校毕业,水平却一般般:也有些人从一般搬的学校毕业,但是水平 ...

  9. ibus mozc 日文输入法

    1)安装:     a) https://qiita.com/TANATY/items/8c0c17d54ab81fd1b491 b) ibus-setup 2)使用: open a console, ...

  10. python中多线程相关

    基础知识 进程:进程就是一个程序在一个数据集上的一次动态执行过程 数据集:程序执行过程中需要的资源 进程控制块:完成状态保存的单元 线程:线程是寄托在进程之上,为了提高系统的并发性 线程是进程的实体 ...