WPF绑定到父级DataContext

54

我们有一个WPF应用程序,使用标准的MVVM模式,利用Cinch(因此使用MefedMVVM)进行View->ViewModel解析。这很有效,我可以将相关控件绑定到ViewModel上的属性。

在特定的视图中,我们有一个Infragistics XamGrid。该网格绑定到ViewModel上的ObservableCollection,并显示相应的行。但是,我需要将此网格上的特定列的TextBox文本值绑定到父DataContext上的属性,而不是ObservableCollection上的属性。此绑定失败了。

我们尝试了几种选项,包括:

  1. 使用AncestorType跟踪上树并绑定到父UserControl的DataContext,如下所示(来自于这个问题的大回答这个问题的回答)...

    {Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
    
  2. 如果要指定ElementName并且直接针对顶层控件进行操作,请查看此处了解如何使用ElementName。

  3. 使用在UserControl资源中定义的“代理”FrameworkElement来尝试按需传递上下文。我们按如下所示定义该元素,然后作为静态资源引用...

  4. <FrameworkElement x:Key="ProxyContext" DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}"></FrameworkElement>
    

在这种情况下,绑定找到了FrameworkElement,但是无法访问超出其范围的任何内容(当指定路径时)。

经过阅读,看起来很可能是Infragistics XamGrid在树之外构建列导致的。然而,即使是这种情况,至少选项2或3应该可行。

我们最后的想法是它与V-VM绑定有关,但即使使用Snoop,我们仍然无法找到确切的问题所在。我对WPF绑定并不是专家,因此任何指针都将不胜感激。

编辑:我在Infragistics的这里找到了一些模板示例,我将尝试一下。

编辑2:正如@Dtex指出的那样,模板是正确的选择。以下是用于XamGrid的相关代码片段:

<ig:GroupColumn Key="CurrentDate">
                <ig:GroupColumn.HeaderTemplate>
                    <DataTemplate>
                        <TextBlock Text="{Binding Path=DataContext.CurrentDateTest, RelativeSource={RelativeSource AncestorType=UserControl}}" />
                    </DataTemplate>
                </ig:GroupColumn.HeaderTemplate>
                <ig:GroupColumn.Columns>

我已经将XML保留了开放状态...你只需要添加你想要的列,然后关闭相关的标签即可。


阅读了相关资料后,看起来很可能是由于Infragistics XamGrid在树外构建列引起的。然而,即使是这种情况,至少选项2或3应该可以工作。实际上,我认为这是不正确的,它们不在可视树中,你无法引用任何东西,甚至使用ElementName也不行,但你可以通过定义DataGridTemplateColumn并在单元格模板中指定绑定来解决这个问题。 - Dtex
@Dtex 好的,这就是我的绑定/树知识的极限所在,导致我进度有些缓慢。我会尝试使用模板列,但如果你有示例的话,我将不胜感激。 - Nick
仅代表个人意见,但我认为用户控件/自定义控件不应该设置它们的DataContext。这会破坏消费者的使用体验! - Dan Puzey
请不要在问题中放置答案。您可以发布单独的答案帖子来分享您的解决方案。 - BartoszKP
2个回答

107

我不知道 XamGrid 是什么,但这是我用标准的wpf DataGrid要做的事情:

我不了解 XamGrid,但对于标准的wpf DataGrid,我会这样操作:

<DataGrid>
    <DataGrid.Columns>
        <DataGridTemplateColumn>
            <DataGridTemplateColumn.CellTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=MyUserControl}}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellTemplate>
            <DataGridTemplateColumn.CellEditingTemplate>
                <DataTemplate>
                    <TextBox Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=MyUserControl}}"/>
                </DataTemplate>
            </DataGridTemplateColumn.CellEditingTemplate>
        </DataGridTemplateColumn>
    </DataGrid.Columns>
</DataGrid>

由于在单元格模板中指定的 TextBlockTextBox 将成为可视树的一部分,因此您可以向上遍历并找到所需的任何控件。


太棒了,模板是正确的选择。我会在我的问题中更新一个XamGrid的相关示例。@Dtex,你有没有任何好的文章来解释可视树的东西?感谢你指引我正确的方向。 - Nick
我也像你一样吃了很多苦头才学会。实际上,我正在尝试绑定DataGridComboBoxColumn的ItemsSource。您可以参考http://blogs.msdn.com/b/vinsibal/archive/2008/08/14/wpf-datagrid-dissecting-the-visual-layout.aspx获取有关DataGrid的具体信息。 - Dtex

0
由于这样的原因,作为一个经验法则,我尽量避免使用太多 XAML 的“诡计”,并且保持 XAML 尽可能愚笨和简单,将其余部分放在 ViewModel 中处理(或者如果真的必要,使用附加属性或 IValueConverters 等)。
如果可能的话,我会给当前 DataContext 的 ViewModel 一个指向相关父 ViewModel 的引用(即属性)。
public class ThisViewModel : ViewModelBase
{
    TypeOfAncestorViewModel Parent { get; set; }
}

直接绑定该项。

<TextBox Text="{Binding Parent}" />


3
同样的问题仍然存在吗?网格绑定到VM上的集合,因此子列的范围是集合,而不是DataContext。在您的示例中,TextBox看不到Parent,因为它的范围已经被其父网格绑定到集合属性上。抱歉,可能是我的描述不够清晰,没有帮助到您! - Nick
1
@Nick 这个答案的意思是:给集合的元素(它们本身应该是视图模型)一个对父视图模型的引用。仔细阅读问题后,问题并不在于绑定到集合的元素,而是集合本身,这似乎是错误的。 - ILMTitan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接