Search code examples
c#wpfdata-binding

How can I transform the location of a UserControl set on an Items Control?


I've got an ItemsControl and I'm attempting to set a UserControl in a specific location on this particular ItemsControl. It is binding fine and is clearly on the ItemsControl but it is stuck at 0, 0. What am I missing here?

Here is the UserControl code behind:

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);
        }
    }
}

Here is the 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>

And here is the 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>

I attempted to TranslateTransform to the correct location. I tried setting the margin. I also checked my bindings in the transform and it is correct but the UserControl does not move nonetheless.


Solution

  • You should use itemcontainerstyle to position your ellipses:

       <ItemsControl.ItemContainerStyle>
           <Style>
                <Setter Property="Canvas.Top" Value="{Binding Y}"/>
                <Setter Property="Canvas.Left"Value="{Binding X}"/>
           </Style>
       </ItemsControl.ItemContainerStyle>
    

    Assuming significant point means you want your ellipse centred about a given point then you can use a path and ellipsegeometry.

    This:

      <Path  
            Height="7"
            Width="7"
            Fill="White"
            Stroke="Black"
            StrokeThickness="1">
                <Path.Data>
                    <EllipseGeometry 
                        RadiusX="2.5" 
                        RadiusY="2.5"
                                   />
                </Path.Data>
      </Path>
    

    Will be centred on it's canvas.top and canvas.left point.

    I use the above to check when I'm developing our game and I need to visualise calculated points.

    That path is the content of a SpotView usercontrol in my code base.

    In the designer it looks like:

    enter image description here

    The blue lines you see are the usercontrol edges.

    Demonstrating the spot itself is centred around the usercontrol top left corner and extending "out" what you might usually consider to be the usercontrol bounding box.

    <UserControl x:Class="UILib.SpotView"
                 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:UILib"
                 mc:Ignorable="d" 
          >
          <Path  
                Height="7"
                Width="7"
                Fill="White"
                Stroke="Black"
                StrokeThickness="1">
                    <Path.Data>
                        <EllipseGeometry 
                            RadiusX="2.5" 
                            RadiusY="2.5"
                                       />
                    </Path.Data>
          </Path>
    </UserControl>