WPF绑定和动态分配StringFormat属性

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)


oft*_*lit 6

这是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)


Jak*_*sen 2

一种方法可能是创建一个继承的类TextBox,并在该类中创建您自己的依赖属性,该属性在StringFormat设置时委托给您。因此,TextBox您将使用继承的文本框并在绑定中设置您自己的依赖属性,而不是在 XAML 中使用。