2011-12-27 103 views
5

我想在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屬性。它的工作原理,但它確實髒:

  • HeaderText財產不同步,如不更新Text當我更新Header。我選擇不爲Text屬性提供吸氣劑以避免一些錯誤,但如果有人直接從DependencyProperty(GetValue(TextProperty))中讀取值,它仍然可能發生。
  • 如果某人向HeaderText屬性提供值,則會發生不可預知的行爲,因爲其中一個值將丟失。

總的來說,我不會推薦使用這個黑客。只有在您的項目是真的才能控制您的項目。如果控件在其他項目中使用的機會甚微,那就放棄StringFormat。

回答

2

StringFormat結合到string屬性時被使用,而在控制的Text屬性是object型的,因此StringFormat被忽略。

+0

錯誤。在格式化字符串之前,運行時爲所有對象調用'object.ToString()'。 – 2011-12-27 14:05:25

+2

是的,但它在應用StringFormat之前檢查依賴項屬性的基礎類型。如果我將dp的類型更改爲String而不是Object,它會有效。 – 2011-12-27 14:11:06