You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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>

问题原因与解决方法

核心问题

  1. DataTemplate误用:直接把SignificantPointControl作为模板目标类型并在模板中实例化,会导致WPF创建两个实例,绑定关系混乱。
  2. Canvas定位逻辑错误:ItemsControl的ItemsPanel是Canvas时,必须通过Canvas.Left/Canvas.Top附加属性定位子项,RenderTransform仅做视觉偏移,不改变布局位置;Margin也不是Canvas的标准定位方式。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 06:33:09