WPF中如何绑定Thickness.Left并实现厚度数值运算?
问题:实现Image Margin.Left与TemplatedParent Padding.Left的负相关绑定
需求与问题
需要对Thickness进行数值运算,让Image的Margin.Left与TemplatedParent的Padding.Left呈相对负值关系(即Margin.Left = -Padding.Left)。尝试用ThicknessDirectionConverter提取Padding.Left的值,再通过ThicknessConverter进行运算,但没能实现预期效果。
现有代码
XAML代码
<Image.Margin> <Binding Path="Padding" RelativeSource="{RelativeSource TemplatedParent}" Converter="{StaticResource ThicknessConverter}"> <Binding.ConverterParameter> <x:Array Type="System:Object"> <Thickness Left="{TemplateBinding Padding,Converter={StaticResource ThicknessDirectionConverter},ConverterParameter='l'}" Top="0" Right="0" Bottom="0"/> <System:String>L</System:String> </x:Array> </Binding.ConverterParameter> </Binding> </Image.Margin>
ThicknessConverter代码
public class ThicknessConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is Thickness thickness && parameter is object[] parameters && parameters.Length == 2 && parameters[0] is Thickness offset && parameters[1] is string operations) { //l,t,r,b for positive //L,T,B,R for negative //use contains if (operations.Contains('l')) thickness.Left += offset.Left; else if (operations.Contains('L')) thickness.Left -= offset.Left; if (operations.Contains('t')) thickness.Top += offset.Top; else if (operations.Contains('T')) thickness.Top -= offset.Top; if (operations.Contains('r')) thickness.Right += offset.Right; else if (operations.Contains('R')) thickness.Right -= offset.Right; if (operations.Contains('b')) thickness.Bottom += offset.Bottom; else if (operations.Contains('B')) thickness.Bottom -= offset.Bottom; return thickness; } return Binding.DoNothing; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
ThicknessDirectionConverter代码
public class ThicknessDirectionConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if(value is Thickness thickness && parameter is string par && par[0] is char direction) { //l,t,r,b switch (direction) { case 'l': double left = (double)thickness.Left; return left; case 't': double left = (double)thickness.Top; return left; case 'r': double left = (double)thickness.Right; return left; case 'b': double left = (double)thickness.Bottom; return left; } } return Binding.DoNothing; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
问题根源
- ConverterParameter不支持绑定:XAML中
Binding.ConverterParameter是静态值,无法通过TemplateBinding动态传递数据,所以Thickness.Left的绑定不会生效,offset.Left始终为0。 - ThicknessDirectionConverter存在语法错误:所有switch分支都重复定义了
double left变量,编译会直接报错。
解决方案
方案1:简化转换器逻辑(推荐)
直接修改ThicknessConverter,让它可以直接对指定方向的Thickness值取负,无需额外的提取转换器:
public class ThicknessConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is Thickness sourceThickness && parameter is string direction) { var result = new Thickness(0); switch (direction.ToLower()) { case "l": result.Left = -sourceThickness.Left; break; case "t": result.Top = -sourceThickness.Top; break; case "r": result.Right = -sourceThickness.Right; break; case "b": result.Bottom = -sourceThickness.Bottom; break; case "all": result = new Thickness(-sourceThickness.Left, -sourceThickness.Top, -sourceThickness.Right, -sourceThickness.Bottom); break; } return result; } return Binding.DoNothing; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
对应的XAML简化为:
<Image.Margin> <Binding Path="Padding" RelativeSource="{RelativeSource TemplatedParent}" Converter="{StaticResource ThicknessConverter}" ConverterParameter="l"/> </Image.Margin>
方案2:修复原转换器结构
如果要保留原有两个转换器的设计,需要用MultiBinding传递动态值,代替不支持绑定的ConverterParameter:
修改后的XAML
<Image.Margin> <MultiBinding Converter="{StaticResource ThicknessConverter}"> <Binding Path="Padding" RelativeSource="{RelativeSource TemplatedParent}"/> <Binding Path="Padding" RelativeSource="{RelativeSource TemplatedParent}" Converter="{StaticResource ThicknessDirectionConverter}" ConverterParameter="l"/> <Binding Source="L"/> </MultiBinding> </Image.Margin>
修改后的ThicknessConverter(改为IMultiValueConverter)
public class ThicknessConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length == 3 && values[0] is Thickness thickness && values[1] is double leftValue && values[2] is string operations) { var offset = new Thickness(leftValue, 0, 0, 0); if (operations.Contains('L')) thickness.Left -= offset.Left; return thickness; } return Binding.DoNothing; } public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
修复ThicknessDirectionConverter的变量错误
public class ThicknessDirectionConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if(value is Thickness thickness && parameter is string par && par[0] is char direction) { switch (direction) { case 'l': return thickness.Left; case 't': return thickness.Top; case 'r': return thickness.Right; case 'b': return thickness.Bottom; } } return Binding.DoNothing; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
内容的提问来源于stack exchange,提问作者Salmy
相关产品推荐
相关产品推荐

