我想在WPF應用程序中使用自定義控件,並且使用StringFormat綁定時出現了一些問題。在自定義控件上綁定StringFormat
問題很容易重現。首先,我們創建一個WPF應用程序並將其稱爲「TemplateBindingTest」。在那裏,添加一個只有一個屬性(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>
現在,只需將控件添加到窗口,並設置Text屬性的綁定,使用的StringFormat。還添加了一個簡單的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是誤導。我發現原因和骯髒的解決方法。
首先,請注意這個問題是按鈕的內容屬性是相同的:
<Button Content="{Binding Path=Text, StringFormat=Test3: {0}}" />
那麼,這是怎麼回事?讓我們使用Reflector並潛入BindingBase類的StringFormat屬性。 「分析」功能顯示該屬性由內部DetermineEffectiveStringFormat
方法使用。讓我們看看這個方法:
internal void DetermineEffectiveStringFormat()
{
Type propertyType = this.TargetProperty.PropertyType;
if (propertyType == typeof(string))
{
// Do some checks then assign the _effectiveStringFormat field
}
}
問題就在這裏。 effectiveStringFormat字段是解析Binding時使用的字段。只有在DependencyProperty類型爲String
(我的Button爲Content的內容屬性Object
)時纔會分配此字段。
爲什麼選擇Object?因爲我的自定義控件比我粘貼的控件複雜一些,並且像Button一樣,我希望控件的用戶能夠提供子控件而不僅僅是文本。
那麼,現在呢?即使在WPF核心控件中,我們也會遇到一種行爲,所以我可以將其保留爲「原樣」。儘管如此,正如我的自定義控制用於僅在一個內部項目,我希望它是更容易從XAML中使用,我決定使用這個技巧:
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使用的屬性。當爲Text
提供值時,將應用StringFormat,因爲該屬性的類型爲String
,那麼使用回調將該值轉發給Header
屬性。它的工作原理,但它確實髒:
- 的
Header
和Text
財產不同步,如不更新Text
當我更新Header
。我選擇不爲Text
屬性提供吸氣劑以避免一些錯誤,但如果有人直接從DependencyProperty(GetValue(TextProperty)
)中讀取值,它仍然可能發生。 - 如果某人向
Header
和Text
屬性提供值,則會發生不可預知的行爲,因爲其中一個值將丟失。
總的來說,我不會推薦使用這個黑客。只有在您的項目是真的才能控制您的項目。如果控件在其他項目中使用的機會甚微,那就放棄StringFormat。
錯誤。在格式化字符串之前,運行時爲所有對象調用'object.ToString()'。 – 2011-12-27 14:05:25
是的,但它在應用StringFormat之前檢查依賴項屬性的基礎類型。如果我將dp的類型更改爲String而不是Object,它會有效。 – 2011-12-27 14:11:06