WPF 绑定和动态分配 StringFormat 属性
Posted
技术标签:
【中文标题】WPF 绑定和动态分配 StringFormat 属性【英文标题】:WPF Binding and Dynamically Assigning StringFormat Property 【发布时间】:2011-03-07 03:45:53 【问题描述】:我有一个基于多个 DataTemplate 元素生成的表单。其中一个 DataTemplate 元素从一个类中创建了一个 TextBox,如下所示:
public class MyTextBoxClass
public object Value get;set;
//other properties left out for brevity's sake
public string FormatString get;set;
我需要一种方法将 FormatString 属性中的值“绑定”到绑定的“StringFormat”属性。到目前为止,我有:
<DataTemplate DataType="x:Type vm:MyTextBoxClass">
<TextBox Text="Binding Path=Value, StringFormat=Binding Path=FormatString" />
</DataTemplate>
但是,由于 StringFormat 不是依赖属性,我无法绑定到它。
我的下一个想法是创建一个值转换器并将 FormatString 属性的值传递给 ConverterParameter,但我遇到了同样的问题 - ConverterParameter 不是 DependencyProperty。
所以,现在我转向你,所以。如何动态设置绑定的 StringFormat;更具体地说,在 TextBox 上?
我更愿意让 XAML 为我完成这项工作,这样我就可以避免使用代码隐藏。我正在使用 MVVM 模式,并希望尽可能保持视图模型和视图之间的界限不模糊。
谢谢!
【问题讨论】:
【参考方案1】:这是来自Andrew Olson 的解决方案,它使用附加属性,因此可以在各种情况下使用。
这样使用:
<TextBlock
local:StringFormatHelper.Format="Binding FormatString"
local:StringFormatHelper.Value="Binding Value"
Text="Binding (local:StringFormatHelper.FormattedValue)"
/>
所需的助手:(source Gist)
public static class StringFormatHelper
#region Value
public static DependencyProperty ValueProperty = DependencyProperty.RegisterAttached(
"Value", typeof(object), typeof(StringFormatHelper), new System.Windows.PropertyMetadata(null, OnValueChanged));
private static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
RefreshFormattedValue(obj);
public static object GetValue(DependencyObject obj)
return obj.GetValue(ValueProperty);
public static void SetValue(DependencyObject obj, object newValue)
obj.SetValue(ValueProperty, newValue);
#endregion
#region Format
public static DependencyProperty FormatProperty = DependencyProperty.RegisterAttached(
"Format", typeof(string), typeof(StringFormatHelper), new System.Windows.PropertyMetadata(null, OnFormatChanged));
private static void OnFormatChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
RefreshFormattedValue(obj);
public static string GetFormat(DependencyObject obj)
return (string)obj.GetValue(FormatProperty);
public static void SetFormat(DependencyObject obj, string newFormat)
obj.SetValue(FormatProperty, newFormat);
#endregion
#region FormattedValue
public static DependencyProperty FormattedValueProperty = DependencyProperty.RegisterAttached(
"FormattedValue", typeof(string), typeof(StringFormatHelper), new System.Windows.PropertyMetadata(null));
public static string GetFormattedValue(DependencyObject obj)
return (string)obj.GetValue(FormattedValueProperty);
public static void SetFormattedValue(DependencyObject obj, string newFormattedValue)
obj.SetValue(FormattedValueProperty, newFormattedValue);
#endregion
private static void RefreshFormattedValue(DependencyObject obj)
var value = GetValue(obj);
var format = GetFormat(obj);
if (format != null)
if (!format.StartsWith("0:"))
format = String.Format("0:0", format);
SetFormattedValue(obj, String.Format(format, value));
else
SetFormattedValue(obj, value == null ? String.Empty : value.ToString());
【讨论】:
我需要添加“Path”和“RelativeSource”才能使其工作。 ***.com/a/5832247/417939 请注意,这只会让您使用 N3 或 X2 等十进制和十六进制修饰符进行格式化。如果您想添加额外的文本,例如“平均温度为 0:N1 摄氏度”,您必须删除所有以if (!format.StartsWith("0:"))
开头的 if 语句
我是否认为使用它不允许编辑文本?【参考方案2】:
此代码(受DefaultValueConverter.cs @ referencesource.microsoft.com 启发)适用于与 TextBox 或类似控件的双向绑定,只要 FormatString 使源属性的 ToString() 版本处于可转换回的状态。 (即像 "#,0.00" 这样的格式是可以的,因为 "1,234.56" 可以被解析回来,但是 FormatString="Some Prefix Text #,0.00" 将转换为 "Some Prefix Text 1,234.56" 不能被解析回来。)
XAML:
<TextBox>
<TextBox.Text>
<MultiBinding Converter="StaticResource ToStringFormatConverter"
ValidatesOnDataErrors="True" NotifyOnValidationError="True" TargetNullValue="">
<Binding Path="Property" TargetNullValue="" />
<Binding Path="PropertyStringFormat" Mode="OneWay" />
</MultiBinding>
</TextBox.Text>
</TextBox>
如果源属性可以为空,请注意重复的 TargetNullValue。
C#:
/// <summary>
/// Allow a binding where the StringFormat is also bound to a property (and can vary).
/// </summary>
public class ToStringFormatConverter : IMultiValueConverter
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
if (values.Length == 1)
return System.Convert.ChangeType(values[0], targetType, culture);
if (values.Length >= 2 && values[0] is IFormattable)
return (values[0] as IFormattable).ToString((string)values[1], culture);
return null;
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
var targetType = targetTypes[0];
var nullableUnderlyingType = Nullable.GetUnderlyingType(targetType);
if (nullableUnderlyingType != null)
if (value == null)
return new[] (object)null ;
targetType = nullableUnderlyingType;
try
object parsedValue = ToStringFormatConverter.TryParse(value, targetType, culture);
return parsedValue != DependencyProperty.UnsetValue
? new[] parsedValue
: new[] System.Convert.ChangeType(value, targetType, culture) ;
catch
return null;
// Some types have Parse methods that are more successful than their type converters at converting strings
private static object TryParse(object value, Type targetType, CultureInfo culture)
object result = DependencyProperty.UnsetValue;
string stringValue = value as string;
if (stringValue != null)
try
MethodInfo mi;
if (culture != null
&& (mi = targetType.GetMethod("Parse",
BindingFlags.Public | BindingFlags.Static, null,
new[] typeof(string), typeof(NumberStyles), typeof(IFormatProvider) , null))
!= null)
result = mi.Invoke(null, new object[] stringValue, NumberStyles.Any, culture );
else if (culture != null
&& (mi = targetType.GetMethod("Parse",
BindingFlags.Public | BindingFlags.Static, null,
new[] typeof(string), typeof(IFormatProvider) , null))
!= null)
result = mi.Invoke(null, new object[] stringValue, culture );
else if ((mi = targetType.GetMethod("Parse",
BindingFlags.Public | BindingFlags.Static, null,
new[] typeof(string) , null))
!= null)
result = mi.Invoke(null, new object[] stringValue );
catch (TargetInvocationException)
return result;
【讨论】:
我喜欢它使用开箱即用的 WPF,并且解析方法可以很容易地适应您的具体情况。【参考方案3】:一种方法可能是创建一个继承TextBox
的类,并在该类中创建您自己的依赖属性,该属性在设置时委托给StringFormat
。因此,您将使用继承的文本框并在绑定中设置自己的依赖项属性,而不是在 XAML 中使用 TextBox
。
【讨论】:
这是个好建议。我得调查一下。我有点希望有一个不涉及自定义控件的解决方案,但我当然愿意接受。我会在稍作研究后回来查看。 我正在尝试做同样的事情,但我不确定如何设置附加属性来处理这个问题。我发布了一个新问题:***.com/q/24119097/65461【参考方案4】:只需将文本框绑定到 MyTextBoxClass 的实例而不是 MyTextBoxClass.Value 并使用 valueconverter 从 value 和 formatstring 创建一个字符串。
另一个解决方案是使用一个多值转换器,它可以同时绑定到 Value 和 FormatString。
第一个解决方案不支持更改属性,也就是说,如果值或格式字符串发生更改,则不会像使用多值转换器并直接绑定到属性时那样调用值转换器。
【讨论】:
绑定到 MyTextBoxClass 实例是我尝试过的,但是 ValueConverter 中的 ConvertBack 方法将成为一个问题,因为我在 TextBox 上没有很多属性目的。所以,我会从 TextBox 返回一个不完整的对象。我将研究多值转换器。但是,FormatString 是不可绑定的,因为它是一个依赖属性,所以我不确定它是否会起作用。 这应该如何工作?当使用数据绑定更新 TextBox 时,使用 FormatString 格式化文本。当用户更新文本框时,他可以输入任何可能与 FormatString 格式不一致的文本。那样可以么?您确定不想使用蒙面文本框吗?此外,FormatString 与任何其他公共属性一样可绑定。 “FormatString 与任何其他公共属性一样可绑定”解释为什么您会收到一条错误消息,指出“无法在 'Binding' 类型的 'StringFormat' 属性上设置 'Binding'。只能在 DependencyObject 的 DependencyProperty 上设置“绑定”。”【参考方案5】:可以创建一个附加行为,该行为可以将绑定替换为指定了 FormatString 的绑定。如果 FormatString 依赖属性,则绑定将再次更新。如果绑定已更新,则 FormatString 将重新应用于该绑定。
我认为您必须处理的仅有的两件棘手的事情。一个问题是您是否要为 FormatString 和存在绑定的 TargetProperty 创建两个相互协调的附加属性,应该应用 FormatString(例如 TextBox.Text),或者您可以假设您处理的是哪个属性取决于目标控件类型。另一个问题可能是,复制现有绑定并稍微修改它可能并非易事,因为那里存在各种类型的绑定,其中可能还包括自定义绑定。
重要的是要考虑,尽管所有这些都只能在从数据到控件的方向上实现格式化。据我所知,使用 MultiBinding 和自定义 MultiValueConverter 之类的东西来消耗原始值和 FormatString 并产生所需的输出仍然遇到同样的问题,主要是因为 ConvertBack 方法只给出了输出字符串,你会期望从中破译 FormatString 和原始值,这在那时几乎总是不可能的。
适用于双向格式化和取消格式化的其余解决方案如下:
编写一个扩展 TextBox 的自定义控件,该控件具有 Jakob Christensen 建议的所需格式行为。 编写一个派生自 DependencyObject 或 FrameworkElement 并具有 FormatString DependencyProperty 的自定义值转换器。如果您想走 DependencyObject 路线,我相信您可以使用 OneWayToSource 绑定和“虚拟分支”技术将值推送到 FormatString 属性中。另一种更简单的方法可能是从 FrameworkElement 继承并将值转换器与其他控件一起放入可视化树中,以便在 ElementName 需要时绑定到它。 使用与我在本文顶部提到的类似的附加行为,但不是设置 FormatString,而是有两个附加属性,一个用于自定义值转换器,一个用于将传递给值转换器的参数.然后,您无需修改原始绑定以添加 FormatString,而是将转换器和转换器参数添加到绑定中。就我个人而言,我认为这个选项会产生最易读和最直观的结果,因为附加的行为往往更干净,但仍然足够灵活,可以在除 TextBox 之外的各种情况下使用。【讨论】:
以上是关于WPF 绑定和动态分配 StringFormat 属性的主要内容,如果未能解决你的问题,请参考以下文章
wpf Content数据绑定StringFormat起作用的原理和解决