【问题标题】:Specific WPF binding lost when unloading / reloading view卸载/重新加载视图时丢失特定的 WPF 绑定
【发布时间】:2026-01-21 12:25:01
【问题描述】:

我有一个非常奇怪和烦人的问题,我无法理解。解释起来有点困难,请耐心等待。

背景是我有一个有 2 个视图的应用程序,我可以在 2 个视图之间来回切换。这是使用 MVVM 实现的,因此我有一个带有 ContentControl 的主窗口. ContentControl 的内容绑定到 MainViewModel 中的 CurrentViewModel 属性。 切换是通过使用将 CurrentViewModel 设置为我的 2 个不同视图的一个或另一个 ViewModel 的命令绑定。 这 2 个 ViewModel 通过 App.xaml 中的 DataTemplate 定义链接到视图。我相信这是一个相当常见的设置。 (注意,在当前的实现中,我使用 MvvmLight 并且 ViewModel 使用它的 SimpleIOC 进行管理)

问题现在是,如果我在一个视图中有一个 ToggleButton,并为切换/取消切换状态绘制了一些自定义内容(矩形和圆形)。这是使用样式触发器(或 DataTriggers)实现的。直到这里仍然没有问题,但是如果我将 Fill 属性绑定到 ToggleButton 的前景色(参见下面的代码示例),就会发生一些奇怪的事情:

  1. 切换按钮,切换到第二个视图,切换回来,取消切换按钮 - 绘图未呈现(原因是填充的绑定不起作用),但切换状态的其他内容没有问题
  2. 一旦未切换的绘图丢失,在此未切换状态下切换到第二个视图然后切换回来 - 未切换状态的绘图奇迹般地返回并且两种状态(切换/未切换)都正确呈现

编辑: 切换回来时出现的错误: System.Windows.Data 错误:4:找不到与引用“ElementName = TB1”绑定的源。绑定表达式:路径=前景;数据项=空;目标元素是'椭圆'(名称='');目标属性是“填充”(输入“画笔”)

有人知道问题出在哪里以及如何解决这个问题吗? 我正在考虑简化测试应用程序,排除所有 MvvmLight / IOC,但我怀疑这个问题与此有关。

<ToggleButton x:Name="TB1"  Grid.Row="0" Grid.Column="1" Margin="5" Width="30"
Command="{Binding Toggle1}"
IsChecked="{Binding IsToggled1}">
<ToggleButton.Style>
    <Style TargetType="{x:Type ToggleButton}">
        <Setter Property="Content" >
            <Setter.Value>
                <Rectangle Width="17" Height="17" Fill="{Binding ElementName=TB1, Path=Foreground}" />
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <DataTrigger Binding="{Binding IsToggled1}" Value="False">
                <Setter Property="Content">
                    <Setter.Value>
                        <Ellipse Width="17" Height="17" Fill="{Binding ElementName=TB1, Path=Foreground}" />
                    </Setter.Value>
                </Setter>
            </DataTrigger>
        </Style.Triggers>
    </Style>
</ToggleButton.Style>

【问题讨论】:

  • 您能尝试将RelativeSource 绑定到父ToggleButton 吗?那应该可以。
  • @oddparity 哦,但这就是我第一次写它的方式,只是试图摆弄xaml。但是,我总是遇到同样的问题,所以它一定比那更“深”
  • 有趣的是,当我窥探 ToggleButton 时,矩形就在那里。当我在 Snoop 的树中单击它时,它甚至会立即可见。

标签: .net wpf mvvm data-binding


【解决方案1】:

XAML 名称范围有问题。它就像变量范围,仅适用于 XAML 名称。 XAML 名称范围是在 XAML 页面的根元素上创建的,但样式和模板有自己的名称范围,因为它们是可重用的。假设您的样式中的 Rectangle 或 Ellipse 有一个名称,并且您的视图中有 5 个使用该样式的 ToggleButtons。如果 Styles 没有自己的名称范围,您将在同一个名称范围中有 5 个同名的元素。 解析 XAML 时,将应用 Content 属性值之一(取决于 ToggleButton 的 IsChecked 状态)。它进入了 TogglButton 的名称范围,那个 ElementName 被解析了,但另一个留在了名称范围之外,无法解析。

那你能做什么?您可以使用x:Reference 标记扩展。因此,您可以使用Source={x:Reference TB1} 而不是ElementName=TB1。但随后 XAML 解析器将引发循环依赖异常,因为您的引用将指向包含该引用的元素(您的 Style 在您的 ToggleButton 中定义)。要解决此问题,请将您的样式定义为资源:

<Window.Resources>
  <Style x:key="TBStyle" TargetType="{x:Type ToggleButton}">
     <Setter Property="Content">
        <Setter.Value>
            <Rectangle Width="17" Height="17"
                     Fill="{Binding Foreground, Source={x:Reference TB1}}"/>
        </Setter.Value>
     </Setter>
     <Style.Triggers>
        <DataTrigger Binding="{Binding IsToggled1}" Value="False">
            <Setter Property="Content">
                <Setter.Value>
                    <Ellipse Width="17" Height="17"
                 Fill="{Binding Foreground, Source={x:Reference TB1}}}" />
                </Setter.Value>
            </Setter>
        </DataTrigger>
    </Style.Triggers>
  </Style>
</Window.Resources>
....
<ToggleButton x:Name="TB1"
              Margin="5"
              Width="30"
              Height="30"
              IsChecked="{Binding IsToggled1}"
              Style="{StaticResource TBStyle}"/>

如果您将此样式应用于 5 个切换按钮,它们都会从名为“TB1”的那个按钮中选择前景色,但这是完全不同的问题。

【讨论】:

  • 这行得通,谢谢!我必须重温我的x:Reference,因为为什么一个 ElementName(或 FindAncestor)可以解决而另一个不能解决仍然有点模糊。但是由于这确实解决了问题,所以我将您的回复标记为答案。
  • 我试图在答案中解释这一点(查看第一段的最后两句话)。 Rectangle 和 Ellipse 元素是在 Style 中创建的,它们不是元素树的一部分,直到其中一个成为 ToggleButton 的实际内容(在此之前,它还没有 ToggleButton 类型的祖先,并且它的名称范围没有t 有一个名为 TB1) 的元素。
  • 我知道,我更愿意把头绕在它身边,谢谢。
最近更新 更多