WPF的“地图”控件;使用画布显示大量位图图片

3
我正在使用Substrate解析Minecraft文件数据。 Minecraft由'块'组成,每个块又由多个方块构成。我有一个带有不同16x16图块的精灵表。我在WPF中使用croppedbitmap来查找适当的图形块以组成地图的一部分。然后我为WPF中的Image()控件分配'source',即我的croppedbitmap。最后,我执行myCanvas.Children.Add(myImage)...的操作。问题是,在我的画布中大约有30多个图像后,应用程序就会变得非常缓慢。这是个问题,因为典型的Minecraft地图将有数百或数千个图像。是否有更好的方法来绘制大量的位图数据?我喜欢使用Image控件,因为我可以在地图上放置工具提示并使其交互式。但我承认Image控件可能更昂贵,拥有数千个Image控件可能行不通。以下是我的代码。(此代码仅解析特定值的瓷砖,因此如果id == 9)
     var mapTiles = (BitmapImage)FindResource("mapTiles");
         CroppedBitmap waterImage = new CroppedBitmap(mapTiles, new Int32Rect(352, 48, 16, 16));
         CroppedBitmap grassImage = new CroppedBitmap(mapTiles, new Int32Rect(0, 0, 16, 16));
         foreach (ChunkRef chunk in chunkManager)
         {
             countOfTiles++;

             for (int x = 0; x <= 15; x++)
             {
                 for (int z = 0; z <= 15; z++)
                 {
                     int height = chunk.Blocks.GetHeight(x, z);
                     //TODO: Normalize Chunk X, Z so 0,0 is the smallest chunk so everything is visible on the canvas.
                     if (height > 0 && chunk.X > 0 && chunk.Z > 0)
                     {
                         var block = chunk.Blocks.GetBlock(x, height - 1, z);

                         if (block.ID == 9)
                         {
                             //352, 48
                             Image image = new Image();
                             image.Source = waterImage;
                             worldMap.Children.Add(image);
                             Canvas.SetTop(image, (chunk.X + x) * 16);
                             Canvas.SetLeft(image, (chunk.Z + z) * 16);
                             image.ToolTip = countOfTiles.ToString();
                         }
1个回答

2

您可以使用更轻的ImageBrush代替Image:

// use ImageBrush's instead
var waterImage = new ImageBrush(new CroppedBitmap(...));
var grassImage = new ImageBrush(new CroppedBitmap(...));

waterImage.Freeze();
grassImage.Freeze();

稍后:

if (block.ID == 9)
{
    var water = new Rectangle
    {
        Width = 16,
        Height = 16,
        Fill = waterImage
    };
    worldMap.Children.Add(water);
    Canvas.SetTop(water, (chunk.X + x) * 16);
    Canvas.SetLeft(water, (chunk.Z + z) * 16);
    water.ToolTip = countOfTiles.ToString();
}

如果我不使用Freeze,我无法让一个100x100的瓷砖世界的测试应用程序正确显示。当我冻结每个画笔时,我可以使地图无缝显示到大约300x300。在1000x1000处几乎无法加载,但此时您需要考虑虚拟化视图,因为UI无法优雅地处理那么多UI元素。请参阅此处

非常感谢!使用这种方法速度快了很多,真是令人惊奇。有一个更正:矩形的属性应该使用'Fill'而不是'Background'。我认为这只是一个打字错误,因为我找不到'Background'属性。 - Richthofen
你说得对,那是个笔误。或许值得设计一个轻量级的“控件”,它封装了一些块的子集。此外,在世界大小方面,你最终必须转移到虚拟化面板。 - user7116
我很好奇你所说的“虚拟化视图”是什么意思。你能提供一些相关链接或其他信息吗?再次感谢你的回答! - Richthofen
1
基本上,它抽象掉了控件的子元素,这样就可以重用较少数量的UI元素来绑定不同的数据,而不是成千上万个单独的UI元素。Dan Crevier的一系列好文章介绍了如何制作虚拟面板。 - user7116

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接