Opis: Mam niestandardową kontrolę zawartości i próbuję włączyć niektóre ustawienia zewnętrzne za pomocą właściwości zależności. Zasadniczo jest to panel dekoratora z dwoma rzędami siatki, górny to nagłówek, dolny to zawartość (przez ContentPresenter).

Istnieją 3 elementy, które są powiązane z szablonem (poprzez TemplateBinding), HeaderHeight, TextSize i Header (każdy z nich ma swoją właściwość zależności odpowiedniego typu).

Problem: Podczas gdy dwa z wiązań działają doskonale (nawet w czasie projektowania), trzecie nie. Wiązania FontSize="{TemplateBinding TextSize}" i Text="{TemplateBinding Header}" działają, ale <RowDefinition Height="{TemplateBinding HeaderHeight}" /> nie działa.

Siatka dzieli wysokości wierszy 50/50, bez względu na to, jaką wartość ustawię we właściwości HeaderHeight. Nie pobiera nawet wartości domyślnej z metadanych DP.

Pytanie: Jaki jest problem z tym scenariuszem? Dlaczego pozostałe dwa wiązania działają bezproblemowo, a to zachowuje się tak, jakby w ogóle nie było wiązania?

Uwaga: Jeśli ustawię DataContext = this w konstruktorze i zamienię {TemplateBinding HeaderHeight} na {Binding HeaderHeight}, problem zniknie i działa zgodnie z przeznaczeniem. Ale chciałbym wiedzieć, dlaczego nie muszę robić tego samego z innymi wiązaniami, aby działały.

XAML (motywy/Generic.xaml):

<Style TargetType="local:KaiPanel">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:KaiPanel">
                <Grid x:Name="LayoutRoot">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="{TemplateBinding HeaderHeight}" />
                        <RowDefinition />
                    </Grid.RowDefinitions>

                    <Grid Grid.Row="0">
                        <Border>
                            <TextBlock FontSize="{TemplateBinding TextSize}" 
                                       Text="{TemplateBinding Header}" />
                        </Border>
                    </Grid>

                    <Grid Grid.Row="1">
                        <Border>
                            <ContentPresenter />
                        </Border>
                    </Grid>

                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Kontrola treści (C#):

public class KaiPanel : ContentControl
{
    public KaiPanel()
    {
        this.DefaultStyleKey = typeof(KaiPanel);
    }

    public static readonly DependencyProperty TextSizeProperty =
        DependencyProperty.Register("TextSize", typeof(double), typeof(KaiPanel), new PropertyMetadata(15.0));

    public double TextSize
    {
        get { return (double)GetValue(TextSizeProperty); }
        set { SetValue(TextSizeProperty, value); }
    }

    public static readonly DependencyProperty HeaderProperty =
        DependencyProperty.Register("Header", typeof(String), typeof(KaiPanel), new PropertyMetadata(""));

    public String Header
    {
        get { return (String)GetValue(HeaderProperty); }
        set { SetValue(HeaderProperty, value); }
    }

    public static readonly DependencyProperty HeaderHeightProperty =
        DependencyProperty.Register("HeaderHeight", typeof(GridLength), typeof(KaiPanel), new PropertyMetadata(new GridLength(40)));

    public GridLength HeaderHeight
    {
        get { return (GridLength)GetValue(HeaderHeightProperty); }
        set { SetValue(HeaderHeightProperty, value); }
    }
}

Użycie kontroli niestandardowej (XAML):

<UserControl ...>

    <Grid x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="150" />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <StackPanel x:Name="buttonsStackPanel" Grid.Column="0" VerticalAlignment="Center">
            <!-- Some buttons here -->
        </StackPanel>

        <Grid Grid.Column="1">
            <controls:KaiPanel x:Name="contentPanel">
                <navigation:Frame x:Name="contentFrame" Source="KP">
                    <navigation:Frame.UriMapper>
                        <uriMapper:UriMapper>
                            <uriMapper:UriMapping Uri="KP" MappedUri="/Views/Kornelijepetak.xaml" />
                            <uriMapper:UriMapping Uri="KAI" MappedUri="/Views/KaiNetwork.xaml" />
                        </uriMapper:UriMapper>
                    </navigation:Frame.UriMapper>
                </navigation:Frame>
            </controls:KaiPanel>
        </Grid>
    </Grid>
</UserControl>
1
Kornelije Petak 26 czerwiec 2011, 17:19
Czy masz przykład swojego Xaml, który faktycznie używa KaiPanel, abyśmy mogli odtworzyć problem? Jak dotąd wszystko, co pokazano, wygląda dobrze, ale wystarczy jeden mały szczegół, aby coś zepsuć.
 – 
Gone Coding
26 czerwiec 2011, 22:32
Zaktualizowałem kod, ale to nie ma znaczenia. Nie działa, nawet jeśli umieścisz pojedynczy znacznik . Powinien pobrać domyślną wartość z DP, ale tak nie jest. Próbowałem umieścić inną zawartość w środku, ale to nie ma znaczenia - zawsze zachowuje się tak samo: 50/50.
 – 
Kornelije Petak
26 czerwiec 2011, 23:25

2 odpowiedzi

Najlepsza odpowiedź

Niestety wygląda na to, że to, co próbujesz zrobić, wymaga więcej niż tylko jednego powiązania danych. RowDefinition nie jest podklasą FrameworkElement i nie spełnia żadnych innych kryteriów określonych w Dokumentacja powiązania danych MSDN Silverlight, więc nie może być używana jako obiekt docelowy powiązania.

To, co chcesz zrobić, jest możliwe, ale niestety wymaga to trochę więcej kodu.

Najpierw dodaj pole dla głównej siatki (nazwałem to mainGrid) do swojej klasy KaiPanel. Następnie zastąp metodę OnApplyTemplate w tej klasie, aby pobrać główny Grid z szablonu i zachować odniesienie do niego w polu mainGrid:

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        mainGrid = GetTemplateChild("LayoutRoot") as Grid;
        SetHeaderRowHeight();
    }

Wywołuje to metodę, która aktualizuje wysokość pierwszego wiersza siatki. Ta metoda wygląda następująco:

    private void SetHeaderRowHeight()
    {
        if (mainGrid != null)
        {
            mainGrid.RowDefinitions[0].Height = HeaderHeight;
        }
    }

Przyznaję, że nie jestem w 100% pewien, że OnApplyTemplate zostanie wywołane po ustawieniu DPs. Wygląda na to, że tak jest (szybki test zdawał się to potwierdzać), ale wszystko, co udało mi się znaleźć, to ten post na forach Silverlight. Jeśli okaże się, że tak nie jest, musisz zarejestrować PropertyChangedCallback w HeaderHeight DP, który również wywoła tę metodę SetHeaderRowHeight.

Zobacz także http://forums.silverlight.net/forums/t/76992.aspx #183089.

0
Luke Woodward 27 czerwiec 2011, 01:11
Mówisz, że nie może być używany w wiązaniu, ale działa w normalnym wiązaniu (nie TemplateBinding). Sprawdź ponownie moją notatkę. Jeśli DataContext=this jest ustawione w konstruktorze i w szablonie, jeśli ustawię: {Binding HeaderHeight} zamiast {TemplateBinding HeaderHeight}, to działa. Bardzo dziwne zachowanie. Mam inną kontrolkę, która działa nawet z TemplateBinding, co czyni to jeszcze bardziej dziwnym.
 – 
Kornelije Petak
27 czerwiec 2011, 08:31
Poprawka: Spojrzałem teraz na moją drugą kontrolkę. Nie działa z wiązaniem szablonu, ale działa z normalnym wiązaniem (z DataContext ustawionym na samą kontrolkę).
 – 
Kornelije Petak
27 czerwiec 2011, 08:37

Zamiast tego użyj RelativeSource i TemplatedParent:

<RowDefinition Height="{Binding RelativeSource={RelativeSource TemplatedParent},
 Path=HeaderHeight}" />

Tutaj wyjaśniono różnicę między TemplateBinding i RelativeSource TemplatedParent: WPF TemplateBinding vs RelativeSource TemplatedParent

0
Lord Tasci 21 kwiecień 2021, 09:42