【问题标题】:WPF DataBinding with simple arithmetic operation?具有简单算术运算的 WPF DataBinding?
【发布时间】:2008-09-24 05:19:29
【问题描述】:

我想在传入的绑定整数上添加一个常量值。事实上,我有几个地方想要绑定到相同的源值但添加不同的常量。所以理想的解决方案是这样的......

<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myInt, Constant=5}"/>
<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myInt, Constant=8}"/>
<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myInt, Constant=24}"/>

(注意:这是一个展示想法的示例,我的实际绑定场景不是到TextBox的canvas属性。但这更清楚地展示了这个想法)

目前我能想到的唯一解决方案是公开许多不同的源属性,每个源属性都将不同的常量添加到相同的内部值。所以我可以做这样的事情......

<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myIntPlus5}"/>
<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myIntPlus8}"/>
<TextBox Canvas.Top="{Binding ElementName=mySource, Path=myIntPlus24}"/>

但这很糟糕,因为将来我可能需要继续为新常量添加新属性。此外,如果我需要更改添加的值,我需要更改源对象,这很不合适。

一定有比这更通用的方法吗?有 WPF 专家有什么想法吗?

【问题讨论】:

    标签: wpf data-binding


    【解决方案1】:

    我使用我创建的 MathConverter 来执行所有简单的算术运算。转换器的代码是here,可以这样使用:

    <TextBox Canvas.Top="{Binding SomeValue, 
                 Converter={StaticResource MathConverter},
                 ConverterParameter=@VALUE+5}" />
    

    您甚至可以将它用于更高级的算术运算,例如

    Width="{Binding ElementName=RootWindow, Path=ActualWidth,
                    Converter={StaticResource MathConverter},
                    ConverterParameter=((@VALUE-200)*.3)}"
    

    【讨论】:

    • 不错的转换器,但如果 value 为负数,则会中断。一个快速的解决方法是使用一些其他符号作为减号,而不是“-”,并分别修改 MathConverter 代码中的操作数列表。真正的解决方案当然需要在解析时添加更多逻辑..
    • @MaximZabolotskikh 一个负数就是0 - @VALUE,所以用户可以很容易地输入它。或者作为一个更好的解决方案,当我们期望一个数字标记时,只需检查 - 符号,如果找到它,请正确解析它。我想我实际上可能已经在我保存在 WPF 库中的转换器副本中做到了这一点......
    • 0 - @VALUE 不起作用。更准确地说,我有一个从 0 开始的列表,并且想要显示文本“y 的元素 x”,因此我将索引 (x) 绑定为“@VALUE + 1”。但是空列表将返回 -1 作为索引,因此它变为 -1+1,mathconverter 按操作数拆分,而不是找不到负秒数,诸如此类。所以应该做更多的解析。但基本上我评论的目的只是向将来可能使用此代码的任何人指出一个可能的问题。无论如何,它是一个有用的转换器。
    • 一般警告:我只是花了很长时间才弄清楚这些逗号是必需的
    【解决方案2】:

    我相信您可以使用价值转换器来做到这一点。这是一个blog entry,它解决了将参数传递给 xaml 中的值转换器的问题。 this blog 给出了一些实现值转换器的细节。

    【讨论】:

    • 值转换器可以采用参数这一事实似乎是解决这里问题的好方法。感谢您的意见。
    【解决方案3】:

    使用值转换器是解决问题的好方法,因为它允许您在源值绑定到 UI 时对其进行修改。

    我在几个地方使用了以下内容。

    public class AddValueConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            object result = value;
            int parameterValue;
    
            if (value != null && targetType == typeof(Int32) && 
                int.TryParse((string)parameter, 
                NumberStyles.Integer, culture, out parameterValue))
            {
                result = (int)value + (int)parameterValue;
            }
    
            return result;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    示例

     <Setter Property="Grid.ColumnSpan"
             Value="{Binding 
                       Path=ColumnDefinitions.Count,
                       RelativeSource={RelativeSource AncestorType=Grid},
                       Converter={StaticResource addValueConverter},
                       ConverterParameter=1}"
      />
    

    【讨论】:

      【解决方案4】:

      我从未使用过 WPF,但我有一个可能的解决方案。

      您的绑定路径可以映射到地图吗?如果是这样,那么它应该能够接受一个参数(密钥)。您需要创建一个实现 Map 接口的类,但实际上只返回您初始化“Map”并添加到键的基值。

      public Integer get( Integer key ) { return baseInt + key; }  // or some such
      

      如果没有从标签传递数字的能力,我看不出你将如何让它从原始值返回不同的增量。

      【讨论】:

        【解决方案5】:

        我会使用多重绑定转换器,例如以下示例:

        C#代码:

        namespace Example.Converters
        {
                public class ArithmeticConverter : IMultiValueConverter
                {            
                    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
                    {
                        double result = 0;
        
                        for (int i = 0; i < values.Length; i++)
                        {
                            if (!double.TryParse(values[i]?.ToString(), out var parsedNumber)) continue;
        
                            if (TryGetOperations(parameter, i, out var operation))
                            {
                                result = operation(result, parsedNumber);
                            }
                        }
        
                        return result;
                    }
        
                    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
                    {
                        return new[] { Binding.DoNothing, false };
                    }
        
        
                    private static bool TryGetOperations(object parameter, int operationIndex, out Func<double, double, double> operation)
                    {
                        operation = null;
                        var operations = parameter?.ToString().Split(',');
        
                        if (operations == null || operations.Length == 0) return false;
        
                        if (operations.Length <= operationIndex)
                        {
                            operationIndex = operations.Length - 1;
                        }
        
                        return Operations.TryGetValue(operations[operationIndex]?.ToString(), out operation);
                    }
        
                    public const string Add = "+";
                    public const string Subtract = "-";
                    public const string Multiply = "*";
                    public const string Divide = "/";
        
                    private static IDictionary<string, Func<double, double, double>> Operations = new Dictionary<string, Func<double, double, double>>
                    {
                        { Add, (x, y) => x + y },
                        { Subtract, (x, y) => x - y },
                        { Multiply, (x, y) => x * y },
                        { Divide, (x, y) => x / y }
                    };
                }
        }
        

        XAML 代码:

        <UserControl 
             xmlns:converters="clr-namespace:Example.Converters">
            <UserControl.Resources>
                            <converters:ArithmeticConverter x:Key="ArithmeticConverter" />
            </UserControl.Resources>
        

        ...

        <MultiBinding Converter="{StaticResource ArithmeticConverter}" ConverterParameter="+,-">
                    <Binding Path="NumberToAdd" />
                    <Binding Path="NumberToSubtract" />
        </MultiBinding>
        

        【讨论】:

          猜你喜欢
          • 2019-06-02
          • 1970-01-01
          • 1970-01-01
          • 2016-06-24
          • 1970-01-01
          • 1970-01-01
          • 2017-04-01
          • 1970-01-01
          • 2015-03-31
          相关资源
          最近更新 更多