使用 ItemsControl 时如何将 X/Y 位置转换为 Canvas Left/Top 属性
我正在尝试使用画布来显示具有“世界”位置(而不是“屏幕”位置)的对象。画布是这样定义的:
<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 是这样定义的:
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; }
}
另外,我有一个在世界坐标和屏幕坐标之间进行转换的方法:
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 方法?
编辑:
我有点困惑我是否走正确的路,所以我发布了另一个问题: 如何使用 WPF 可视化一个简单的 2D 世界(地图和元素)
对于这个问题,这里也有一个有用且完整的答案
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
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您提供的代码的主要问题是
Canvas.Left
和Canvas.Top
属性与Canvas
中的Canvas
相关。ItemsControl
的 code>DataTemplate。这会不断“重置”原点。相反,您可以:DataTemplate
中删除Canvas
ListBox
的ItemsPanel
成为Canvas< /code>
Canvas.Top
和Canvas.Left
定位包裹ItemsControl
项的ItemsPresenter
Image
和Canvas
具有相同的坐标,或者切换到使用 `Canvas这里是一个完整的仅 XAML 示例,用于将
ItemsControl
项定位在一个Canvas
,Canvas
后面有一个Image
:The main problem with the code you presented is that the
Canvas.Left
andCanvas.Top
properties are relative to aCanvas
that is in theDataTemplate
for theItemsControl
. This keeps "resetting" the origin. Instead you can:Canvas
from theDataTemplate
ItemsPanel
for theListBox
aCanvas
ItemsPresenter
that wraps theItemsControl
items withCanvas.Top
andCanvas.Left
Image
and theCanvas
have the same coordinates, or switch to using the `CanvasHere is a complete XAML-only example of positioning
ItemsControl
items on aCanvas
with anImage
behind theCanvas
:您可以在绑定中的值转换器中应用此转换。值转换器实现 IValueConverter 接口 (http://msdn. microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx)。问题是您的转换需要项目的 X 和 Y 分量。对此的一个简单解决方案是绑定到 MyItem,而不是 MyItem.WorldX。您可以通过使用“Path=.”来实现此目的,如果您创建以下值转换器...
您可以在绑定中使用它,如下所示:
在页面资源中创建 CooperativeLeftConverter 实例的位置:
您当然可以需要为 Canvas.Top 属性添加另一个转换器,或者提供 ConverterParameter 以在转换后的 Point 的 X / Y 属性之间切换。
然而,更简单的解决方案可能是在 MyItem 类中执行转换,从而无需转换器!
You can apply this conversion within a value converter in your binding. Value converters implement the IValueConverter interface (http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx). The problem is that your conversion requires both the X and Y component of your item. A simple solution to this would be to bind to MyItem, rather than MyItem.WorldX. You can achieve this by using "Path=.", if you then create the following value converter ...
You can use it in your binding as follows:
Where you create an instance of CoordinateLeftConverter in your page Resources:
You would then of course need to add another converter for the Canvas.Top property, or supply a ConverterParameter to switch between the X / Y property of the transformed Point.
However, a simpler solution might be to perform the conversion within your MyItem class, removing the need for a converter!
当我尝试将 Canvas.Top 属性绑定到具有 CanvasTop 属性的 ViewModel 对象时,我遇到了类似的问题,Canvas.Top 属性将首先获取该值,但随后它会以某种方式重置并丢失绑定表达式。但我从这里的代码中做了一些工作。由于我使用的是 Silverlight,因此没有 ItemsContainerStyle 属性,因此我使用了 ItemsControl.Resources,因此鉴于上面的示例,我的代码如下所示:
I had a similar problem when I was trying to bind the Canvas.Top property to a ViewModel's object that has a CanvasTop property, the Canvas.Top property would first get the value, but then it gets reset somehow and loses the binding expression. But I did a little work around from the code here. And since I'm using Silverlight, there's no ItemsContainerStyle property, so I used ItemsControl.Resources instead, so given the above example, my code looks something like this: