Jas*_*ams 10 wpf binding dynamic string-formatting
我有一个基于几个DataTemplate元素生成的表单.其中一个DataTemplate元素从类中创建一个TextBox,如下所示:
public class MyTextBoxClass
{
public object Value { get;set;}
//other properties left out for brevity's sake
public string FormatString { get;set;}
}
Run Code Online (Sandbox Code Playgroud)
我需要一种方法将FormatString属性中的值"绑定"到绑定的"StringFormat"属性.到目前为止,我有:
<DataTemplate DataType="{x:Type vm:MyTextBoxClass}">
<TextBox Text="{Binding Path=Value, StringFormat={Binding Path=FormatString}" />
</DataTemplate>
Run Code Online (Sandbox Code Playgroud)
但是,由于StringFormat不是依赖项属性,因此无法绑定它.
我的下一个想法是创建一个值转换器并在ConverterParameter上传递FormatString属性的值,但我遇到了同样的问题 - ConverterParameter不是DependencyProperty.
所以,现在我转向你,所以.如何动态设置绑定的StringFormat; 更具体地说,在TextBox上?
我更愿意让XAML为我工作,这样我就可以避免使用代码隐藏.我正在使用MVVM模式,并希望尽可能不模糊视图模型和视图之间的界限.
谢谢!
小智 6
此代码(灵感来自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>
Run Code Online (Sandbox Code Playgroud)
如果源属性可以为 null,请注意重复的 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;
}
}
Run Code Online (Sandbox Code Playgroud)
这是Andrew Olson的解决方案,它使用附加属性,因此可以在各种情况下使用。
像这样使用:
<TextBlock
local:StringFormatHelper.Format="{Binding FormatString}"
local:StringFormatHelper.Value="{Binding Value}"
Text="{Binding (local:StringFormatHelper.FormattedValue)}"
/>
Run Code Online (Sandbox Code Playgroud)
所需的助手:(来源要点)
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());
}
}
}
Run Code Online (Sandbox Code Playgroud)
一种方法可能是创建一个继承的类TextBox,并在该类中创建您自己的依赖属性,该属性在StringFormat设置时委托给您。因此,TextBox您将使用继承的文本框并在绑定中设置您自己的依赖属性,而不是在 XAML 中使用。