在 WPF 使用绑定的时候,发现绑定不上,也许是因为绑定的对象不是当前作用的对象的原因。也许是绑定错了对象,或者对象被变更了。本文来告诉大家这个调试思路和方法

使用一个简单的例子来告诉大家,这样比较直观

我有一个简单的应用,这个应用的字符串显示绑定了一个 ToggleButton 也就是下图的写着 点击 的按钮

在 ToggleButton 的 IsChecked 变化的时候,将会自动更改 TextBlock 的内容

大概的效果如下图

界面的代码大概如下

    <Window.Resources>
        <local:FooConverter x:Key="FooConverter" />
    </Window.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <TextBlock x:Name="TextBlock" HorizontalAlignment="Center" VerticalAlignment="Center"
                   Text="{Binding ElementName=ToggleButton,Path=IsChecked,Converter={StaticResource FooConverter}}" />
        <StackPanel x:Name="Panel" Grid.Row="1" Orientation="Horizontal">
            <ToggleButton x:Name="ToggleButton" Margin="10,10,10,10" Content="点击" Click="ToggleButton_OnClick" />
            <Button Margin="10,10,10,10" Content="不要点击这里" Click="Button_OnClick" />
        </StackPanel>
    </Grid>

而 FooConverter 的代码如下

    public class FooConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value is true)
            {
                return "林德熙是逗比";
            }

            return "林德熙不是逗比";
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }

细心的小伙伴可以看到在上面动态图片的点击里面,在点击了 不要点击这里 按钮之后,再次点击 ToggleButton 时,就不会更改 TextBlock 的内容了

看起来就是 TextBlock 的绑定失效了

其中的一个调试思路就是判断绑定的对象是否更改,在后台代码里面,可以在对象状态变更的时候,如 ToggleButton 的 Click 事件,进行绑定对象的判断

通过以下代码可以读取某个对象的某个依赖属性的绑定表达式的值

        private void ToggleButton_OnClick(object sender, RoutedEventArgs e)
        {
            var bindingExpression = TextBlock.GetBindingExpression(TextBlock.TextProperty);
        }

如果读取不到绑定表达式,那么可以证明这个绑定表达式没替换了,如后台代码给依赖属性赋值,如以下代码

    TextBlock.Text = "123";

此时的赋值将会替换掉原先的绑定表达式,因此尝试获取绑定表达式将会返回空的值

而如果在获取绑定表达式的时候,可以看到有值,那么也许是绑定的对象不对,可以通过 bindingExpression.DataItem 进行判断,如下面代码

        private void ToggleButton_OnClick(object sender, RoutedEventArgs e)
        {
            var bindingExpression = TextBlock.GetBindingExpression(TextBlock.TextProperty);
            var toggleButton = (ToggleButton) sender;
            if (ReferenceEquals(toggleButton, bindingExpression.DataItem))
            {
            }
        }

此时如果作用的 ToggleButton 和绑定表达式的绑定对象是相同的,那么 ReferenceEquals 将会进入分支

在实际调试的时候,可以看到默认都会进入 ReferenceEquals 返回 true 的分支。而在点击了 不要点击这里 按钮之后,将不会进入 ReferenceEquals 返回 true 的分支

这就证明了绑定表达式绑定的对象更改了

而为什么对象更改了,在这个例子里面是谁更改了 ToggleButton 对象,这就需要其他调试手段了。如本文的更改实际是逗比的代码,在 不要点击这里 按钮的点击事件里面写了下面代码

        private void Button_OnClick(object sender, RoutedEventArgs e)
        {
            var fakeToggleButton = new ToggleButton()
            {
                Margin = new Thickness(10),
                Content = "点击"
            }.Do(button => button.Click += ToggleButton_OnClick);

            Panel.Children.RemoveAt(0);
            Panel.Children.Insert(0, fakeToggleButton);
        }

本文代码放在github欢迎小伙伴访问


本文会经常更新,请阅读原文: https://dotnet-campus.github.io//post/WPF-%E8%B0%83%E8%AF%95%E5%9B%A0%E4%B8%BA%E5%AF%B9%E8%B1%A1%E4%B8%8D%E5%90%8C%E8%80%8C%E7%BB%91%E5%AE%9A%E5%A4%B1%E6%95%88%E9%97%AE%E9%A2%98.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 lindexi (包含链接: https://dotnet-campus.github.io/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系