WPF DataBinding с простой арифметической операцией?
Я хочу добавить постоянное значение на входящее связанное целое число. На самом деле у меня есть несколько мест, где я хочу привязать одно и то же значение источника, но добавить разные константы. Таким образом, идеальным решением было бы что-то вроде этого...
<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}"/>
(ПРИМЕЧАНИЕ. Это пример, демонстрирующий идею, мой реальный сценарий привязки не связан со свойством canvas элемента TextBox. Но это показывает идею более четко)
На данный момент единственное решение, которое я могу придумать, - это выставить множество различных свойств источника, каждое из которых добавляет разные константы к одному и тому же внутреннему значению. Так что я мог бы сделать что-то вроде этого...
<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 есть идеи?
4 ответа
Я использую MathConverter
что я создал для выполнения всех простых арифметических операций. Код для конвертера здесь, и его можно использовать так:
<TextBox Canvas.Top="{Binding SomeValue,
Converter={StaticResource MathConverter},
ConverterParameter=@VALUE+5}" />
Вы даже можете использовать его с более сложными арифметическими операциями, такими как
Width="{Binding ElementName=RootWindow, Path=ActualWidth,
Converter={StaticResource MathConverter},
ConverterParameter=((@VALUE-200)*.3)}"
Использование преобразователя значений является хорошим решением проблемы, поскольку позволяет изменять исходное значение, поскольку оно привязано к пользовательскому интерфейсу.
Я использовал следующее в нескольких местах.
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}"
/>
Я бы использовал конвертер с несколькими привязками, например, в следующем примере:
Код 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>
Я никогда не использовал WPF, но у меня есть возможное решение.
Может ли ваш обязательный путь к карте? Если это так, он должен иметь возможность принимать аргумент (ключ). Вам нужно создать класс, который реализует интерфейс Map, но на самом деле просто возвращает базовое значение, которое вы инициализировали "Map", добавив ключ.
public Integer get( Integer key ) { return baseInt + key; } // or some such
Без какой-либо возможности передать число из тега, я не понимаю, как вы получите его, чтобы возвращать разные дельты от исходного значения.