2011-04-08 29 views
2

我希望有一個TextBox以貨幣格式顯示數字(通過在綁定上設置StringFormat=c)。當選擇TextBoxIsKeyboardFocused==true)時,我希望格式化消失,直到關注TextBox丟失。通過使用樣式來修改文本框文本的參數綁定

我找到了一種方法來做到這一點,代碼粘貼在下面。我的問題是,綁定是在Style內部指定的 - 這意味着我必須重新鍵入每個TextBox的樣式,我希望這樣做。理想情況下,我希望將樣式置於某個中央位置,並在每個TextBox重複使用該樣式,併爲每個樣式指定不同的綁定目標。

有沒有辦法讓我使用Style來設置現有綁定的參數,如Text.Binding.StringFormat=""? (而不是將文本的整個值設置爲新定義的綁定)

其他建議,以實現這一點也將不勝感激。

代碼(這個作品,它只是不方便):

<TextBox x:Name="ContractAmountTextBox"> 
<TextBox.Style> 
    <Style TargetType="{x:Type TextBox}">          
     <Style.Triggers> 
      <DataTrigger Binding="{Binding IsKeyboardFocused, ElementName=ContractAmountTextBox}" Value="False"> 
       <Setter Property="Text" Value="{Binding Path=ContractAmount, UpdateSourceTrigger=LostFocus, StringFormat=c}"/> 
      </DataTrigger> 
      <DataTrigger Binding="{Binding IsKeyboardFocused, ElementName=ContractAmountTextBox}" Value="True"> 
       <Setter Property="Text" Value="{Binding Path=ContractAmount, UpdateSourceTrigger=LostFocus}"/> 
      </DataTrigger> 
     </Style.Triggers> 
    </Style> 
</TextBox.Style> 

回答

6

它與附加屬性是可行的,但它意味着你必須更換全部綁定,然後把它放回去。

這裏有一個快速和骯髒的實施:

public static class TextBoxBehavior 
{ 

    #region StringFormat 

    public static string GetStringFormat(TextBox obj) 
    { 
     return (string)obj.GetValue(StringFormatProperty); 
    } 

    public static void SetStringFormat(TextBox obj, string value) 
    { 
     obj.SetValue(StringFormatProperty, value); 
    } 


    public static readonly DependencyProperty StringFormatProperty = 
     DependencyProperty.RegisterAttached(
      "StringFormat", 
      typeof(string), 
      typeof(TextBoxBehavior), 
      new UIPropertyMetadata(
      null, 
      StringFormatChanged)); 

    // Used to store the original format 
    private static readonly DependencyPropertyKey OriginalBindingPropertyKey = 
     DependencyProperty.RegisterAttachedReadOnly(
      "OriginalBinding", 
      typeof(BindingBase), 
      typeof(TextBoxBehavior), 
      new UIPropertyMetadata(null)); 

    private static void StringFormatChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) 
    { 
     TextBox textBox = o as TextBox; 
     if (textBox == null) 
      return; 

     string oldValue = (string)e.OldValue; 
     string newValue = (string)e.NewValue; 

     if (!string.IsNullOrEmpty(oldValue) && string.IsNullOrEmpty(newValue)) 
     { 
      // Update target for current binding 
      UpdateTextBindingSource(textBox); 

      // Restore original binding 
      var originalBinding = (BindingBase)textBox.GetValue(OriginalBindingPropertyKey.DependencyProperty); 
      if (originalBinding != null) 
       BindingOperations.SetBinding(textBox, TextBox.TextProperty, originalBinding); 
      textBox.SetValue(OriginalBindingPropertyKey, null); 
     } 
     else if (!string.IsNullOrEmpty(newValue) && string.IsNullOrEmpty(oldValue)) 
     { 
      // Get current binding 
      var originalBinding = BindingOperations.GetBinding(textBox, TextBox.TextProperty); 
      if (originalBinding != null) 
      { 
       // Update target for current binding 
       UpdateTextBindingSource(textBox); 

       // Create new binding 
       var newBinding = CloneBinding(originalBinding); 
       newBinding.StringFormat = newValue; 

       // Assign new binding 
       BindingOperations.SetBinding(textBox, TextBox.TextProperty, newBinding); 

       // Store original binding 
       textBox.SetValue(OriginalBindingPropertyKey, originalBinding); 
      } 
     } 
    } 

    private static void UpdateTextBindingSource(TextBox textBox) 
    { 
     var expr = textBox.GetBindingExpression(TextBox.TextProperty); 
     if (expr != null && 
      expr.ParentBinding != null && 
      (expr.ParentBinding.Mode == BindingMode.Default // Text binds two-way by default 
      || expr.ParentBinding.Mode == BindingMode.TwoWay 
      || expr.ParentBinding.Mode == BindingMode.OneWayToSource)) 
     { 
      expr.UpdateSource(); 
     } 
    } 

    private static Binding CloneBinding(Binding original) 
    { 
     var copy = new Binding 
         { 
          Path = original.Path, 
          XPath = original.XPath, 
          Mode = original.Mode, 
          Converter = original.Converter, 
          ConverterCulture = original.ConverterCulture, 
          ConverterParameter = original.ConverterParameter, 
          FallbackValue = original.FallbackValue, 
          TargetNullValue = original.TargetNullValue, 
          NotifyOnSourceUpdated = original.NotifyOnSourceUpdated, 
          NotifyOnTargetUpdated = original.NotifyOnTargetUpdated, 
          NotifyOnValidationError = original.NotifyOnValidationError, 
          UpdateSourceExceptionFilter = original.UpdateSourceExceptionFilter, 
          UpdateSourceTrigger = original.UpdateSourceTrigger, 
          ValidatesOnDataErrors = original.ValidatesOnDataErrors, 
          ValidatesOnExceptions = original.ValidatesOnExceptions, 
          BindingGroupName = original.BindingGroupName, 
          BindsDirectlyToSource = original.BindsDirectlyToSource, 
          AsyncState = original.AsyncState, 
          IsAsync = original.IsAsync, 
          StringFormat = original.StringFormat 
         }; 

     if (original.Source != null) 
      copy.Source = original.Source; 
     if (original.RelativeSource != null) 
      copy.RelativeSource = original.RelativeSource; 
     if (original.ElementName != null) 
      copy.ElementName = original.ElementName; 

     foreach (var rule in original.ValidationRules) 
     { 
      copy.ValidationRules.Add(rule); 
     } 
     return copy; 
    } 

    #endregion 
} 

用法:

<TextBox x:Name="ContractAmountTextBox" 
     Text="{Binding Path=ContractAmount, UpdateSourceTrigger=LostFocus, StringFormat=c}"> 
    <TextBox.Style> 
     <Style TargetType="{x:Type TextBox}">          
      <Style.Triggers> 
       <Trigger Property="IsKeyboardFocused" Value="True"> 
        <Setter Property="local:TextBoxBehavior.StringFormat" Value="N"/> 
       </Trigger> 
      </Style.Triggers> 
     </Style> 
    </TextBox.Style> 
</TextBox> 

利用這一點,你也可以重複使用風格不同的文本框

+0

感謝托馬斯,這是相當整潔。我有兩個問題。在您的樣式的DataTrigger綁定中,您可以引用ElementName。如果我想在資源字典中定義樣式並將其重用於多個文本框,這怎麼可以概括?我的第二個問題是我擔心手動替換綁定導致的無法預料的副作用。是否會發生綁定模型在未來的.NET版本中擴展,我們不再複製所有參數? – 2011-04-19 08:06:01

+0

關於ElementName,實際上我只是複製了你的代碼;)。沒有必要使用ElementName,並且可以使用普通的Trigger而不是DataTrigger。我會更新我的答案。 – 2011-04-19 08:18:37

+0

是的,它可能不適用於未來版本的.NET ... – 2011-04-19 08:19:05