【问题标题】:Windows Store App: set an image as a background for an UI elementWindows 应用商店应用程序:将图像设置为 UI 元素的背景
【发布时间】:2015-12-09 12:43:37
【问题描述】:

很抱歉问了一个非常基本的问题,但这可能是多年来我第一次感到非常困惑。

Windows 提供了两组控件:Windows.UI.Xaml 命名空间(我认为这被称为 Metro),用于 Windows 应用商店应用程序和 System.Windows (WPF) 用于桌面。

由于我打算主要为 Windows 8.1 和 Windows 10 手机开发 Windows 应用商店应用程序,我将不得不坚持使用 Windows.UI.Xaml,它不仅有一组单独的 UI 元素,而且还有一组单独的 UI 元素。位图、画笔等(Windows.UI.Xaml.Media 与 System.Windows.Media)。

我发现 Windows.UI.Xaml 对图形的支持非常有限,远远低于 WPF、Android 或(甚至!)iOS 平台提供的支持。首先,我遇到了一个简单的任务:平铺背景!

由于 Windows.UI.Xaml.Media.ImageBrush 不支持平铺,我想“手动”进行。一些网站建议制作许多孩子,每个孩子都拿着一块瓷砖。老实说,这对我来说看起来是一种相当尴尬的方法,所以我决定以一种看起来更自然的方式来做:创建一个离屏平铺图像,将其分配给画笔并将画笔指定为面板的背景。

平铺代码相当简单(它可能有错误,甚至可能无法在手机上运行,​​因为使用了一些不可用的类)。

int panelWidth = (int) contentPanel.Width;
int panelHeight = (int) contentPanel.Height;

Bitmap bmpOffscreen = new Bitmap(panelWidth, panelHeight);

Graphics gOffscreen = Graphics.FromImage(bmpOffscreen);

string bmpPath = Path.Combine(Windows.ApplicationModel.Package.Current.InstalledLocation.Path, "Assets/just_a_tile.png");
 System.Drawing.Image tile = System.Drawing.Image.FromFile(bmpPath,  true);
 int tileWidth = tile.Width;
 int tileHeight = tile.Height;

 for (int y = 0; y < panelHeight; y += tileHeight)
     for (int x = 0; x < panelWidth; x += tileWidth)
         gOffscreen.DrawImage(tile, x, y);

现在我大概在 bmpOffscreen 中有平铺图像。但是如何将其分配给画笔?为此,我需要将 Bitmap 转换为 BitmapSource,而我找不到类似于 System.Windows.Imaging.CreateBitmapSourceFromHBitmap 可用于 WPF 结构的东西!

【问题讨论】:

  • 试试这个作为示例(使用 xaml):
  • Michael Kozak:这不是资源位图,而是以编程方式创建的位图。

标签: xaml windows-phone-8


【解决方案1】:

首先,System.Drawing 命名空间在 Windows 通用平台中不可用,因此您将无法使用 Bitmap

但是,所有希望都不会丢失 - 您可以使用 Windows.UI.Xaml.Media.Imaging.WriteableBitmap

如果您查看此页面中包含的示例,您会看到有一次图像数据被提取到一个字节数组中 - 您需要做的就是根据您的需要复制它

如果您希望我包含完整的代码示例,请告诉我。

编辑:

StorageFile file = await StorageFile.GetFileFromPathAsync(filePath);
Scenario4WriteableBitmap = new WriteableBitmap(2000, 2000);
// Ensure a file was selected
if (file != null)
{
    using (IRandomAccessStream fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
    {
        int columns = 4;
        int rows = 4;
        BitmapDecoder decoder = await BitmapDecoder.CreateAsync(fileStream);

        // Scale image to appropriate size
        BitmapTransform transform = new BitmapTransform()
        {
            ScaledHeight = Convert.ToUInt32(Scenario4ImageContainer.Height),
            ScaledWidth = Convert.ToUInt32(Scenario4ImageContainer.Width)
        };

        PixelDataProvider pixelData = await decoder.GetPixelDataAsync(
            BitmapPixelFormat.Bgra8,    // WriteableBitmap uses BGRA format
            BitmapAlphaMode.Straight,
            transform,
            ExifOrientationMode.IgnoreExifOrientation, // This sample ignores Exif orientation
            ColorManagementMode.DoNotColorManage);

        // An array containing the decoded image data, which could be modified before being displayed
        byte[] sourcePixels = pixelData.DetachPixelData();

        // Open a stream to copy the image contents to the WriteableBitmap's pixel buffer
        using (Stream stream = Scenario4WriteableBitmap.PixelBuffer.AsStream())
        {
            for (int i = 0; i < columns * rows; i++)
            {
                await stream.WriteAsync(sourcePixels, 0, sourcePixels.Length);
            }
        }
     }

     // Redraw the WriteableBitmap
     Scenario4WriteableBitmap.Invalidate();
     Scenario4Image.Source = Scenario4WriteableBitmap;
     Scenario4Image.Stretch = Stretch.None;
 }

【讨论】:

  • 哇,如果您没有像 System.Drawing.Graphics 这样的类,那么您将如何拥有双缓冲和自定义视图,这是编写严肃游戏应用程序的常用技术?虽然有些游戏是在 Windows 商店发布的,所以应该有办法 :) 文章中的示例真的很模糊,所以。对完整代码的引用是我的最大希望,并且非常可取。
  • @cyanide 我编辑了我的答案并添加了代码示例 - 我基于来自 MSDN 的示例(此示例的完整源代码可用 here 为简单起见,我假设正方形尺寸和固定数量平铺中的行和列,但您应该可以从这里开始工作回答您关于游戏的问题 - 游戏开发人员通常使用 DirectX API,以及那些不会遭受糟糕性能的开发人员
【解决方案2】:

谢谢你,阿卡迪乌斯。由于澳大利亚时间比欧洲稍早, 在您发布代码之前,我有一个优势并看到了代码。我下载了 MSDN XAML images sample 这对我帮助很大。我给了你+1,但显然有人给了-1,所以它互相补偿。不要难过我经常得到-1,以至于我不再关注它了:)

所以我已经设法使用 Windows 通用平台进行平铺!在我的 Lumia 532 手机上它可以运行magnifique。我想重新发明一个轮子,因为所有这些东西都必须由 SDK 处理,而不是由第三方开发人员处理。

                public static async Task<bool> setupTiledBackground(Panel panel, string tilePath)
                {
                    Brush backgroundBrush = await createTiledBackground((int)panel.Width, (int)panel.Height, TilePath);
                    if (backgroundBrush == null) return false;
                    panel.Background = backgroundBrush;
                    return true;
                }



                private static async Task<Brush> createTiledBackground(int width, int height, string tilePath)
                {
                    StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///" + tilePath));


                    byte[] sourcePixels;
                    int tileWidth, tileHeight;

                    using (IRandomAccessStream inputStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read))
                    {
                        if (inputStream == null) return null;

                        BitmapDecoder tileDecoder = await BitmapDecoder.CreateAsync(inputStream);
                        if (tileDecoder == null) return null;


                        tileWidth = (int)tileDecoder.PixelWidth;
                        tileHeight = (int) tileDecoder.PixelHeight;

                        PixelDataProvider pixelData = await tileDecoder.GetPixelDataAsync(
                                BitmapPixelFormat.Bgra8,    // WriteableBitmap uses BGRA format
                                BitmapAlphaMode.Straight,
                                new BitmapTransform(),
                         ExifOrientationMode.IgnoreExifOrientation,
                         ColorManagementMode.DoNotColorManage);

                        sourcePixels = pixelData.DetachPixelData();
                        //            fileStream.Dispose();

                    }

                    WriteableBitmap backgroundBitmap = new WriteableBitmap(width, height);

                    int tileBmpWidth = tileWidth << 2;
                    int screenBmpWidth = width << 2;
                    int tileSize = tileBmpWidth * tileHeight;
                    int sourceOffset = 0;

                    using (Stream outputStream = backgroundBitmap.PixelBuffer.AsStream())
                    {
                        for (int bmpY=0; bmpY < height; bmpY++) {
                           for (int bmpX = 0; bmpX < screenBmpWidth; bmpX += tileBmpWidth)
                                await outputStream.WriteAsync(sourcePixels, sourceOffset, Math.Min(screenBmpWidth - bmpX, tileBmpWidth));

                            if ((sourceOffset += tileBmpWidth) >= tileSize)
                                sourceOffset -= tileSize;
                        } 
                    }

                    ImageBrush backgroundBrush = new ImageBrush();
                    backgroundBrush.ImageSource = backgroundBitmap;   // It's very easy now!
                    return backgroundBrush;  // Finita la comédia!
                }

只有一句话:如果你在表单开始时这样做,你不应该等待它。 这不起作用:

   public MainPage()
   {
       this.InitializeComponent();
       bool result = setupTiledBackground(contextPanel, TilePath).Result;
   }

这行得通:

  private Task<bool> backgroundImageTask;

  public MainPage()
  {
       this.InitializeComponent();
       backgroundImageTask = setupTiledBackground(contextPanel, TilePath);
  }       

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多