2013-05-07 15 views
6

Próbuję ponownie przekształcić szablon TextBox, aby mieć dwie ramki z dopełnieniem pośrednim; Jednak nawet jeśli wyraźnie ustawię dopełnienie na PART_ ContentHost na zero, dopełnienie dla elementu sterującego jest zawsze stosowane również do wewnętrznego obiektu.Problem z dopełnieniem szablonu TextBox

Mały przykład:

<Style TargetType="{x:Type TextBox}"> 
    <Setter Property="Template"> 
     <Setter.Value> 
      <ControlTemplate TargetType="{x:Type TextBox}"> 
       <Border Padding="{TemplateBinding Padding}" 
         BorderThickness="1" 
         BorderBrush="Black" 
         Background="LightBlue"> 
        <ScrollViewer Padding="0" Margin="0" Background="Turquoise" 
            x:Name="PART_ContentHost" /> 
       </Border> 
      </ControlTemplate> 
     </Setter.Value> 
    </Setter> 
</Style> 

<TextBox Padding="15"/> 

Rezultatem jest pole tekstowe, które wygląda następująco: [15 [15 textField 15] 15]

podczas gdy oczekiwano tylko: [15 [textField] 15]

Jak właściwie wymusić na PART_ContentHost (scrollviewer/pole tekstowe) zerowe wypełnienie?

Odpowiedz

6

To naprawdę wygląda na dziwny błąd, chyba że ktoś ma lepsze wytłumaczenie tego zachowania.

ScrollViewer (PART_ContentHost) wykorzystuje wewnętrznie Template jak:

<ControlTemplate TargetType="{x:Type ScrollViewer}"> 
    <Grid x:Name="Grid" 
     Background="{TemplateBinding Background}"> 
    <Grid.ColumnDefinitions> 
     <ColumnDefinition Width="*" /> 
     <ColumnDefinition Width="Auto" /> 
    </Grid.ColumnDefinitions> 
    <Grid.RowDefinitions> 
     <RowDefinition Height="*" /> 
     <RowDefinition Height="Auto" /> 
    </Grid.RowDefinitions> 
    <Rectangle x:Name="Corner" 
       Grid.Row="1" 
       Grid.Column="1" 
       Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" /> 
    <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" 
          Grid.Row="0" 
          Grid.Column="0" 
          Margin="{TemplateBinding Padding}" 
          CanContentScroll="{TemplateBinding CanContentScroll}" 
          CanHorizontallyScroll="False" 
          CanVerticallyScroll="False" 
          Content="{TemplateBinding Content}" 
          ContentTemplate="{TemplateBinding ContentTemplate}" /> 
    <ScrollBar x:Name="PART_VerticalScrollBar" 
       Grid.Row="0" 
       Grid.Column="1" 
       AutomationProperties.AutomationId="VerticalScrollBar" 
       Cursor="Arrow" 
       Maximum="{TemplateBinding ScrollableHeight}" 
       Minimum="0" 
       ViewportSize="{TemplateBinding ViewportHeight}" 
       Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" 
       Value="{Binding VerticalOffset, 
           Mode=OneWay, 
           RelativeSource={RelativeSource TemplatedParent}}" /> 
    <ScrollBar x:Name="PART_HorizontalScrollBar" 
       Grid.Row="1" 
       Grid.Column="0" 
       AutomationProperties.AutomationId="HorizontalScrollBar" 
       Cursor="Arrow" 
       Maximum="{TemplateBinding ScrollableWidth}" 
       Minimum="0" 
       Orientation="Horizontal" 
       ViewportSize="{TemplateBinding ViewportWidth}" 
       Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" 
       Value="{Binding HorizontalOffset, 
           Mode=OneWay, 
           RelativeSource={RelativeSource TemplatedParent}}" /> 
    </Grid> 
</ControlTemplate> 

Interesującą bit jest:

<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" 
         Grid.Row="0" 
         Grid.Column="0" 
         Margin="{TemplateBinding Padding}" 
         CanContentScroll="{TemplateBinding CanContentScroll}" 
         CanHorizontallyScroll="False" 
         CanVerticallyScroll="False" 
         Content="{TemplateBinding Content}" 
         ContentTemplate="{TemplateBinding ContentTemplate}" /> 

teraz, aby rozwiązać swój problem można po prostu ustawić margines tam 0 zamiast {TemplateBinding Padding}, a otrzymasz żądany wynik.

Ale dlaczego musieliśmy to zrobić?

TemplateBinding Padding wydaje się być ignorowanie wartości ustawionej bezpośrednio nad ScrollViewer który jest w zakresie wewnętrznego i podnosi wartość Dodat odziedziczone od rodzica (Button), który jest 15.

Ok to dziwne, ale co gorsza to tylko do Padding. Foreground, Background, Margin są w porządku po ustawieniu bezpośrednio na ScrollViewer zastępują one pola TextBox. Potwierdziłem nawet przeniesienie zestawu Padding bezpośrednio do urządzenia TextBox do domyślnego ustawiacza stylów, aby sprawdzić, czy problem dotyczy jakiegoś precedensu.

Wyglądało na to, że tak nie jest. Mam takie same wyniki.

Wypełnienie jest zdefiniowane w System.Windows.Controls.Control, który jest tą samą klasą, Pierwszy plan i tło pochodzą z tego ScrollViewer dziedziczenia. Nie wiem, dlaczego samo podszycie zachowuje się inaczej.

Próbowałem też zmiany prezentera coś jak

<ScrollContentPresenter x:Name="PART_ScrollContentPresenter" 
         Grid.Row="0" 
         Grid.Column="0" 
         Margin="{TemplateBinding Margin}" 
         CanContentScroll="{TemplateBinding CanContentScroll}" 
         CanHorizontallyScroll="False" 
         CanVerticallyScroll="False" 
         Content="{TemplateBinding Padding}" 
         ContentTemplate="{TemplateBinding ContentTemplate}" /> 

To druku 15,15,15,15. Nie robi tego dla Margin.

Ten sam efekt z wiązaniem {Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=Padding}.

Widziałem jeden wpis z informacją, że ScrollViewer nie przekazuje właściwości ustawionych na nim do jego dzieci.Naprawdę nie rozumiem, skoro tak było, jak mogłem pokonać przeszkody? Co tak specjalnego o Padding? Jeśli jest to prawidłowe zachowanie, tak naprawdę nie widzę sposobu, aby pozbyć się tego zachowania, także bez szablonów ScrollViewer i jest to jedna myląca implementacja.

+0

Dziękuję bardzo za szczegółową i dobrze przetestowaną odpowiedź! Naprawdę doceniam dane wejściowe i informacje dotyczące szablonu scrollviewer. Jest to z pewnością dziwny przypadek, który nie ma dla mnie większego sensu. –

3

To dziwne zachowanie pochodzi z TextBoxBase. To nadpisuje metadanych dla niektórych z nich znajduje się właściwości zależność i dla właściwości Padding wygląda:

Control.PaddingProperty.OverrideMetadata(
    typeof (TextBoxBase), 
    new FrameworkPropertyMetadata(
     new PropertyChangedCallback(TextBoxBase.OnScrollViewerPropertyChanged))); 

Jeśli spojrzeć na OnScrollViewerPropertyChanged obsługi zauważysz, że przechodzi ona wartość zmianą własności na jego ScrollViewer:

if (newValue == DependencyProperty.UnsetValue) 
    textBoxBase.ScrollViewer.ClearValue(e.Property); 
    else 
    textBoxBase.ScrollViewer.SetValue(e.Property, newValue); 

Tak więc, bez względu na wartość padding masz ustawione w szablonie sterowania, zostanie zastąpione przez wartości lokalnej TextBox w czasie pracy.

Aby zrekompensować ten wyściółkę można ustawić margines negatywnego do ScrollViewer w szablonie:

<ScrollViewer 
    x:Name="PART_ContentHost" 
    Margin="{TemplateBinding Padding, Converter={StaticResource InvertThicknessConverter}}" 
/> 

gdzie InvertThicknessConverter jest konwerter wartość, która neguje każdy składnik przeszedł wartości grubość:

public class InvertThicknessConverter: IValueConverter 
{ 
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (value is Thickness) return InvertThickness((Thickness)value); 
     return value; 
    } 

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 
    { 
     if (value is Thickness) return InvertThickness((Thickness)value); 
     return value; 
    } 

    private static Thickness InvertThickness(Thickness value) 
    { 
     return new Thickness(-value.Left, -value.Top, -value.Right, -value.Bottom); 
    } 
} 
+0

Interesujące znalezisko! Dzięki za dalsze szczegóły. Chociaż prawdopodobnie będę nadal używać niestandardowego stylu przewijania, aby obejść ten problem, dobrze jest mieć opcje i zrozumieć, skąd się bierze problem. –

+0

To jest rzeczywiście lepsze niż zaakceptowana odpowiedź, ponieważ używasz konwertera zamiast przesuwać 'ControlTemplate'; działa to jednak tylko wtedy, gdy w moim przypadku wiążesz z 'Wyróżnikiem' ScrollViewer', _not_ 'Margin'. –

0

wpadłem ten problem podczas ustawiania właściwości Padding na DataGridColumnHeader, i obejść go, określając właściwość padding blisko, ale nie równa zero:

<Style x:Key="CustomHeader" TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}"> 
    <!-- Why you might ask do we need a value that is near but not actually 0? No idea, but if it is not there, "4" is used. --> 
    <!-- See http://stackoverflow.com/questions/16424739/textbox-template-padding-issue --> 
    <Setter Property="Padding" Value="0,0,0.000000000001,0" /> 
</Style>