How to convert X/Y position to Canvas Left/Top properties when using ItemsControl

coordinates, data-binding, itemscontrol, wpf

Solution

The main problem with the code you presented is that the `Canvas.Left` and `Canvas.Top` properties are relative to a `Canvas` that is in the `DataTemplate` for the `ItemsControl`. This keeps "resetting" the origin. Instead you can:

- remove the `Canvas` from the `DataTemplate`

- make the `ItemsPanel` for the `ListBox` a `Canvas`

- position the `ItemsPresenter` that wraps the `ItemsControl` items with `Canvas.Top` and `Canvas.Left`

- ensure that the `Image` and the `Canvas` have the same coordinates, or switch to using the `Canvas

Here is a complete XAML-only example of positioning `ItemsControl` items on a `Canvas` with an `Image` behind the `Canvas`:

<Grid>
    <Image x:Name="image" Height="100" Width="Auto" Source="http://thecybershadow.net/misc/stackoverflow.png"/>
    <ItemsControl Name="myItemsControl">
        <ItemsControl.ItemsSource>
            <PointCollection>
                <Point X="10" Y="10"/>
                <Point X="30" Y="30"/>
            </PointCollection>
        </ItemsControl.ItemsSource>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="Text" Foreground="Red"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter Property="Canvas.Left" Value="{Binding X}"/>
                <Setter Property="Canvas.Top" Value="{Binding Y}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>
</Grid>

Problem

I am trying to use a Canvas to display objects that have "world" location (rather than "screen" location). The canvas is defined like this: ``` <Canvas Background="AliceBlue"> <ItemsControl Name="myItemsControl" ItemsSource="{Binding MyItems}"> <Image x:Name="myMapImage" Panel.ZIndex="-1" /> <ItemsControl.ItemTemplate> <DataTemplate> <Canvas> <TextBlock Canvas.Left="{Binding WorldX}" Canvas.Top="{Binding WorldY}" Text="{Binding Text}" Width="Auto" Height="Auto" Foreground="Red" /> </Canvas> </DataTemplate> </ItemsControl.ItemTemplate> </ItemsControl> </Canvas> ``` MyItem is defined like this: ``` public class MyItem { public MyItem(double worldX, double worldY, string text) { WorldX = worldX; WorldY = worldY; Text = text; } public double WorldX { get; set; } public double WorldY { get; set; } public string Text { get; set; } } ``` In addition, I have a method to convert between world and screen coordinates: ``` Point worldToScreen(double worldX, double worldY) { // Note that the conversion uses an internal m_mapData object var size = m_mapData.WorldMax - m_mapData.WorldMin; var left = ((worldX - m_currentMap.WorldMin.X) / size.X) * myMapImage.ActualWidth; var top = ((worldY - m_currentMap.WorldMin.Y) / size.Y) * myMapImage.ActualHeight; return new Point(left, top); } ``` With the current implementation, the items are positioned in the wrong location, because their location is not converted to screen coordinates. How can I apply the worldToScreen method on the MyItem objects before they are added to the canvas? Edit: I got a little confused whether I'm going in the right way, so I posted another question: How to use WPF to visualize a simple 2D world (map and elements) There is a helpful and complete answer there also for this question

Original source

Related problems