WPF错误:无法找到目标元素的主要FrameworkElement

103
我有一个包含图像的行的。这个图像绑定了一个触发器到特定的状态。当状态改变时,我想要改变这个图像。模板本身设置在的上。该模板具有一些绑定。第一个绑定Day显示是哪一天,State通过触发器更改图像。这些属性都在ViewModel中设置。
属性:
public class HeaderItem
{
    public string Day { get; set; }
    public ValidationStatus State { get; set; }
}

this.HeaderItems = new ObservableCollection<HeaderItem>();
for (int i = 1; i < 15; i++)
{
    this.HeaderItems.Add(new HeaderItem()
    {
        Day = i.ToString(),
        State = ValidationStatus.Nieuw,
    });
}

数据网格:

<DataGrid x:Name="PersoneelsPrestatiesDataGrid" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
              AutoGenerateColumns="False" SelectionMode="Single" ItemsSource="{Binding CaregiverPerformances}" FrozenColumnCount="1" >

    <DataGridTemplateColumn HeaderStyle="{StaticResource headerCenterAlignment}" Header="{Binding HeaderItems[1]}" Width="50">
        <DataGridTemplateColumn.CellEditingTemplate>
            <DataTemplate>
                <TextBox Text="{ Binding Performances[1].Duration,Converter={StaticResource timeSpanConverter},Mode=TwoWay}"/>
            </DataTemplate>
        </DataGridTemplateColumn.CellEditingTemplate>

        <DataGridTemplateColumn.CellTemplate>
            <DataTemplate>
                <TextBlock TextAlignment="Center" Text="{ Binding Performances[1].Duration,Converter={StaticResource timeSpanConverter}}"/>
            </DataTemplate>
        </DataGridTemplateColumn.CellTemplate>
    </DataGridTemplateColumn> 
</DataGrid>

Datagrid的HeaderStyleTemplate:

<Style x:Key="headerCenterAlignment" TargetType="{x:Type DataGridColumnHeader}">
    <Setter Property="HorizontalContentAlignment" Value="Center"/>

    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type DataGridColumnHeader}">
                <Grid>
                    <Grid.RowDefinitions>
                        <RowDefinition />
                        <RowDefinition />
                    </Grid.RowDefinitions>

                    <TextBlock Grid.Row="0" Text="{Binding Day}" />
                    <Image x:Name="imageValidation" Grid.Row="1" Width="16" Height="16" Source="{StaticResource imgBevestigd}" />
                </Grid>

                <ControlTemplate.Triggers>
                    <MultiDataTrigger >
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding State}" Value="Nieuw"/>                                 
                        </MultiDataTrigger.Conditions>
                        <Setter TargetName="imageValidation" Property="Source" Value="{StaticResource imgGeenStatus}"/>
                    </MultiDataTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

现在当我启动这个项目时,图片不显示,并且出现以下错误:

System.Windows.Data Error: 2 : 找不到目标元素的控制FrameworkElement或FrameworkContentElement。BindingExpression:Path=HeaderItems[0]; DataItem=null; 目标元素是'DataGridTemplateColumn'(HashCode=26950454); 目标属性是'Header'(类型为'Object')

为什么会出现这个错误?


4
我查看了之前的解决方案,但在我的情况下无法运行。当我尝试了另一个解决方案,链接如下:http://www.thomaslevesque.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/。这个想法与之前的解决方案相同,不过他们创建了另一个类来代替使用FrameworkElement。然后它就可以正常工作了。 - leo5th
对于那些通过搜索错误信息最终到达这里的人:这个类似问题的答案帮助我相当容易地解决了问题。https://dev59.com/ZXA75IYBdhLWcg3wkZ-A#18657986 - Tim Pohlmann
4个回答

184
很遗憾,任何放在 DataGrid.Columns 下的 DataGridColumn 都不是 Visual Tree 的一部分,因此不与 datagrid 的数据上下文连接。 所以绑定无法使用它们的属性,如 Visibility 或 Header 等(尽管这些属性都是有效的依赖属性!)。
现在你可能会想,这怎么可能? 它们的 Binding 属性不应该绑定到数据上下文吗?事实上,这只是一个 hack。 绑定实际上并没有真正发挥作用。 实际上是 datagrid 单元格复制/克隆了该绑定对象,并将其用于显示它们自己的内容!
那么现在回到解决问题上来,我假设 HeaderItems 是设置为父视图 DataContext 的对象的属性。 我们可以通过某些我们称之为“代理元素”的东西将视图的 DataContext 连接到任何 DataGridColumn。
以下示例说明了如何将逻辑子项,例如 ContextMenu 或 DataGridColumn,连接到父视图的 DataContext:
 <Window x:Class="WpfApplicationMultiThreading.Window5"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"        
         xmlns:vb="http://schemas.microsoft.com/wpf/2008/toolkit"
         Title="Window5" Height="300" Width="300" >
  <Grid x:Name="MyGrid">
    <Grid.Resources>
        <FrameworkElement x:Key="ProxyElement" DataContext="{Binding}"/>
    </Grid.Resources>
    <Grid.DataContext>
         <TextBlock Text="Text Column Header" Tag="Tag Columne Header"/>
    </Grid.DataContext>
    <ContentControl Visibility="Collapsed"
             Content="{StaticResource ProxyElement}"/>
    <vb:DataGrid AutoGenerateColumns="False" x:Name="MyDataGrid">
        <vb:DataGrid.ItemsSource>
            <x:Array Type="{x:Type TextBlock}">
                <TextBlock Text="1" Tag="1.1"/>
                <TextBlock Text="2" Tag="1.2"/>
                <TextBlock Text="3" Tag="2.1"/>
                <TextBlock Text="4" Tag="2.2"/>
            </x:Array>
        </vb:DataGrid.ItemsSource>
        <vb:DataGrid.Columns>
            <vb:DataGridTextColumn
                       Header="{Binding DataContext.Text,
                                     Source={StaticResource ProxyElement}}"
                       Binding="{Binding Text}"/>
            <vb:DataGridTextColumn
                       Header="{Binding DataContext.Tag,
                                     Source={StaticResource ProxyElement}}"
                       Binding="{Binding Tag}"/>
        </vb:DataGrid.Columns>
    </vb:DataGrid>
  </Grid>
</Window>

如果我没有实现ProxyElement hack,那么上述视图会遇到与您发现的相同的绑定错误。ProxyElement是任何FrameworkElement,它从主视图中“窃取”DataContext并将其提供给逻辑子项,例如ContextMenu或DataGridColumn。为此,它必须作为Content托管到一个不可见的ContentControl中,该ContentControl位于同一视图下。

希望这能指导您朝着正确的方向前进。


32
我觉得不得不使用这种笨拙的代理工具真的很令人失望,但如果没有其他方法来实现相同的功能,我就只能这么做了……谢谢。 - Alex Hope O'Connor
2
这对我不起作用,但在阅读了Josh Smith关于虚拟分支的文章后,我尝试在我的根控件上添加OneWayToSource绑定来设置“ProxyElement”DataContext,并且它成功了。 - jpierson
1
不,上面的解决方案非常适用于.NET 3.5。 - WPF-it
1
这个答案虽然有点老,但对于.NET 4.0仍然有用。 许多涉及将DataContext复制到列中的答案似乎不起作用。 我需要根据视图模型属性显示/隐藏列,这个解决方案很好用。 而且没有代码后台,不会在代码审查中引起外交事件。 - James_UK_DEV
3
FYI,上下文菜单并不相同,并且存在一种无代理的解决方法。上下文菜单有一个暴露的属性Parent,而DataGridTextColumn没有暴露其DataGridOwner属性。请参见我的答案中如何通过RelativeSource绑定实现上下文项绑定到父窗口的DataContextContext Menu Binding to Parent Window's Datacontext - ΩmegaMan
显示剩余7条评论

21

使用x:Reference比接受的答案中使用StaticResource更为简短:

<StackPanel>

    <!--Set the DataContext here if you do not want to inherit the parent one-->
    <FrameworkElement x:Name="ProxyElement" Visibility="Collapsed"/>

    <DataGrid>
        <DataGrid.Columns>
            <DataGridTextColumn
                Header="{Binding DataContext.Whatever, Source={x:Reference ProxyElement}}"
                Binding="{Binding ...}" />
        </DataGrid.Columns>
    </DataGrid>

</StackPanel>

这样做的主要优点是:如果您已经有一个元素不是DataGrid的祖先(即上面示例中的StackPanel之外的元素),您可以给它命名并将其用作x:Reference,因此无需定义任何虚拟FrameworkElement。
如果您尝试引用祖先元素,则会由于循环依赖关系在运行时出现XamlParseException。

有趣,谢谢!不过我想知道为什么我们要经历这些麻烦。 - ENDEESA
1
这会产生运行时错误在类型为Reference的对象上找不到DataContext属性。 - Pawel

0

代理元素对我来说在工具提示中不起作用。对于Infragistics DataGrid,我做了这个,你可以轻松地将其更改为你的网格类型:

<igDP:ImageField Label="_Invited" Name="Invited">
    <igDP:Field.Settings>
        <igDP:FieldSettings>
            <igDP:FieldSettings.CellValuePresenterStyle>
                <Style TargetType="{x:Type igDP:CellValuePresenter}">
                    <Setter Property="ToolTip">
                        <Setter.Value>
                            <Label Content="{Binding DataItem.InvitationSent, Converter={StaticResource dateTimeConverter}}"/>
                        </Setter.Value>
                    </Setter>
                </Style>
            </igDP:FieldSettings.CellValuePresenterStyle>
        </igDP:FieldSettings>
    </igDP:Field.Settings>
 </igDP:ImageField>

0

没有代理的方法是在构造函数中设置绑定:

var i = 0;
var converter = new BooleanToVisibilityConverter();
foreach(var column in DataGrid.Columns)
{
    BindingOperations.SetBinding(column, DataGridColumn.VisibilityProperty, new Binding($"Columns[{i++}].IsSelected")
    { 
        Source = ViewModel,
        Converter = converter,
    });
}

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