Wpf 使用ItemsControl时如何将X/Y位置转换为画布左/顶属性

Wpf 使用ItemsControl时如何将X/Y位置转换为画布左/顶属性,wpf,data-binding,coordinates,itemscontrol,Wpf,Data Binding,Coordinates,Itemscontrol,我试图使用画布来显示具有“世界”位置(而不是“屏幕”位置)的对象。画布的定义如下: <Canvas Background="AliceBlue"> <ItemsControl Name="myItemsControl" ItemsSource="{Binding MyItems}"> <Image x:Name="myMapImage" Panel.ZIndex="-1" /> <ItemsControl.Item

我试图使用画布来显示具有“世界”位置(而不是“屏幕”位置)的对象。画布的定义如下:

<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>
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; }
}
<Grid>
<Image x:Name="image" Height="100" Width="Auto" Source="http://thecybershadow.net/misc/stackoverflow.png"/>
<ItemsControl Name="myItemsControl">
    <ItemsControl.Resources>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding X}"/>
            <Setter Property="Canvas.Top" Value="{Binding Y}"/>
        </Style>
    </ItemsControl.Resources>
    <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>
此外,我还有一种在世界坐标和屏幕坐标之间转换的方法:

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);
}
在当前的实现中,项目定位在错误的位置,因为它们的位置未转换为屏幕坐标

在将MyItem对象添加到画布之前,如何对其应用worldToScreen方法


编辑: 我有点困惑我是否走对了路,所以我发布了另一个问题:


对于这个问题也有一个有用且完整的答案

您可以在绑定中的值转换器中应用此转换。值转换器实现IValueConverter接口()。问题是转换需要项目的X和Y分量。一个简单的解决方案是绑定到MyItem,而不是MyItem.WorldX。您可以通过使用“Path=。”来实现这一点,如果您随后创建以下值转换器

public class CoordinateLeftConverter: IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        MyItem item = value as MyItem;
        return worldToScreen(item.WorldX, item.WorldY).X;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
    }
}
您可以在绑定中使用它,如下所示:

<TextBlock Canvas.Left="{Binding Path=.,Converter={StaticResource CoordinateLeftConverter}" ... />

在页面资源中创建CoordinateLeftConverter实例的位置:

<UserControl.Resources>
  <CoordinateLeftConverter x:Key="CoordinateLeftConverter"/> 
</UserControl.Resources>

然后,您当然需要为Canvas.Top属性添加另一个转换器,或者提供一个ConverterParameter来在转换点的X/Y属性之间切换


但是,一个更简单的解决方案可能是在MyItem类中执行转换,从而消除对转换器的需要

您介绍的代码的主要问题是
Canvas.Left
Canvas.Top
属性与
Canvas
属性相关,后者位于
ItemsControl
DataTemplate
中。这将保持原点的“重置”。相反,您可以:

  • 数据模板中删除
    画布
  • 列表框
    a
    画布
  • 定位用
    Canvas.Top
    Canvas.Left包装
    ItemsControl
    项目的
    itemsresenter
  • 确保
    图像
    画布
    具有相同的坐标,或切换到使用“画布”
下面是一个完整的XAML示例,用于在
画布
上定位
项控件
项,并在
画布
后面放置
图像

<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>

在尝试将Canvas.Top属性绑定到具有CanvasTop属性的ViewModel对象时,我遇到了类似的问题,Canvas.Top属性将首先获取值,但随后会以某种方式重置,并丢失绑定表达式。但是我在这里做了一些代码方面的工作。因为我使用的是Silverlight,所以没有ItemsContainerStyle属性,所以我使用了ItemsControl.Resources,所以在上面的示例中,我的代码如下所示:

<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>
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; }
}
<Grid>
<Image x:Name="image" Height="100" Width="Auto" Source="http://thecybershadow.net/misc/stackoverflow.png"/>
<ItemsControl Name="myItemsControl">
    <ItemsControl.Resources>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding X}"/>
            <Setter Property="Canvas.Top" Value="{Binding Y}"/>
        </Style>
    </ItemsControl.Resources>
    <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>


感谢您的回答,请注意转换方法并不是那么简单-它还需要两个对象。我编辑了我的问题以反映这一点。基于复杂性,我鼓励您将此逻辑封装在MyItem类中。这将产生一个更简单的结果。我上面的示例代码在这个问题上是正确的。然而,实际定位仍然存在问题——我不能只使用X和Y,因为它们不在屏幕坐标中。转换取决于图像对象和动态MapData对象。你有什么建议?@Rick如何移动包装该
ItemsControl
ItemsPresenter
?我如何获得对该
项目resenter
的引用?哦,我得到了不正常工作的内容,我正在将Canvas.Top属性设置为TextBlock,也许TextBlock被包装在ContentPresenter中,这现在就有意义了