Ошибка WPF: не удается найти управляющий FrameworkElement для целевого элемента

87

У меня есть DataGridстрока с изображением. Это изображение привязано триггером к определенному состоянию. Когда состояние меняется, я хочу изменить изображение.

Сам шаблон устанавливается на HeaderStyleиз DataGridTemplateColumn. У этого шаблона есть привязки. Первый день привязки показывает, какой сегодня день, а состояние меняет изображение с помощью триггера.

Эти свойства устанавливаются в 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 Ошибка: 2: не удается найти управляющий FrameworkElement или FrameworkContentElement для целевого элемента. BindingExpression: Путь = HeaderItems [0]; DataItem = null; целевой элемент - DataGridTemplateColumn (HashCode = 26950454); целевое свойство - "Заголовок" (тип "Объект")

Почему отображается эта ошибка?

ДПК
источник
4
Я проверил ответное решение, но в моем случае оно не работает. Когда я переключаюсь на другое решение, как в ссылке thomaslevesque.com/2011/03/21/… . Идея такая же, как и у решения, вместо использования FrameworkElement они создали другой класс. Тогда это работает для меня.
leo5,
Для других, которые попадают сюда путем поиска сообщения об ошибке: ответ на этот аналогичный вопрос помог мне довольно легко решить проблему stackoverflow.com/a/18657986/4961688
Тим

Ответы:

165

К сожалению, все, что DataGridColumnнаходится под DataGrid.Columnsним, не является частью Visualдерева и, следовательно, не связано с контекстом данных datagrid. Таким образом, привязки не работают со своими свойствами, такими как Visibilityили и Headerт.д. (хотя эти свойства являются действительными свойствами зависимости!).

Теперь вы можете задаться вопросом, как это возможно? Разве их Bindingсобственность не должна быть привязана к контексту данных? Ну это просто взлом. Привязка толком не работает. На самом деле именно ячейки сетки данных копируют / клонируют этот объект привязки и используют его для отображения своего содержимого!

Итак, теперь вернемся к решению вашей проблемы, я предполагаю, что HeaderItemsэто свойство объекта, установленного в качестве DataContextродительского представления. Мы можем связать DataContextпредставление с любым с DataGridColumnпомощью того, что мы называем a ProxyElement.

В приведенном ниже примере показано, как подключить логический дочерний элемент, например, ContextMenuили DataGridColumnк родительскому View.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. ProxyElement любое FrameworkElement , который крадетDataContext от главного View и предлагает ее логического ребенка , такие как ContextMenuили DataGridColumn. Для этого он должен быть размещен как объект- Contentневидимка, ContentControlнаходящийся под тем же представлением.

Надеюсь, это приведет вас в правильном направлении.

WPF-это
источник
25
Я считаю, что необходимость использовать этот хакерский прокси действительно разочаровывает, но я не могу найти другого способа достичь той же функциональности в противном случае ... Спасибо.
Alex Hope O'Connor
2
У меня это не сработало, но после прочтения статьи Джоша Смита о виртуальных ветвях я попытался добавить привязку OneWayToSource к моему корневому элементу управления, чтобы установить контекст данных «ProxyElement», и это сработало.
jpierson
1
Неа. Приведенное выше решение очень хорошо подходит для .NET 3.5.
WPF-it
1
Это старый ответ, но он все еще полезен для .NET 4.0. Многие ответы, связанные с копированием DataContext в столбец, похоже, не работают. Мне нужно было показать / скрыть столбец в зависимости от свойства модели представления, и это решение работало хорошо. И отсутствие кода не вызовет дипломатических инцидентов при проверке кода.
James_UK_DEV
3
FYI Контекстное меню не то же самое и имеет обходной путь без прокси. Контекстное меню имеет открытое свойство, Parentтогда как DataGridTextColumnне раскрывает его DataGridOwnerсвойство. Посмотрите, как выполняется привязка элементов контекста через привязку RelativeSource в моем ответе « Привязка контекстного меню к тексту данных родительского окна»
ΩmegaMan
8

Немного более короткая альтернатива использованию StaticResourceas в принятом ответе x:Reference:

<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>

Главное преимущество этого: если у вас уже есть элемент , который не предок сетки данных (т.е. неStackPanel в примере выше), вы можете просто дать ему имя и использовать его как x:Referenceвместо того, чтобы , следовательно , не нуждаясь определить любую пустышку FrameworkElementсовсем.

Если вы попытаетесь сослаться на предка, вы получите XamlParseExceptionво время выполнения из-за циклической зависимости.

FernAndr
источник