【问题标题】:Modifying the Parameters of a TextBox's Text Binding through the use of a Style通过使用样式修改 TextBox 的文本绑定的参数
【发布时间】:2011-04-08 11:34:50
【问题描述】:

我想要一个以货币格式显示数字的TextBox(通过在绑定上设置StringFormat=c)。选择TextBox @ 987654324时(IsKeyboardFocused==true)时,我希望格式化消失,直到焦点在TextBox丢失。

我找到了一种方法,代码粘贴在下面。我的问题是绑定是在Style 中指定的——这意味着我必须为我想要这样做的每个TextBox 重新键入样式。理想情况下,我想将样式放在中心位置,并为每个 TextBox 重用它,每个都有不同的绑定目标。

有没有办法让我使用Style 在现有绑定上设置一个参数,比如Text.Binding.StringFormat=""? (而不是将 Text 的整个值设置为新定义的 Binding)

我们也将不胜感激提出其他建议。

代码(这行得通,只是不方便):

<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>

【问题讨论】:

    标签: wpf xaml data-binding styles string-formatting


    【解决方案1】:

    附加属性是可行的,但这意味着你必须完全替换绑定,然后再放回去。

    这是一个快速而肮脏的实现:

    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>
    

    使用它,您还可以为不同的TextBox重用样式

    【讨论】:

    • 谢谢托马斯,这很整洁。我有两个问题。在您的样式的 DataTrigger 绑定中,您指的是 ElementName。如果我想在资源字典中定义样式并将其重用于多个文本框,如何概括?我的第二个问题是我担心手动更换绑定会导致无法预料的副作用。会不会在未来的 .NET 版本中扩展了 Binding 模型,我们不再复制所有参数?
    • 关于 ElementName,实际上我只是复制了您的代码;)。不需要 ElementName,您可以使用普通 Trigger 代替 DataTrigger。我会更新我的答案。
    猜你喜欢
    • 2016-04-10
    • 1970-01-01
    • 2021-07-10
    • 1970-01-01
    • 2014-08-14
    • 1970-01-01
    • 2013-07-15
    • 2011-02-08
    • 1970-01-01
    相关资源
    最近更新 更多