如何对XAML绑定值进行计算:反转、乘以、减去或加上?

21

首先,这个问题是修辞性的,我有答案!我从这里找到了很多帮助,所以我想回馈一个不错的技巧。

假设你有一个想要绑定的值,但它可能在某些方面或某种程度上有误。

  • 我曾遇到这样一种情况,我想要将一个值绑定到另一个值,但当这个值为1时,我需要0,反之亦然。
  • 有一次我想将一个元素的宽度绑定到父元素的宽度减去68像素。

1
一个非常好的通用转换器的想法。我会记住这个想法。我需要在绑定值上进行的大部分数学计算都可以使用这个转换器完成。我的经验是将它们称为线性变换函数或一次多项式。我可能会将其命名为LinearTransformConverter。 - Alain
3
你应该将答案作为实际答案发布,而不是包含在问题中。 - H.B.
在法语中,它也被称为“一次函数”... 好吧,在任何地方,我想 :-D - Emmanuel
聪明的做法,但是如果你在使用值转换器而不是视图模型逻辑的情况下,你可能犯了某种错误。当然,这其中肯定有很多例外(例如我相信存在一些可信的用例,不需要视图模型,尽管我没有见过)。但是我的感觉是,总的来说,值转换器是将代码注入XAML的一种hack,这使得它成为了一个在hack中注入代码的hack。 - Robert Rossney
1
我意识到已经过去了8年,现在可能更清楚了,但我还是要说:@RobertRossney - 我强烈不同意。原则“不要将代码注入XAML”更正确的陈述是“不要混合业务/建模逻辑和表示逻辑”。涉及表达式甚至约束条件的GUI系统是100%有效和适当的-只要计算严格用于显示目的。 “有限”的情况是,人们最终在他们的视图模型中进行这些仅用于显示的计算,因为那样更容易,这就是错误的。 - ToolmakerSteve
请参见 https://dev59.com/Smox5IYBdhLWcg3w8IxJ#30708634,其中链接了一些相关的NuGet包。 - ToolmakerSteve
2个回答

17
输入FirstDegreeFunctionConverter
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;

namespace GenericWPF
{
    /// <summary>
    /// Will return a*value + b
    /// </summary>
    public class FirstDegreeFunctionConverter : IValueConverter
    {
        public double A { get; set; }
        public double B { get; set; }

        #region IValueConverter Members

        public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
        {
            double a = GetDoubleValue( parameter, A );

            double x = GetDoubleValue( value, 0.0 );

            return ( a * x ) + B;
        }

        public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
        {
            double a = GetDoubleValue( parameter, A );

            double y = GetDoubleValue( value, 0.0 );

            return ( y - B ) / a;
        }

        #endregion


        private double GetDoubleValue( object parameter, double defaultValue )
        {
            double a;
            if( parameter != null )
                try
                {
                    a = System.Convert.ToDouble( parameter );
                }
                catch
                {
                    a = defaultValue;
                }
            else
                a = defaultValue;
            return a;
        }
    }
}

如何使用它?
在资源部分为每个用途创建一个资源。
<GenericWPF:FirstDegreeFunctionConverter x:Key="ReverseOne"
                            A="-1"
                            B="1" />

<Border Opacity="{Binding Path=Opacity
    , ElementName=daOtherField
    , Converter={StaticResource ReverseOne}}" />

<GenericWPF:FirstDegreeFunctionConverter x:Key="ListboxItemWidthToErrorWidth"
     A="1"
     B="-68" />

<TextBox MaxWidth="{Binding Path=ActualWidth
   , Converter={StaticResource ListboxItemWidthToErrorWidth}
   , RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" />

这个名字来自于函数 y = ax + b(在挪威被称为“一次函数”),当然也可以升级为二次函数 y= ax^2 + bx + c,但我还没有找到它的用途。
我曾经遇到过一个情况,我想根据宽度创建列。每增加200像素的宽度,我希望容器显示另一列。那时候我硬编码了一个转换器,但我应该使用一个 y=(a/x) + b 的转换器。
现在,我应该给这个转换器取什么名字,以便每个人都能理解它呢?由于我是挪威人,我使用了我们在学校学到的表达方式,直接翻译过来。如果你有建议或意见,请告诉我。 你认为有哪些改进或提高的地方也会很受欢迎...
也许使用"LinearTransformConverter"这个名称会更好地传达转换器的功能,我会考虑一下。 还有其他建议吗? Tor

万一有人遇到XAML找不到/接受类的问题:确保以下两点:1:它有自己的命名空间(至少与主窗口/应用程序不同),2:通过在主窗口/页面等属性中设置xmlns别名来引用它。对于命名空间"ValueConverters",我在我的窗口属性中添加了'xmlns:converters="clr-namespace:ValueConverters"',然后通过使用'<converters:FirstDegreeFunctionConverter x:Key="MultiplyByTwoThirds" A="0.6" B="0" />'来引用它,例如。我不知道为什么它对不同的命名空间那么挑剔,但没有这个就行不通。 - Ben Philipp
另外:你可能需要重新启动以消除一些错误 - 也许是因为我在使用链条中的某个旧版本,谁知道呢 - Ben Philipp

7
更好的选择是使用 PolynomialConverter,以下是单向转换版本:
public class PolynomialConverter : IValueConverter
{
    public DoubleCollection Coefficients { get; set; }

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double x = (double)value;
        double output = 0;
        for (int i = Coefficients.Count - 1; i >= 0 ; i--)
            output += Coefficients[i] * Math.Pow(x, (Coefficients.Count - 1) - i);

        return output;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        //This one is a bit tricky, if anyone feels like implementing this...
        throw new NotSupportedException();
    }
}

例子:

<!-- x^2 -->
<vc:PolynomialConverter Coefficients="1,0,0"/>
<!-- x + 5 -->
<vc:PolynomialConverter Coefficients="1,5"/>
<!-- 2x + 4 -->
<vc:PolynomialConverter Coefficients="2,4"/>

或者,可以使用ConverterParameter来避免在转换器本身中设置系数。
DoubleCollection coefficients = DoubleCollection.Parse((string)parameter);
//...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接