WPF中DataTemplate绑定Crosshair背景失效问题排查
问题分析与解决方案
问题描述
我为所有Crosshair类型创建了DataTemplate并应用到ListBox控件,模板里包含十字线线条和自定义的DragControl,后续打算用这个自定义控件拖动十字线。我把DragControl的左上角定位在十字线中心位置,同时尝试绑定十字线的Background颜色,但这个绑定没生效,请问这个实现方式有什么问题?
相关代码
DragControl.xaml
<UserControl x:Class="WPFCrosshairSandbox.Views.DragControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Width="100" Height="100" Background="Green"> <Grid/> </UserControl>
Crosshair.cs
public class Crosshair : BindableBase { private string _name; public string Name { get { return _name; } set { SetProperty(ref _name, value); } } private Brush _color; public Brush Color { get { return _color; } set { SetProperty(ref _color, value); } } private double _thickness; public double Thickness { get { return _thickness; } set { SetProperty(ref _thickness, value); } } private int _x; public int X { get { return _x; } set { SetProperty(ref _x, value); } } private int _y; public int Y { get { return _y; } set { SetProperty(ref _y, value); } } }
MainWindow.xaml
<Window.Resources> <DataTemplate DataType="{x:Type local:Crosshair}"> <Canvas> <Line Stroke="{Binding Color}" StrokeThickness="{Binding Thickness}" X1="{Binding X}" X2="{Binding X}" Y1="0" Y2="{Binding Path=ActualHeight, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" /> <Line Stroke="Transparent" StrokeThickness="20" X1="{Binding X}" X2="{Binding X}" Y1="0" Y2="{Binding Path=ActualHeight, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" /> <Line Stroke="{Binding Color}" StrokeThickness="{Binding Thickness}" X1="0" X2="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" Y1="{Binding Y}" Y2="{Binding Y}" /> <Line Stroke="Transparent" StrokeThickness="20" X1="0" X2="{Binding Path=ActualWidth, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}}" Y1="{Binding Y}" Y2="{Binding Y}" /> <views:DragControl x:Name="xDragControl" Visibility="Collapsed" Canvas.Left="{Binding X}" Canvas.Top="{Binding Y}" Background="{Binding Color}"/> </Canvas> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Path=IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="True"> <Setter TargetName="xDragControl" Property="Visibility" Value="Visible"/> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> <Style TargetType="ListBox" x:Key="MarkerListBoxStyle"> <Setter Property="ItemsPanel"> <Setter.Value> <ItemsPanelTemplate> <Canvas/> </ItemsPanelTemplate> </Setter.Value> </Setter> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <ItemsPresenter/> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="ListBoxItem" x:Key="MarkerListBoxItemStyle"> <Setter Property="Canvas.Left" Value="0"/> <Setter Property="Canvas.Top" Value="0"/> <Setter Property="Height" Value="{Binding ActualHeight}"/> <Setter Property="Width" Value="{Binding ActualWidth}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <ContentPresenter ContentSource="Content"/> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources> <ScrollViewer VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Visible"> <Grid> <Image x:Name="TargetBackgroundImage" VerticalAlignment="Top" HorizontalAlignment="Left" Source="/Views/image.jpg"/> <ListBox VerticalAlignment="Top" HorizontalAlignment="Left" Width="{Binding Path=ActualWidth, ElementName=TargetBackgroundImage}" Height="{Binding Path=ActualHeight, ElementName=TargetBackgroundImage}" ItemsSource="{Binding CrosshairColletion}" SelectedItem="{Binding SelectedCrosshair}" Style="{StaticResource MarkerListBoxStyle}" ItemContainerStyle="{StaticResource MarkerListBoxItemStyle}"/> </Grid> </ScrollViewer>
问题诊断与修复
1. DragControl Background绑定失效
原因:DragControl的XAML中直接硬编码了Background="Green",WPF属性值优先级里,本地设置的优先级高于数据绑定,导致绑定被覆盖无法生效。
修复:移除DragControl标签里的Background="Green",让绑定正常工作:
<UserControl x:Class="WPFCrosshairSandbox.Views.DragControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Width="100" Height="100"> <Grid/> </UserControl>
2. DragControl定位错误
原因:当前把Canvas.Left绑定到X、Canvas.Top绑定到Y,会让DragControl的左上角对齐十字线中心,但控件本身有100x100的尺寸,导致控件整体偏移,视觉上无法居中于十字线。
修复:将控件中心对齐十字线中心,可通过转换器计算偏移量:
<!-- 先在Window.Resources中声明转换器 --> <local:SubtractHalfConverter x:Key="SubtractHalfConverter"/> <!-- 修改DragControl的绑定 --> <views:DragControl x:Name="xDragControl" Visibility="Collapsed" Canvas.Left="{Binding X, Converter={StaticResource SubtractHalfConverter}, ConverterParameter={Binding ActualWidth, ElementName=xDragControl}}" Canvas.Top="{Binding Y, Converter={StaticResource SubtractHalfConverter}, ConverterParameter={Binding ActualHeight, ElementName=xDragControl}}" Background="{Binding Color}"/>
对应的转换器代码:
public class SubtractHalfConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is double center && parameter is double size) { return center - size / 2; } return value; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
3. ListBoxItem的Height/Width绑定无效
原因:MarkerListBoxItemStyle中Height="{Binding ActualHeight}"未指定绑定源,默认会绑定Crosshair对象的ActualHeight属性,但Crosshair没有该属性,导致绑定失败,ListBoxItem尺寸异常。
修复:修改绑定,指定源为父级ListBox的实际尺寸:
<Style TargetType="ListBoxItem" x:Key="MarkerListBoxItemStyle"> <Setter Property="Canvas.Left" Value="0"/> <Setter Property="Canvas.Top" Value="0"/> <Setter Property="Height" Value="{Binding Path=ActualHeight, RelativeSource={RelativeSource AncestorType=ListBox}}"/> <Setter Property="Width" Value="{Binding Path=ActualWidth, RelativeSource={RelativeSource AncestorType=ListBox}}"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <ContentPresenter ContentSource="Content"/> </ControlTemplate> </Setter.Value> </Setter> </Style>
内容的提问来源于stack exchange,提问作者Buruhazard
相关产品推荐
相关产品推荐

