7

TextBox間にパディングがある 2 つの境界線を持つようにa を再テンプレート化しようとしています。ただし、PART_ のパディングを明示的ContentHostにゼロに設定した場合でも、コントロールのパディングは常に内側にも適用さScrollViewerれます。

小さな例:

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

結果は次のようなテキストボックスです: [15[15 Textfield 15]15]

私はただ期待していましたが: [15[ Textfield ]15]

PART_ContentHost ( scrollviewer / textfield ) にゼロパディングを適切に強制するにはどうすればよいですか?

4

3 に答える 3

9

誰かがこの動作についてより良い説明を持っていない限り、これは本当に奇妙なバグのように見えます。

ScrollViewer(PART_ContentHost) は内部的に次のTemplateようなものを使用します:

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

興味深い点は次のとおりです。

<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}" />

問題を解決するには、代わりに Margin を 0 に設定するだけで{TemplateBinding Padding}、目的の出力が得られます。

しかし、なぜそれを行う必要があったのでしょうか?

TemplateBinding Padding内部スコープにあるに直接設定された値を無視しているようで、 15 でScrollViewerある Parent( ) から継承された Padding 値を選択しますButton

それは奇妙ですが、さらに悪いことに、これはパディング専用です。ForegroundBackground、 にMargin直接設定すると、のフィールドScrollViewerが上書きされます。使用中のセットをデフォルトのスタイルセッターに直接TextBox移動して、優先順位のケースが問題であるかどうかを確認することさえできました。PaddingTextBox

そうではないようでした。同じ出力を得ました。

パディングはSystem.Windows.Controls.Control、Foreground と Background がScrollViewer継承する同じクラスで定義されます。パディングだけで動作が異なる理由がわかりません。

また、プレゼンターを次のようなものに変更してみました

<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}" />

15,15,15,15 と表示されます。に対してはそうしませんMargin

{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ScrollViewer}}, Path=Padding}バインディングと同じ効果。

ScrollViewer私は、それに設定されたプロパティをその子に渡さないという投稿を見ました。もしそうなら、どうやってBackground,Marginを上書きしてもいいのでしょうか? 何がそんなに特別なのPadding?それが有効な動作である場合、テンプレートを使用せずにその動作を取り除く方法が実際にはわかりません。これはScrollViewer紛らわしい実装の 1 つです。

于 2013-05-07T18:51:05.990 に答える
3

この奇妙な動作はTextBoxBase. 一部の依存関係プロパティとPadding次のようなプロパティのメタデータをオーバーライドします。

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

ハンドラーを見るとOnScrollViewerPropertyChanged、変更されたプロパティの値が次のように渡されていることがわかりますScrollViewer

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

そのため、コントロール テンプレートに設定した値に関係なく、実行時Paddingにローカル値で上書きされます。TextBox

このパディングを補うためScrollViewerに、テンプレートで負のマージンを設定できます。

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

ここInvertThicknessConverterで、渡された厚さ値のすべてのコンポーネントを無効にする値コンバーターです。

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);
    }
}
于 2014-10-10T13:14:19.487 に答える