【问题标题】:Data Binding Scopes - In Need of clarification数据绑定范围 - 需要澄清
【发布时间】:2011-04-18 13:45:41
【问题描述】:

我一直在 WPF 中做一些工作,而且我的绑定大部分都在工作,但我需要在这里澄清一下范围。我遇到了一些看似简单的操作,这些操作需要愚蠢的绑定变通办法,我相信其中很多都与范围有关。

示例 #1 - 超出可视化树,绑定到父级。

<ComboBox x:Name="Combo1" ItemsSource="{Binding SomeListOfStrings}">
  <ComboBox.ContextMenu>
    <ContextMenu>
      <MenuItem Header="{Binding ElementName=Combo1, Path=SelectedItem}" />
      <MenuItem Header="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}, Path=SelectedItem}" />
    </ContextMenu>
  </ComboBox.ContextMenu>
</ComboBox>

在此示例中,我尝试将子元素的属性绑定到父元素的属性。由于此项目不在元素下的 可视化树 中,而只是一个属性,因此我无法使用 FindAncestor 定位父项。以我的经验,在这种情况下我也没有运气绑定ElementName(尝试了Name=""x:Name="")。

这里的范围是什么? MenuItem 与 ComboBox 有什么关系?因为我知道它在这里继承了它的父级的 DataContext,为什么 FindAncestor / ElementName 无法访问它?

示例 #2 - 资源 + 静态资源/动态资源

<UserControl x:Name="MainControl" ... />
  <UserControl.Resources>
     <Style TargetType="ComboBox">
       <Setter Property="ContextMenu">
         <Setter.Value>
           <ContextMenu ItemsSource="{Binding ViewModelsMenuItems}" />
         </Setter.Value>
       </Setter>
     </Style>
     <Style TargetType="ComboBox" x:Key="Example2_Style2">
       <Setter Property="ContextMenu">
         <Setter.Value>
           <ContextMenu ItemsSource="{Binding ElementName=MainControl, Path=DataContext.ViewModelMenuItems}" />
         </Setter.Value>
       </Setter>
     </Style>
  </UserControl.Resources>
  <StackPanel>
    <ComboBox />
    <ComboBox />
    <ComboBox Style="{StaticResource Example2_Style2" />
  </StackPanel>
</UserControl>

在此示例中,我尝试为我的用户控件中的所有 ComboBox 设置上下文菜单(如果我使用命名样式,则为特定的)。由于 ContextMenu 是在范围之外定义的,并且“设置”在范围内,因此我之前遇到过继承 DataContext 或能够使用 ElementName 的问题(因为该项目超出了范围?)。

奖金问题

因为我和ElementName 一起运气不好,有人可以告诉我使用哪个,因为我在互联网/书籍上都看到了。 Name="Whatever"x:Name="Whatever"

更新(根据要求)

我遇到的绑定失败类型是:

System.Windows.Data 错误:4:找不到与引用“RelativeSource FindAncestor、AncestorType='System.Windows.Controls.ComboBox'、AncestorLevel='1''的绑定源。绑定表达式:路径=选定项;数据项=空;目标元素是'MenuItem'(名称='');目标属性是'Header'(类型'object')

【问题讨论】:

  • 您能否添加一些有关您在输出窗口中看到的绑定失败的详细信息
  • @benPearce:更新了更多信息。

标签: c# wpf xaml data-binding scope


【解决方案1】:

有一种叫做继承上下文的东西,除了this blog post (archive) 之外,没有太多信息可以找到。

在那篇文章中给出了 ContextMenu 作为没有链接发生的示例。

我会使用PlacementTarget 属性来解决这个问题:

<ContextMenu>
  <MenuItem Header="{Binding RelativeSource={RelativeSource AncestorType=ContextMenu}, Path=PlacementTarget.SelectedItem}" />
</ContextMenu>

原始文章已消失;以下是存档的要点:

Nick on Silverlight 和 WPF

什么是继承上下文?

尼克克莱默 [MSFT] 2006 年 8 月 17 日

但是在我告诉你继承上下文之前,我必须解释一下 它解决的问题。曾几何时,财产继承看起来 只在逻辑树和可视树上——所以如果一个元素 没有逻辑或视觉父级,它的属性没有继承 来自它没有的父级的值。如果你认为这是有道理的 关于代码的世界。但如果你看世界 通过 xaml 眼镜,一个元素看起来有很多地方 就像它真的没有父母一样。考虑以下几点:

<Button>
  <Button.Background>
    <SolidColorBrush>green</SolidColorBrush>
  </Button.Background>
</Button>

快速,SolidColorBrush 的父级是什么?如果你说巴顿,你 会错的——SolidColorBrush 不是可视化树的一部分(它是 不是视觉)。 SolidColorBrush 也不是逻辑树的一部分, 因为如果你打电话给 Button.Content 答案不是 纯色画笔。所以这个例子中的 SolidColorBrush 没有父级,所以 它没有从任何人那里继承属性值。

起初这可能看起来很学术——谁在乎 SolidColorBrush 继承?实际上,有几个原因很重要, DataContext 属性和 Loaded 事件。 DataContext 是一个 为您的 {Binding} 使用默认数据源的继承属性 陈述。当你写:

    <SolidColorBrush Color="{Binding}"/>

由于您没有指定数据源(以及由谁指定),因此它使用 数据上下文属性。如果这继承了你所期望的方式, 一切都很快乐。但是很容易写:

<Button DataContext="whatever">
  <Button.Background>
    <SolidColorBrush Color="{Binding}"/>
  </Button.Background>
</Button>

你的 SolidColorBrush 没有继承 数据上下文。同样,Loaded 事件最初与 逻辑树,所以如果你把你的 MediaElement 放在 VisualBrush 中, 视觉树中会有一个缺口,你的媒体永远不会得到 加载事件并且从不开始播放视频。

这就是我们发明继承上下文的原因。你可以想到 作为逻辑树 2.0——继承上下文是一个额外的指针,它 属性引擎在没有逻辑父级或视觉对象时使用 父级从中获取值。继承上下文并不能解决所有问题 这个领域的问题,但他们解决了很多问题,而且在未来 我们将添加更多继承上下文指针并解决更多问题。

我们在很多地方建立了继承上下文指针, 我不会尝试列出所有这些,但这里有一些更多 有趣的:

FrameworkElement 内可冻结——我们的 SolidColorBrush/Button VisualBrush 触发器中的 FrameworkElement 上方的示例和 二传手

资源字典提供了另一个有趣的案例。假设你 在资源字典中使用 DynamicResource:

是否在 SolidColorBrush 所在的位置评估该动态资源 定义?或者刷子用在哪里?如果是后者,会发生什么 如果您在两个不同的地方使用 SolidColorBrush DynamicResource 会给出两个不同的答案吗?听起来可能 做作:

&lt;Window.Resources&gt;

  `<Color x:Key="color">red</Color>`

  `<SolidColorBrush x:Key="brush" Color="{DynamicResource color}" />  `

&lt;/Window.Resources&gt;

<Button>
  <Button.Background>
    <StaticResource ResourceKey="brush"/>
  </Button.Background>
</Button>


<Button>
  <Button.Resources>
    <Color x:Key="color">blue</Color>
  </Button.Resources>
  <Button.Background>
    <StaticResource ResourceKey="brush"/>
  </Button.Background>
</Button>

但它实际上发生在实际代码中。我们选择了第一个解决方案, SolidColorBrush 的继承上下文指向资源 字典,而不是它的使用点。

继承上下文非常有用,但我们还没有 继承上下文链接到理论上可能的任何地方, 主要是因为一天中的时间(添加继承上下文链接 很难高效地实现并且不会导致 不希望的行为变化)。可能是最简单的例子 我们没有继承上下文链接是跨随机属性的 元素:

<Button>
  <Button.ContextMenu>
    <ContextMenu/>
  </Button.ContextMenu>
</Button>

ContextMenu 既不是 Button 的视觉或逻辑子级,也不是 上面列出的继承上下文案例之一(ContextMenu 不是 一个可冻结的)。但是,我们可以使用继承上下文概念, 我们希望在未来的 WPF 版本中解决这个问题。

【讨论】:

  • 哇,感谢您挖掘这个老问题。天哪,我早就放弃了。 +1 肯定的。我将无法测试这是否对我有用,因为我已经转向其他事情,但我非常感谢你的努力!
  • 不客气。回答老问题总是有点奇怪,因为大多数人已经继续前进和/或不再关心,但我并不介意,也许至少其他人会从中得到一些东西:)(用于测试小型 XAML像这样的东西,顺便说一下Kaxaml这样的程序很方便)。
  • 我认为这可以适应我们在@我的工作场所所做的其他事情,所以这些知识肯定不会浪费。再次感谢:)
猜你喜欢
  • 2019-01-13
  • 1970-01-01
  • 1970-01-01
  • 2014-04-09
  • 1970-01-01
  • 2011-09-15
  • 1970-01-01
  • 2011-11-09
  • 2012-07-18
相关资源
最近更新 更多