WPF中如何调整ItemsControl内UserControl的位置?绑定正常却固定在(0,0)
问题描述
我用ItemsControl想在指定位置放置UserControl,控件已经成功添加到ItemsControl里,绑定也正常,但它始终固定在(0,0)位置,我哪里漏了?
UserControl后台代码
using System; using System.Collections.Generic; using System.ComponentModel; using System.Runtime.CompilerServices; using System.Windows; namespace CADViewer.User_Controls { /// <summary> /// Interaction logic for SignificantPointControl.xaml /// </summary> [Serializable] public partial class SignificantPointControl : System.Windows.Controls.UserControl, INotifyPropertyChanged { private Point dxfCenter; private Point mapPageCanvasCenter; private string color = "Red"; public Double Size { get; set; } = 2; public Double Left { get; set; } public Double Top { get; set; } public Thickness Margin { get; set; } public String Color { get { return color; } set { color = value; OnPropertyChanged(); } } public Point DxfCenter { get { return dxfCenter; } set { dxfCenter = value; OnPropertyChanged(); } } public Point MapPageCanvasCenter { get { return mapPageCanvasCenter; } set { mapPageCanvasCenter = value; OnPropertyChanged(); } } public SignificantPointControl() { DataContext = this; InitializeComponent(); //DxfCenter = dxfCenter; } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string name = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } public void GetDxfCenter(Point dxfCenter) { DxfCenter = dxfCenter; } public void GetMapPageCanvasCenter(List<double> extents, double canvasWidth, double canvasHeight) { MapPageCanvasCenter = DXFToCanvas.DXFCoordToCanvas(DxfCenter, extents, canvasWidth, canvasHeight); Left = mapPageCanvasCenter.X - (Size / 2); Top = mapPageCanvasCenter.Y - (Size / 2); Margin = new Thickness(Left, Top, 0, 0); } } }
UserControl的XAML代码
<UserControl x:Class="CADViewer.User_Controls.SignificantPointControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:CADViewer.User_Controls" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Canvas x:Name="PointCanvas" Height="6" Width="6" Background="Transparent" Canvas.Left="{Binding MapPageCanvasCenter.X}" Canvas.Top="{Binding MapPageCanvasCenter.Y}"> <Ellipse Width="{Binding Size}" Height="{Binding Size}" Stroke="{Binding Color}" Canvas.Left="0" Canvas.Top="0"/> </Canvas> </UserControl>
ItemsControl代码
<ItemsControl x:Name="MapPageItemsControl" Width="900" Height="500" MouseLeftButtonDown="MapPageItemsControl_MouseLeftButtonDown" > <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas Background="Transparent" Width="900" Height="500"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.Resources> <DataTemplate DataType="{x:Type uc:SignificantPointControl}"> <uc:SignificantPointControl.RenderTransform> <TranslateTransform X="{Binding Left}" Y="{Binding Top}"/> </uc:SignificantPointControl.RenderTransform> </uc:SignificantPointControl> </DataTemplate> </ItemsControl.Resources> <ItemsControl.RenderTransform> <MatrixTransform x:Name="transform"/> </ItemsControl.RenderTransform> </ItemsControl>
问题原因与解决方法
核心问题
- DataTemplate误用:直接把
SignificantPointControl作为模板目标类型并在模板中实例化,会导致WPF创建两个实例,绑定关系混乱。 - Canvas定位逻辑错误:ItemsControl的ItemsPanel是Canvas时,必须通过
Canvas.Left/Canvas.Top附加属性定位子项,RenderTransform仅做视觉偏移,不改变布局位置;Margin也不是Canvas的标准定位方式。 - UserControl内部绑定无效:UserControl内部的
PointCanvas设置Canvas.Left只会让内部元素偏移,不会影响UserControl在ItemsControl的Canvas中的位置。
修正步骤
1. 分离视图与数据(定义ViewModel)
不要用UI控件作为数据源,创建独立的ViewModel存储点位数据:
public class SignificantPointViewModel : INotifyPropertyChanged { private Point dxfCenter; private Point mapPageCanvasCenter; private string color = "Red"; private double size = 2; private double left; private double top; public double Size { get => size; set { size = value; OnPropertyChanged(); } } public double Left { get => left; set { left = value; OnPropertyChanged(); } } public double Top { get => top; set { top = value; OnPropertyChanged(); } } public string Color { get => color; set { color = value; OnPropertyChanged(); } } public Point DxfCenter { get => dxfCenter; set { dxfCenter = value; OnPropertyChanged(); } } public Point MapPageCanvasCenter { get => mapPageCanvasCenter; set { mapPageCanvasCenter = value; OnPropertyChanged(); } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string name = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); } public void GetMapPageCanvasCenter(List<double> extents, double canvasWidth, double canvasHeight) { MapPageCanvasCenter = DXFToCanvas.DXFCoordToCanvas(DxfCenter, extents, canvasWidth, canvasHeight); Left = MapPageCanvasCenter.X - (Size / 2); Top = MapPageCanvasCenter.Y - (Size / 2); } }
2. 修改UserControl
移除构造函数中DataContext = this;的代码,让DataContext由外部传入:
<UserControl x:Class="CADViewer.User_Controls.SignificantPointControl" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:local="clr-namespace:CADViewer.User_Controls" mc:Ignorable="d" d:DesignHeight="450" d:DesignWidth="800"> <Canvas x:Name="PointCanvas" Height="6" Width="6" Background="Transparent"> <Ellipse Width="{Binding Size}" Height="{Binding Size}" Stroke="{Binding Color}" Canvas.Left="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=ActualWidth/2 - Size/2}" Canvas.Top="{Binding RelativeSource={RelativeSource AncestorType=UserControl}, Path=ActualHeight/2 - Size/2}"/> </Canvas> </UserControl>
3. 修正ItemsControl的定位逻辑
通过ItemContainerStyle给Canvas的子元素(ContentPresenter)设置Canvas.Left/Canvas.Top,并正确绑定ViewModel:
<ItemsControl x:Name="MapPageItemsControl" Width="900" Height="500" MouseLeftButtonDown="MapPageItemsControl_MouseLeftButtonDown"> <ItemsControl.ItemsPanel> <ItemsPanelTemplate> <Canvas Background="Transparent" Width="900" Height="500"/> </ItemsPanelTemplate> </ItemsControl.ItemsPanel> <ItemsControl.ItemContainerStyle> <Style TargetType="ContentPresenter"> <Setter Property="Canvas.Left" Value="{Binding Left}"/> <Setter Property="Canvas.Top" Value="{Binding Top}"/> </Style> </ItemsControl.ItemContainerStyle> <ItemsControl.Resources> <DataTemplate DataType="{x:Type local:SignificantPointViewModel}"> <uc:SignificantPointControl/> </DataTemplate> </ItemsControl.Resources> <ItemsControl.RenderTransform> <MatrixTransform x:Name="transform"/> </ItemsControl.RenderTransform> </ItemsControl>
4. 添加数据到ItemsControl
创建ViewModel实例并添加到ItemsControl:
var pointVm = new SignificantPointViewModel(); pointVm.GetDxfCenter(new Point(100, 200)); pointVm.GetMapPageCanvasCenter(extents, 900, 500); MapPageItemsControl.Items.Add(pointVm);
内容的提问来源于stack exchange,提问作者user21293279
相关产品推荐
相关产品推荐

