Помилка 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: Path = HeaderItems [0]; DataItem = null; цільовим елементом є 'DataGridTemplateColumn' (HashCode = 26950454); цільовою властивістю є "Заголовок" (тип "Об'єкт")

Чому відображається ця помилка?


4
Я перевірив вище відповідне рішення, але воно не працює в моєму випадку. Коли я переходжу до іншого рішення, як у посиланні thomaslevesque.com/2011/03/21/… . Ідея така ж, як і рішення, замість використання FrameworkElement, вони створили інший клас. Тоді це працює для мене.
leo5th

Для інших, хто опинився тут, шукаючи повідомлення про помилку: відповідь на це подібне запитання допомогло мені досить легко вирішити проблему stackoverflow.com/a/18657986/4961688
Тім Полманн,

Відповіді:


165

На жаль, будь-який DataGridColumnрозміщений під DataGrid.Columnsне є частиною Visualдерева і, отже, не пов'язаний з контекстом даних сітки даних. Отже, прив’язки не працюють зі своїми властивостями, такими як Visibilityабо Headerтощо (хоча ці властивості є дійсними властивостями залежностей!).

Тепер ви можете задатися питанням, як це можливо? Хіба їх Bindingвластивість не повинна бути прив'язана до контексту даних? Ну це просто хак. Прив'язка насправді не працює. Фактично комірки сітки даних копіюють / клонують цей об’єкт прив’язки та використовують його для відображення власного вмісту!

Отже, повертаючись до вирішення вашої проблеми, я припускаю, що HeaderItemsце властивість об’єкта, який встановлено як DataContextбатьківський подання. Ми можемо зв’язати DataContextвигляд з будь-яким DataGridColumnчерез щось, що ми називаємо a ProxyElement.

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

Сподіваюся, це допоможе вам у правильному напрямку.


25
Я вважаю, що доводиться використовувати цей хакі-проксі, що справді розчаровує, але я не можу знайти іншого способу досягти тієї самої функціональності інакше ... Дякую.
Алекс Хоуп О'Коннор

2
Це не спрацювало для мене, але прочитавши статтю Джоша Сміта про віртуальні гілки, я спробував додати прив'язку OneWayToSource до мого кореневого елемента керування, щоб встановити "ProxyElement" DataContext і це спрацювало.
jpierson

1
Ні. Наведене вище рішення дуже добре підходить для .NET 3.5.
WPF-it

1
Ця відповідь давня, але все ще корисна проти .NET 4.0. Багато відповідей навколо копіювання DataContext у стовпець, здається, не працюють. Мені потрібно було показати / приховати стовпець залежно від властивості моделі подання, і це рішення добре працювало. І відсутність коду не спричинить дипломатичного інциденту під час перегляду кодексу.
James_UK_DEV

3
Контекстне меню FYI не є однаковим і містить непроксі-сервер. Контекстне меню має відкриту властивість, Parentтоді як DataGridTextColumnне виставляє свою DataGridOwnerвластивість. Подивіться, як здійснюється прив'язка елементів контексту за допомогою прив'язки RelativeSource, у моїй відповіді Прив'язка контекстного меню до контексту даних батьківського вікна
ΩmegaMan

8

Трохи коротшою альтернативою використанню StaticResourceяк у прийнятій відповіді є 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>

Головною перевагою цього є: якщо у вас вже є елемент, який не є родоначальником DataGrid (тобто не таким, як StackPanelу наведеному вище прикладі), ви можете просто назвати його і використовувати його x:Referenceзамість цього, отже, не потрібно визначати жодну фіктивну FrameworkElementзовсім.

Якщо ви спробуєте посилатися на предка, ви отримаєте час XamlParseExceptionвиконання під час циклічної залежності.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.