在WPF中更改Canvas的坐标系

时间:2008-10-31 16:34:09

标签: c# wpf xaml wpf-controls

我正在编写一个使用Canvas定位元素的地图应用程序。对于每个元素,我必须以编程方式将元素的Lat / Long转换为画布的坐标,然后设置Canvas.Top和Canvas.Left属性。

如果我有360x180画布,我可以将画布上的坐标转换为X轴上的-180到180而不是0到360,而Y轴上的90到-90而不是0到180? / p>

扩展要求:

  • 画布可以是任意大小,因此如果它是360x180或5000x100,它仍然可以使用。
  • Lat / Long区域可能不总是(-90,-180)x(90,180),它可以是任何东西(即(5,-175)x(89,-174))。
  • 像PathGeometry这样的元素是基点,而不是基于Canvas.Top/Left的元素需要工作。

8 个答案:

答案 0 :(得分:6)

这是一个全XAML解决方案。好吧,主要是XAML,因为你必须在代码中使用IValueConverter。所以:创建一个新的WPF项目并为其添加一个类。该类是MultiplyConverter:

namespace YourProject
{
    public class MultiplyConverter : System.Windows.Data.IValueConverter
    {
        public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            return AsDouble(value)* AsDouble(parameter);
        }
        double AsDouble(object value)
        {
            var valueText = value as string;
            if (valueText != null)
                return double.Parse(valueText);
            else
                return (double)value;
        }

        public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new System.NotSupportedException();
        }
    }
}

然后将此XAML用于您的Window。现在,您应该在XAML预览窗口中看到结果。

编辑:您可以通过将Canvas放入另一个Canvas来修复背景问题。有点奇怪,但它的确有效。另外,我添加了一个ScaleTransform,它翻转Y轴,使正Y向上,负向向下。请注意哪些名称去哪里:

<Canvas Name="canvas" Background="Moccasin">
    <Canvas Name="innerCanvas">
        <Canvas.RenderTransform>
            <TransformGroup>
                <TranslateTransform x:Name="translate">
                    <TranslateTransform.X>
                        <Binding ElementName="canvas" Path="ActualWidth"
                                Converter="{StaticResource multiplyConverter}" ConverterParameter="0.5" />
                    </TranslateTransform.X>
                    <TranslateTransform.Y>
                        <Binding ElementName="canvas" Path="ActualHeight"
                                Converter="{StaticResource multiplyConverter}" ConverterParameter="0.5" />
                    </TranslateTransform.Y>
                </TranslateTransform>
                <ScaleTransform ScaleX="1" ScaleY="-1" CenterX="{Binding ElementName=translate,Path=X}"
                        CenterY="{Binding ElementName=translate,Path=Y}" />
            </TransformGroup>
        </Canvas.RenderTransform>
        <Rectangle Canvas.Top="-50" Canvas.Left="-50" Height="100" Width="200" Fill="Blue" />
        <Rectangle Canvas.Top="0" Canvas.Left="0" Height="200" Width="100" Fill="Green" />
        <Rectangle Canvas.Top="-25" Canvas.Left="-25" Height="50" Width="50" Fill="HotPink" />
    </Canvas>
</Canvas>

至于您需要不同范围的新要求,更复杂的ValueConverter可能会有所帮助。

答案 1 :(得分:1)

我能够通过创建自己的自定义画布并覆盖ArrangeOverride函数来实现它:

    public class CustomCanvas : Canvas
    {
        protected override Size ArrangeOverride(Size arrangeSize)
        {
            foreach (UIElement child in InternalChildren)
            {
                double left = Canvas.GetLeft(child);
                double top = Canvas.GetTop(child);
                Point canvasPoint = ToCanvas(top, left);
                child.Arrange(new Rect(canvasPoint, child.DesiredSize));
            }
            return arrangeSize;
        }
        Point ToCanvas(double lat, double lon)
        {
            double x = this.Width / 360;
            x *= (lon - -180);
            double y = this.Height / 180;
            y *= -(lat + -90);
            return new Point(x, y);
        }
    }

哪个适用于我描述的问题,但它可能不适用于我的另一个需求,即PathGeometry。它不起作用,因为这些点没有定义为Top和Left,而是定义为实际点。

答案 2 :(得分:0)

我很确定你不能完全这样做,但是有一个从lat / long转换为Canvas坐标的方法会非常简单。

Point ToCanvas(double lat, double lon) {
  double x = ((lon * myCanvas.ActualWidth) / 360.0) - 180.0;
  double y = ((lat * myCanvas.ActualHeight) / 180.0) - 90.0;
  return new Point(x,y);
}

(或类似的东西)

答案 3 :(得分:0)

我想另一个选择是扩展画布并覆盖度量/安排,使其表现得如你所愿。

答案 4 :(得分:0)

您可以使用变换在坐标系之间进行平移,也可以使用TranslateTranform的TransformGroup将(0,0)移动到画布的中心,使用ScaleTransform将坐标移动到正确的范围。

使用数据绑定和一两个值转换器,您可以根据画布大小自动更新转换。

这样做的好处是它适用于任何元素(包括PathGeometry),可能的缺点是它会扩展所有内容而不仅仅是点 - 所以它会改变地图上图标和文本的大小。 / p>

答案 5 :(得分:0)

另一种可能的解决方案:

在另一个画布(背景画布)中嵌入自定义画布(绘图到画布)并将绘图设置为画布,使其透明且不会剪切到边界。使用矩阵转换绘图到画布,使y翻转(M22 = -1)并翻译/缩放父画布内的画布,以查看您正在查看的世界的延伸。

实际上,如果在绘图到画布上绘制-115,42,则绘制的项目将“关闭”画布,但无论如何都会显示,因为画布没有剪切到边界。然后,您可以将绘图转换为画布,以便该点显示在背景画布上的正确位置。

这是我很快就会尝试的事情。希望它有所帮助。

答案 6 :(得分:0)

这里是an answer,它描述了一个允许您应用笛卡尔坐标系的Canvas扩展方法。即:

canvas.SetCoordinateSystem(-10, 10, -10, 10)

将设置canvas的坐标系,以便x从-10到10,y从-10到10。

答案 7 :(得分:0)

我有几乎相同的问题。所以我上网了而且这个家伙使用矩阵从“设备像素”转换为他所谓的“世界坐标”,并且他认为真实世界数字而不是“设备像素”看到链接

http://csharphelper.com/blog/2014/09/use-transformations-draw-graph-wpf-c/