Kesalahan WPF: Tidak dapat menemukan FrameworkElement yang mengatur untuk elemen target

88

Saya punya DataGriddengan baris yang memiliki gambar. Gambar ini terikat dengan pemicu ke keadaan tertentu. Ketika keadaan berubah, saya ingin mengubah gambar.

Template itu sendiri disetel di HeaderStyledari a DataGridTemplateColumn. Template ini memiliki beberapa binding. Hari penjilidan pertama menunjukkan hari apa ini dan Negara bagian mengubah gambar dengan pemicu.

Properti ini disetel dalam ViewModel.

Properti:

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,
    });
}

Kisi Data:

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

Sekarang ketika saya memulai proyek, gambar tidak muncul dan saya mendapatkan kesalahan ini:

System.Windows.Data Error: 2: Tidak dapat menemukan FrameworkElement yang mengatur atau FrameworkContentElement untuk elemen target. BindingExpression: Path = HeaderItems [0]; DataItem = null; elemen target adalah 'DataGridTemplateColumn' (HashCode = 26950454); properti target adalah 'Header' (ketik 'Object')

Mengapa kesalahan ini muncul?

KDP
sumber
4
Saya memeriksa solusi yang dijawab di atas, tetapi tidak berfungsi dalam kasus saya. Ketika saya beralih ke solusi lain seperti pada tautan thomaslevesque.com/2011/03/21/… . Idenya sama dengan solusi, alih-alih menggunakan FrameworkElement, mereka membuat kelas lain. Kemudian itu berhasil untuk saya.
leo5
Untuk orang lain yang berakhir di sini dengan mencari pesan kesalahan: Jawaban dari pertanyaan serupa ini membantu saya memecahkan masalah dengan cukup mudah stackoverflow.com/a/18657986/4961688
Tim Pohlmann

Jawaban:

166

Sayangnya, setiap DataGridColumnhost di bawah DataGrid.Columnsbukan bagian dari Visualpohon dan karena itu tidak terhubung ke konteks data dari datagrid. Jadi binding tidak berfungsi dengan propertinya seperti Visibilityatau Headerdll (meskipun properti ini adalah properti dependensi yang valid!).

Sekarang Anda mungkin bertanya-tanya bagaimana mungkin? Bukankah Bindingproperti mereka seharusnya terikat pada konteks data? Yah itu hanyalah sebuah retasan. Pengikatan tidak benar-benar berfungsi. Ini sebenarnya adalah sel datagrid yang menyalin / mengkloning objek pengikat ini dan menggunakannya untuk menampilkan isinya sendiri!

Jadi sekarang kembali ke pemecahan masalah Anda, saya berasumsi bahwa itu HeaderItemsadalah properti objek yang disetel sebagai DataContextTampilan orang tua Anda. Kita dapat menghubungkan DataContexttampilan ke apapun DataGridColumnmelalui sesuatu yang kita sebut a ProxyElement.

Contoh di bawah ini mengilustrasikan cara menghubungkan anak logis seperti ContextMenuatau DataGridColumnke Tampilan indukDataContext

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

Tampilan di atas mengalami kesalahan pengikatan yang sama dengan yang Anda temukan jika saya tidak menerapkan peretasan ProxyElement. The ProxyElement adalah setiap FrameworkElement yang mencuri itu DataContextdari View utama dan menawarkan kepada anak logis seperti ContextMenuatau DataGridColumn. Untuk itu harus dihosting sebagai a Contentinto a invisible ContentControlyang berada di bawah View yang sama.

Saya harap ini membimbing Anda ke arah yang benar.

WPF-itu
sumber
26
Saya merasa harus menggunakan proxy hacky ini benar-benar mengecewakan tetapi saya tidak dapat menemukan cara lain untuk mencapai fungsi yang sama jika tidak ... Terima kasih.
Alex Hope O'Connor
2
Ini tidak berhasil untuk saya, tetapi setelah membaca artikel Josh Smith tentang Cabang Virtual, saya mencoba menambahkan pengikatan OneWayToSource pada kontrol root saya untuk menyetel "ProxyElement" DataContext dan berhasil.
jpierson
1
Nggak. Solusi di atas sangat cocok untuk .NET 3.5.
WPF-it
1
Jawaban ini sudah lama, tetapi masih berguna melawan .NET 4.0. Banyak jawaban seputar penyalinan DataContext ke kolom sepertinya tidak berhasil. Saya perlu menampilkan / menyembunyikan kolom tergantung pada properti model tampilan dan solusi ini bekerja dengan baik. Dan tanpa kode di belakang tidak akan menyebabkan insiden diplomatik dalam peninjauan kode.
James_UK_DEV
3
FYI Menu konteks tidak sama dan memiliki non-proxy yang berfungsi. Menu konteks memiliki properti yang terekspos Parentsedangkan yang DataGridTextColumntidak mengekspos DataGridOwnerpropertinya. Lihat bagaimana pengikatan item konteks dilakukan melalui pengikatan RelativeSource dalam jawaban saya Menu Konteks Mengikat ke Teksdata Jendela Induk
ΩmegaMan
8

Alternatif yang sedikit lebih pendek untuk menggunakan a StaticResourceseperti pada jawaban yang diterima adalah 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>

Keuntungan utama dari ini adalah: jika Anda sudah memiliki sebuah elemen yang tidak nenek moyang datagrid (yaitu tidak yang StackPaneldalam contoh di atas), Anda hanya bisa memberikan nama dan menggunakannya sebagai x:Referencegantinya, maka tidak perlu mendefinisikan boneka setiap FrameworkElementsama sekali.

Jika Anda mencoba mereferensikan leluhur, Anda akan mendapatkan XamlParseExceptionwaktu proses karena ketergantungan siklis.

FernAndr
sumber