2013-04-11 9 views
18

私はthis questionを見ていましたが、文字列以外の値へのバインドLabel.Contentは暗黙的なTextBlockスタイルを適用しますが、文字列へのバインディングはそうではありません。Label.Contentを文字列ではなく文字列にバインドするときに暗黙的なTextBlockスタイルが適用されるのはなぜですか?

ここで問題を再現するいくつかのサンプルコードです:

バインド値のコードが

SomeDecimal = 50; 
SomeString = SomeDecimal.ToString(); 

され、最終結果は、暗黙的からMarginプロパティで、次のようになります

<Window.Resources> 
    <Style TargetType="Label"> 
     <Setter Property="FontSize" Value="26"/> 
     <Setter Property="Margin" Value="10"/> 
     <Setter Property="VerticalAlignment" Value="Center"/> 
    </Style> 
    <Style TargetType="{x:Type TextBlock}"> 
     <Setter Property="FontSize" Value="26"/> 
     <Setter Property="Margin" Value="10"/> 
    </Style> 
</Window.Resources> 

<Grid> 
    <StackPanel Orientation="Horizontal"> 
     <Label Content="{Binding SomeString}" Background="Red"/> 
     <Label Content="{Binding SomeDecimal}" Background="Green"/> 
    </StackPanel> 
</Grid> 

TextBlockスタイルはLabelに適用され、非文字列のみにバインドされます:

enter image description here

どちらのラベルは私がSnoopでVisualTreeをチェックアウトすると、私は第二のTextBlockは、暗黙的なスタイルからマージンを適用する以外、それは一方で、両方の要素のためにまったく同じに見えることがわかります

<Label> 
    <Border> 
     <ContentPresenter> 
      <TextBlock /> 
     </ContentPresenter> 
    </Border> 
</Label> 

としてレンダリングされます最初はありません。

enter image description here

私は、デフォルトのラベルテンプレートのコピーを引き出すためにブレンドを使用しましたが、そこに奇妙な何も表示されない、と私は両方の私のラベルにテンプレートを適用すると、同じことが起こります。

<Label.Template> 
    <ControlTemplate TargetType="{x:Type Label}"> 
     <Border BorderBrush="{TemplateBinding BorderBrush}" 
       BorderThickness="{TemplateBinding BorderThickness}" 
       Background="{TemplateBinding Background}" 
       Padding="{TemplateBinding Padding}" 
       SnapsToDevicePixels="True"> 
      <ContentPresenter ContentTemplate="{TemplateBinding ContentTemplate}" 
           Content="{TemplateBinding Content}" 
           ContentStringFormat="{TemplateBinding ContentStringFormat}" 
           HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" 
           RecognizesAccessKey="True" 
           SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" 
           VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/> 
     </Border> 
     <ControlTemplate.Triggers> 
      <Trigger Property="IsEnabled" Value="False"> 
       <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/> 
      </Trigger> 
     </ControlTemplate.Triggers> 
    </ControlTemplate> 
</Label.Template> 

またTextBlockにデフォルトContentTemplateを設定すると、両方の項目は、暗黙的なスタイルずにレンダリングさせるんので、WPFは、パートなどの非文字列値をレンダリングしようとすると、それはとは何かを持っている必要がありますことに留意すべきですUIの

<Window.Resources> 
    <Style TargetType="Label"> 
     <Setter Property="FontSize" Value="26"/> 
     <Setter Property="Margin" Value="10"/> 
     <Setter Property="VerticalAlignment" Value="Center"/> 
    </Style> 
    <Style x:Key="TemplatedStyle" TargetType="Label" BasedOn="{StaticResource {x:Type Label}}"> 
     <Setter Property="ContentTemplate"> 
      <Setter.Value> 
       <DataTemplate> 
        <TextBlock Text="{Binding }"/> 
       </DataTemplate> 
      </Setter.Value> 
     </Setter> 
    </Style> 
    <Style TargetType="{x:Type TextBlock}"> 
     <Setter Property="FontSize" Value="26"/> 
     <Setter Property="Margin" Value="10"/> 
    </Style> 
</Window.Resources> 

<Grid> 
    <StackPanel Orientation="Horizontal"> 
     <Label Content="{Binding SomeString}" Background="Red"/> 
     <Label Content="{Binding SomeDecimal}" Background="Green"/> 
     <Label Content="{Binding SomeString}" Background="Red" 
       Style="{StaticResource TemplatedStyle}"/> 
     <Label Content="{Binding SomeDecimal}" Background="Green" 
       Style="{StaticResource TemplatedStyle}"/> 
    </StackPanel> 
</Grid> 

enter image description here

暗黙のTextBlockのスタイルを使用して描画するUIに挿入された文字列以外の原因が、UIに挿入された文字列ではないでしょうか?ロジックは何ですかそして、これはどこで起こりますか?

+1

この質問は私に興味をそそっていたので、古いリフレクターを始める必要がありました。まあ、これまで私が見つけたことは一つしかない。 TextBlockは単純なテキストと "複雑な"オブジェクトを区別しているようですが、両方のために2つの異なるサブクラスを使用します。残念ながら、これが終わるところです。私は特殊なスタイルや何かの痕跡を見つけることができません。 TextBlockをContentTemplateに提供し、それが何かを変更するかどうかを確認することを試してください。その場合、それはその神秘的なデフォルトテンプレートと何か関係があります。 – dowhilefor

+0

@dowhilefor私はBlendからデフォルトのテンプレートを引っ張って両方のラベルに適用しようとしました。同じことが起こりました(その情報で更新された質問)。 WPFは 'System.String'を暗黙的なスタイリングなしで' TextBlock'としてレンダリングすることを知っていますが、文字列以外の値をレンダリングするときは、 'TextBlock'を' .ToString 'にバインドして描画します() 'を呼び出し、暗黙のスタイリングを適用します。 – Rachel

+0

@dowhileforデフォルトの 'ContentTemplate'を' 'に設定すると暗黙のスタイルなしで両方のアイテムがレンダリングされますので、非文字列オブジェクトのデフォルトの 'ContentTemplate' – Rachel

答えて

2

EDIT:(多分下にこれを移動?)

そして、私はもう少しつつい - 私は問題の核心「私は考える」に関する(ワット/強調になったと思うを)

(再び、私たちはこれについて「怠惰」行く必要があるいくつかのButton1_Clickか何かにこれを入れて - 私たちはビジュアルツリーが構築必要があるとして、 - 我々だけのテンプレートを作ったとして、私たちは「ロードされた」でそれを行うことはできません - これはより良い初期化技術を必要としましたが、それは単なるテストなので誰が気にします)

void Button_Click(object sender, EventArgs e) 
{ 
var insideTextBlock = FindVisualChild<TextBlock>(_labelString); 
var value = insideTextBlock.GetProperty<bool>("HasImplicitStyleFromResources"); // false 
value = insideTextBlock.GetProperty<bool>("ShouldLookupImplicitStyles"); // true 

var boundaryElement = insideTextBlock.TemplatedParent; // ContentPresenter and != null 

insideTextBlock = FindVisualChild<TextBlock>(_labelDecimal); 
value = insideTextBlock.GetProperty<bool>("HasImplicitStyleFromResources"); // true 
value = insideTextBlock.GetProperty<bool>("ShouldLookupImplicitStyles"); // true 

boundaryElement = insideTextBlock.TemplatedParent; // == null !! 

ここに述べたようにImplicit styles in Application.Resources vs Window.Resources?
FindImplicitStyleResourceは(FrameworkElementで)のようなものを使用しています...

boundaryElement = fe.TemplatedParent; 

とNO TemplatedParent(および原因TextBlockDefaultTemplate内に構築され 通りに)が存在しない場合ようだ - が はありません「境界」セットされている - と、暗黙のリソースを検索します/スタイル - はすべてに伝播します。



オリジナル回答:(ちょうど到着した場合は、この最初のを読んで)

(@dowhileforと@Jehofはすでに主なものに触れた)
私はこれがわかりませんそれはまだ推測の仕事ですが、私は何が起こっているのかを説明するためにもっと多くのスペースが必要でした。

あなたは、ウェブ上の「のContentPresenterソース」コードを見つけることができます - それは、リフレクタを使用するよりも簡単だ - それのためだけの「グーグル」を、私は明白な理由のためにそれをここに投稿していないよ:)

それはについてですContentPresenter(この順で)のために選択されているContentTemplate ...

ContentTemplate // if defined 
ContentTemplateSelector // if defined 
FindResource // for typeof(Content) - eg if defined for sys:Decimal takes that one 
DefaultTemplate used internally by the presenter 
...specific templates are chosen based on typeof(Content) 

そして実際それはContentPresenterを使用していますが、どのLabel ContentControlにまたはコントロールテンプレートとは何の関係もありません。 私の目標「文字列」またはあらゆる種類のコンテンツについても同様の挙動を再現することでした - それとも、ここで

内で何が起こっているのREPROがあるなど資源に結合することができます。背後にあるXAMLで

だけで「名前」ラベル(と、それはタイプミスではなく、故意にソートの活躍の場を平準化するために、両方の中の文字列を入れて)...

<Label Name="_labelString" Content="{Binding SomeString}" Background="Red"/> 
<Label Name="_labelDecimal" Content="{Binding SomeString}" Background="Green"/> 

とコードから(最小限のコード何プレゼンター模倣のその種):
注:私はプレゼンターへのアクセスを必要に応じてLoadedにそれをやった暗黙的_labelStringための最初の部分は、()「テキスト」のテンプレートが何をし

void Window1_Loaded(object sender, RoutedEventArgs e) 
{ 
FrameworkElementFactory factory = new FrameworkElementFactory(typeof(TextBlock)); 
factory.SetValue(TextBlock.TextProperty, new TemplateBindingExtension(ContentProperty)); 
var presenterString = FindVisualChild<ContentPresenter>(_labelString); 
presenterString.ContentTemplate = new DataTemplate() { VisualTree = factory }; 

// return; 

var presenterDecimal = FindVisualChild<ContentPresenter>(_labelDecimal); 
presenterDecimal.ContentTemplate = new DataTemplate(); 
// just to avoid the 'default' template kicking in 

// this is what 'default template' does actually, the gist of it 
TextBlock textBlock = new TextBlock(); 
presenterDecimal.SetProperty(typeof(FrameworkElement), "TemplateChild", textBlock); 
textBlock.Text = presenterDecimal.Content.ToString(); 

を作成文字列の場合。

その直後にreturnとすると、2つの同じ見た目のボックスが表示され、暗黙的なテンプレートは表示されません。

2番目の部分(_labelDecimal)は、「小数点」に対して呼び出される「デフォルトテンプレート」を模倣しています。

最終結果は元の例と同じになります。我々 はstringdecimalのようにテンプレートを構築しましたが、 はコンテンツに何かを入れることができます(もちろん意味があれば)。私の推測では、このようなものである -理由を

(遠い一定のけれども - 誰かが私が推測するより賢明なものでジャンプします)...

このリンクFrameworkElementFactory

あたりとして

このクラスは、ControlTemplateや DataTemplateなどのFrameworkTemplateのサブクラスである というテンプレートをプログラムで作成するための非推奨の方法です。このクラスを使用してテンプレートを作成すると、すべてのテンプレート機能が使用可能になるわけではありません。 テンプレートをプログラムで作成するには、XamlReaderクラスのLoadメソッドを使用して、文字列または メモリストリームからXAMLを読み込むことをお勧めします。

私は、TextBlockの定義されたスタイルを呼び出さないと思っています。

「その他のテンプレート」(デフォルトテンプレート)は、実際にはTextBlockとその行のどこかを構築しますが、暗黙のスタイルを実際に選択します。

私は、WPFの「内部」全体と、実際にスタイルが適用されるまでの短い間に、結論を出すことができました。


このコードFinding control within WPF itemscontrolFindVisualChildに使用しました。
そして、SetPropertyは反射です。これをすべて行うには、アクセスする必要があるプロパティです。例えば

public static void SetProperty<T>(this object obj, string name, T value) { SetProperty(obj, obj.GetType(), name, value); } 
public static void SetProperty<T>(this object obj, Type typeOf, string name, T value) 
{ 
    var property = typeOf.GetProperty(name, BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public); 
    property.SetValue(obj, value, null); 
} 
0

この質問とすべての貴重なコメントを経て、私はTextBlock Stylingに関するいくつかの調査を行った。

ここでの問題は、LabelまたはTextBlockではなく、contentpresenterと、Label、Button、ComboBoxItemなどのcontentpresenterを使用するコントロールです。

MSDNからコンテンツプレゼンターの特性の一つ:http://msdn.microsoft.com/en-us/library/system.windows.controls.contentpresenter.aspx

"を文字列にコンテンツの種類を変換にTypeConverterがあれば、のContentPresenterがそのにTypeConverterを使用し、その文字列を格納するためのTextBlockを作成します。テキストブロックはSomeStringコンテンツプレゼンタについて、上記の例で

"が表示されるテキストブロックに変換し、それを20

を行うラベルマージン(10)と一緒のTextBlockマージン(10)を適用しています

      <ContentPresenter > 
           <ContentPresenter.Resources> 
            <Style TargetType="{x:Type TextBlock}"> 
             <Setter Property="Margin" Value="5" /> 
            </Style> 
           </ContentPresenter.Resources> 
          </ContentPresenter> 

下に示すように、このシナリオを避けるためにあなたがのContentPresenter内のTextBlockのスタイルをオーバーライドする必要が続いてあなたのコードへの変更です。

<Window.Resources> 
     <Style TargetType="Label"> 
      <Setter Property="FontSize" Value="26"/> 
      <Setter Property="Margin" Value="10"/> 

      <Setter Property="VerticalAlignment" Value="Center"/> 
      <Setter Property="Template"> 
       <Setter.Value> 

        <ControlTemplate TargetType="Label"> 
         <Grid> 
          <Rectangle Fill="{TemplateBinding Background}" /> 
          <ContentPresenter > 
           <ContentPresenter.Resources> 
            <Style TargetType="{x:Type TextBlock}"> 
             <Setter Property="Margin" Value="5" /> 
            </Style> 
           </ContentPresenter.Resources> 
          </ContentPresenter> 
         </Grid> 

        </ControlTemplate> 
       </Setter.Value> 
      </Setter> 
     </Style> 
     <Style TargetType="{x:Type TextBlock}"> 
      <Setter Property="FontSize" Value="26"/> 
      <Setter Property="Margin" Value="10"/> 
      <Setter Property="Foreground" Value="Pink" /> 
     </Style> 
    </Window.Resources> 

    <Grid> 
     <StackPanel Orientation="Horizontal"> 
      <Label Content="{Binding SomeString}" Background="Red" /> 
      <Label Content="{Binding SomeDecimal}" Background="Green"/> 
     </StackPanel> 
    </Grid> 
</Window> 

この説明は私の理解に基づくものです。あなたのコメントを教えてください。

ありがとうございました

+0

私は実際にラベルの 'ContentTemplate'を上書きすると、暗黙的なTextBlockスタイルの適用を中止することを説明する私の質問に少し追加しました。私の質問は、UIに文字列を配置するのは暗黙のスタイルを適用するのに対し、UIでは文字列に変換されない文字列を配置するのはなぜですか?私はあなたがMSDNの引用に近いかもしれないと思う。 'TypeConverter'を使って' TextBlock'を作成するプロセスは、テンプレートの境界にかかわらず暗黙のスタイルを適用していると思います。 – Rachel

+0

@Rachel - 'TypeConverter'は' textBlock.Text = presenter.Content.ToString(); 'を作ったところです(望むならコードと完全なリンクを投稿できますが、それは非常に「終わり」にテキストを埋めます。 TextBlockの作成とは何の関係もありません(テンプレートが行います)。私の 'EDIT'を見てください。問題は実際には'境界線 'です。 – NSGaga

0

私のコメントによると、私は質問に詳しい情報を追加します。直接答えではなく、記述された問題に追加情報を提供します。

以下のXAMLは、説明したビヘイビアーをVisual Studioのデザイナーに直接表示し、問題の原因と思われるContentPresenterに絞り込みました。スタイルは最初の両方のContentPresenter(intPresenterboolPresenter)に適用されますが、コンテンツ(stringPresenter)として文字列を使用する最後のスタイルは適用されません。デバッガで

<Window.Resources> 
    <system:Int32 x:Key="intValue">5</system:Int32> 
    <system:Boolean x:Key="boolValue">false</system:Boolean> 
    <system:String x:Key="stringValue">false</system:String> 
    <Style TargetType="{x:Type TextBlock}"> 
    <Setter Property="FontSize" Value="26" /> 
    <Setter Property="Margin" Value="10" /> 
    </Style> 
</Window.Resources> 

<Grid> 
    <StackPanel Orientation="Horizontal"> 
    <ContentPresenter x:Name="intPresenter" 
         VerticalAlignment="Center" 
         Content="{StaticResource intValue}" /> 
    <ContentPresenter x:Name="boolPresenter" 
         VerticalAlignment="Center" 
         Content="{StaticResource boolValue}" /> 
    <ContentPresenter x:Name="stringPresenter" 
         VerticalAlignment="Center" 
         Content="{StaticResource stringValue}" /> 
    </StackPanel> 
    </Grid> 

私はintPresenterはそうではないstringPresenterがDefaultStringTemplateを使用していることを分析されています。

enter image description here

もその興味深いstringPresenterそのことによってながらintPresenterLanguageは、設定されていること。

そしてメソッドの実装は(dotPeekから取られた)そのようなものになります

private bool IsUsingDefaultStringTemplate 
    { 
     get 
     { 
     if (this.Template == ContentPresenter.StringContentTemplate || this.Template == ContentPresenter.AccessTextContentTemplate) 
      return true; 
     DataTemplate dataTemplate1 = ContentPresenter.StringFormattingTemplateField.GetValue((DependencyObject) this); 
     if (dataTemplate1 != null && dataTemplate1 == this.Template) 
      return true; 
     DataTemplate dataTemplate2 = ContentPresenter.AccessTextFormattingTemplateField.GetValue((DependencyObject) this); 
     return dataTemplate2 != null && dataTemplate2 == this.Template; 
     } 
    } 

StringContentTemplateとAccessTextTemplateはビジュアルを生成するFrameworkElementFactoryを使用しています。

関連する問題