WPFアプリケーションでカスタムコントロールを使用しようとしていますが、StringFormatバインディングを使用していくつかの問題があります。カスタムコントロールのStringFormatでのバインド
問題は再現しやすいです。まず、WPFアプリケーションを作成し、それを "TemplateBindingTest"と呼んでみましょう。そこで、1つのプロパティ(Text)だけを持つカスタムViewModelを追加し、それをWindowのDataContextに割り当てます。 Textプロパティを "Hello World!"に設定します。
今すぐソリューションにカスタムコントロールを追加します。カスタムコントロール、それは得ることができるように簡単です:
using System.Windows;
using System.Windows.Controls;
namespace TemplateBindingTest
{
public class CustomControl : Control
{
static CustomControl()
{
TextProperty = DependencyProperty.Register(
"Text",
typeof(object),
typeof(CustomControl),
new FrameworkPropertyMetadata(null));
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
public static DependencyProperty TextProperty;
public object Text
{
get
{
return this.GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
}
}
}
}
液にカスタムコントロールを追加する場合、Visual Studioは自動的にgeneric.xamlファイルを使用して、テーマフォルダを作成しました。のは、そこにコントロールのデフォルトのスタイルを入れてみましょう:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TemplateBindingTest">
<Style TargetType="{x:Type local:CustomControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:CustomControl}">
<TextBlock Text="{TemplateBinding Text}" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
は今、ちょうどウィンドウにコントロールを追加し、StringFormatを使用して、Textプロパティにバインディングを設定します。また、結合構文が正しいことを確認するために、単純なのTextBlockを追加します。
<Window x:Class="TemplateBindingTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:TemplateBindingTest="clr-namespace:TemplateBindingTest" Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TemplateBindingTest:CustomControl Text="{Binding Path=Text, StringFormat=Test1: {0}}"/>
<TextBlock Text="{Binding Path=Text, StringFormat=Test2: {0}}" />
</StackPanel>
コンパイル、実行、aaaaand ...テキストが画面に表示することです:
のHello World !
Test2:Hello World!
カスタムコントロールでは、StringFormatは完全に無視されます。 VS出力ウィンドウにエラーは表示されません。どうしたの?
編集:回避策。
いいえ、TemplateBindingは誤解を招きました。私は原因と汚れた回避策を見つけました。
まず、問題はボタンのContentプロパティと同じであることに気づく:
<Button Content="{Binding Path=Text, StringFormat=Test3: {0}}" />
だから、何が起こっているの? Reflectorを使用し、BindingBaseクラスのStringFormatプロパティに移動しましょう。 「分析」機能は、このプロパティが内部のDetermineEffectiveStringFormat
メソッドによって使用されていることを示しています。この方法を見てみましょう:
ここに問題があります。 effectiveStringFormatフィールドは、Bindingを解決するときに使用されるフィールドです。また、このフィールドは、DependencyPropertyのタイプがString
である場合にのみ割り当てられます(これはButtonのコンテンツプロパティとしてObject
です)。
なぜオブジェクトですか?私のカスタムコントロールは、貼り付けたものよりも少し複雑なので、ボタンのように、コントロールのユーザーがテキストだけでなく子コントロールを提供できるようにしたい。
だから今は何ですか?私たちはWPFのコアコントロールでも動作しているので、そのままの状態にしておきます。
using System.Windows;
using System.Windows.Controls;
namespace TemplateBindingTest
{
public class CustomControl : Control
{
static CustomControl()
{
TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(CustomControl),
new FrameworkPropertyMetadata(null, Callback));
HeaderProperty = DependencyProperty.Register(
"Header",
typeof(object),
typeof(CustomControl),
new FrameworkPropertyMetadata(null));
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
static void Callback(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
obj.SetValue(HeaderProperty, e.NewValue);
}
public static DependencyProperty TextProperty;
public static DependencyProperty HeaderProperty;
public object Header
{
get
{
return this.GetValue(HeaderProperty);
}
set
{
SetValue(HeaderProperty, value);
}
}
public string Text
{
set
{
SetValue(TextProperty, value);
}
}
}
}
Header
が私のTemplateBindingのに使用されるプロパティです:それでも、私のカスタムコントロールとしてのみ内部のプロジェクトで使用され、私はXAMLから使いやすいようにしたいですが、私はこのハックを使用することにしました。値がText
に指定されている場合、プロパティのタイプがString
であるためStringFormatが適用され、コールバックを使用して値がHeader
プロパティに転送されます。それは動作しますが、それは本当に汚いです:私はHeader
を更新するときText
が更新されないよう
Header
とText
プロパティは、同期していません。何らかの間違いを避けるためにText
プロパティにgetterを提供しないことを選択しましたが、誰かがDependencyProperty(GetValue(TextProperty)
)から値を直接読み取ってもそれが起こる可能性があります。- 値のいずれかが失われるため、誰かが
Header
とText
の両方のプロパティに値を提供すると、予期しない動作が発生することがあります。
全体として、私はこのハッキングを使用することをお勧めしません。あなたのプロジェクトのコントロールにが実際にの場合にのみそれをしてください。コントロールが別のプロジェクトで使用される可能性が少しでもある場合は、StringFormatをあきらめてください。
が間違っています。ランタイムは、文字列の書式を設定する前に、すべてのオブジェクトに対して 'object.ToString()'を呼び出します。 –
はい、ただし、StringFormatを適用する前に、依存関係プロパティの基になる型をチェックします。私のdpの型をObjectではなくStringに変更すると動作します。 –