关于Windows 8:使用WritableBitmapEx从xaml控件创建位图图像

Creating Bitmap Image from xaml control using WritableBitmapEx

如何使用WritableBitmapex从XAML控件创建位图图像。在我的WinRT应用程序中,我需要创建一个窗口快照来实现pin-to-tile(固定辅助tile)。我发现winrt中缺少writableBitmap.render()。如何使用writableBitmapex来实现此功能。


不知怎的,他们错过了实现writeablebitmap.render(),而据我所知,它可能出现在更高版本的SDK中,现在您的选项是要么使用writeablebitmap,并使用控件的内容逐像素填充它,也许借助于writeablebitmapex,或者使用directx,也许与sharpdx的帮助,它是DirectX的包装,您可以在C应用程序中使用。如果要呈现任何文本,则需要使用directx/direct2d/directwrite。如果您的用户界面很简单,那么使用DirectX并不难。有一个样本似乎是从这里开始的好地方。

编辑*

然后还有一个可写的bitmap.render()扩展方法,我在winrt xaml工具包中开始实现,它对呈现可视化树的支持有限,但可能有帮助。我计划在必要时扩展它以支持更多的UI元素。它目前支持具有纯色或渐变背景的典型文本块和形状。

编辑2 *

Windows8.1添加了RenderTargetBitmap.RenderAsync()API,这是非常有用的,尽管它有一些限制,例如它要求呈现的UI是可视化树的一部分(尽管它可以在隐藏的面板中),错过了视频播放、DirectX内容,我相信WebView内容也是如此。


WritableBitmapex在被问到这个问题后已经更新,现在支持WinRT,所以它应该可以正常工作。

这里有大量的演示来展示这一点,并在:https://gist.github.com/2834070上回答了许多这样的问题。


我一直在使用windows.storage.streams中的randomaccessstreamreference类来创建位图。一个例子(这实际上是用于共享的代码):

1
2
            var reference = RandomAccessStreamReference.CreateFromUri(new Uri(item.ImagePath.AbsoluteUri));
            request.Data.SetBitmap(reference);

还要记住,对于固定辅助图块,只要图块是应用程序包的一部分,就可以在不创建实际位图的情况下,为图块上的徽标传递一个URI,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
        var uri = new Uri(item.TileImagePath.AbsoluteUri);

        var tile = new SecondaryTile(
                item.UniqueId,              // Tile ID
                item.ShortTitle,            // Tile short name
                item.Title,                 // Tile display name
                item.UniqueId,              // Activation argument
                TileOptions.ShowNameOnLogo, // Tile options
                uri                         // Tile logo URI
            );

        await tile.RequestCreateAsync();

最后,如果要在辅助图块上使用的图像是联机的,而不是应用程序包的一部分,则必须先在本地复制它,然后才能使用它。下面是一些代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
        // This is the URI that you will then pass as the last parameter into
        // the Secondary Tile constructor, like the code above:
        var logoUri = await GetLocalImageAsync(restaurant.ImagePath, restaurant.Key);

        // and here's the method that does the meat of the work:

    /// <summary>
    /// Copies an image from the internet (http protocol) locally to the AppData LocalFolder.
    /// This is used by some methods (like the SecondaryTile constructor) that do not support
    /// referencing images over http but can reference them using the ms-appdata protocol.  
    /// </summary>
    /// <param name="internetUri">The path (URI) to the image on the internet</param>
    /// <param name="uniqueName">A unique name for the local file</param>
    /// <returns>Path to the image that has been copied locally</returns>
    private async Task<Uri> GetLocalImageAsync(string internetUri, string uniqueName)
    {
        if (string.IsNullOrEmpty(internetUri))
        {
            return null;
        }

        using (var response = await HttpWebRequest.CreateHttp(internetUri).GetResponseAsync())
        {
            using (var stream = response.GetResponseStream())
            {
                var desiredName = string.Format("{0}.jpg", uniqueName);
                var file = await ApplicationData.Current.LocalFolder.CreateFileAsync(desiredName, CreationCollisionOption.ReplaceExisting);

                using (var filestream = await file.OpenStreamForWriteAsync())
                {
                    await stream.CopyToAsync(filestream);
                    return new Uri(string.Format("ms-appdata:///local/{0}.jpg", uniqueName), UriKind.Absolute);
                }
            }
        }
    }