使用Win2D中的VirtualBitmapControl并立即加载图像

时间:2016-06-21 14:44:33

标签: uwp uwp-xaml win2d

XAML,UWP,MVVM和Win2D的新手。

我需要在页面视图中显示一个非常大的图像。我正在看Win2D,特别是在VirtualBitmapControl和VirtualBitmapExample。

这与我想做的很接近,但我不需要选择我的图像,当我导航到页面时,我已经掌握了这些信息。

我尝试复制控件并删除文件选择器,但我无法看到我将从文件路径加载图像到页面中显示的位置。

从逐步调试器开始,VirtualBitmapControl初始化,甚至在设置文件路径的绑定之前。为了进一步添加另一个层,我也使用MVVM,所以我试图将它全部封装为UserControl,并且能够以与使用Image控件相同的方式使用它。

这是我的XAML代码:

    <UserControl
    x:Class="PEERNET.UWPImageViewer.Views.VirtualBitmapControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:PEERNET.UWPImageViewer.Views"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:Win2Dcanvas="using:Microsoft.Graphics.Canvas.UI.Xaml"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400"
    d:DataContext="{d:DesignInstance Type=local:VirtualBitmapControl, IsDesignTimeCreatable=true}"
    SizeChanged="Control_SizeChanged"
    Unloaded="Control_Unloaded"
    Loading="Control_Loading"
    Loaded="Control_Loaded">

    <Grid>
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup x:Name="AdaptiveVisualStateGroup">
                <VisualState x:Name="VisualStateNarrow">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="{StaticResource NarrowMinWidth}" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <!--  TODO: change properties for narrow view  -->
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="VisualStateNormal">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="{StaticResource NormalMinWidth}" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <!--  TODO: change properties for normal view  -->
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="VisualStateWide">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="{StaticResource WideMinWidth}" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <!--  TODO: change properties for wide view  -->
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>

        <ScrollViewer HorizontalScrollMode="Enabled"
                      VerticalScrollMode="Enabled"
                      ZoomMode="Disabled"
                      HorizontalScrollBarVisibility="Auto"
                      VerticalScrollBarVisibility="Auto"
                      x:Name="ImageScrollViewer">
            <Grid>
                <Win2Dcanvas:CanvasVirtualControl
                    x:Name="ImageVirtualControl"
                    CreateResources="ImageVirtualControl_CreateResources"
                    RegionsInvalidated="ImageVirtualControl_RegionsInvalidated"/>
            </Grid>
        </ScrollViewer>

    </Grid>
</UserControl>

以下是代码隐藏:

    public sealed partial class VirtualBitmapControl : UserControl, INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public VirtualBitmapControl()
    {
        this.InitializeComponent();

        if (!DesignMode.DesignModeEnabled)
        {
            DataContext = this;
        }

        virtualBitmapOptions = CanvasVirtualBitmapOptions.None;
        //virtualBitmapOptions = CanvasVirtualBitmapOptions.CacheOnDemand;
        //virtualBitmapOptions = CanvasVirtualBitmapOptions.ReleaseSource;
    }

    public string LoadedImageInfo { get; private set; }

    public bool IsImageLoaded { get { return virtualBitmap != null; } }


    //StorageFile PhotoAsStorageFile;
    IRandomAccessStream imageStream;

    CanvasVirtualBitmap virtualBitmap;
    CanvasVirtualBitmapOptions virtualBitmapOptions;

    // This is the file we are displaying
    public string FilePath
    {
        get { return (string)GetValue(FilePathProperty); }
        set { SetValue(FilePathProperty, value); }
    }

    // Using a DependencyProperty as the backing store for FilePath.
    // This enables animation, styling, binding, etc...
    public static readonly DependencyProperty FilePathProperty =
        DependencyProperty.Register("FilePath", typeof(string), typeof(VirtualBitmapControl),
                                    new PropertyMetadata(null, new PropertyChangedCallback(OnPropertyChanged)));
                                    //null);

     private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var instance = d as VirtualBitmapControl;
        if (d == null)
            return;

        if (instance.virtualBitmap != null)
        {
            //instance.virtualBitmap.Invalidate();
            //instance.virtualBitmap.InvalidateMeasure();
        }            
    }

    private void Control_SizeChanged(object sender, SizeChangedEventArgs e)
    {

        // TODO: What do I need to do here?
        ImageScrollViewer.MaxWidth = double.MaxValue;
        ImageScrollViewer.MaxHeight = double.MaxValue;

        /* WIN2d sample code
        if (smallView)
        {
            ImageScrollViewer.MaxWidth = ActualWidth / 4;
            ImageScrollViewer.MaxHeight = ActualHeight / 4;
        }
        else
        {
            ImageScrollViewer.MaxWidth = double.MaxValue;
            ImageScrollViewer.MaxHeight = double.MaxValue;
        }*/
    }

    private void Control_Loading(FrameworkElement sender, object args)
    {
        System.Diagnostics.Debug.WriteLine("VirtualBitmapControl::Control_Loading");

    }


    private void Control_Loaded(object sender, RoutedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("VirtualBitmapControl::Control_Loaded");
    }

    private void Control_Unloaded(object sender, RoutedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("VirtualBitmapControl::Control_Unloaded");

        if (ImageVirtualControl != null)
        {
            ImageVirtualControl.RemoveFromVisualTree();
            ImageVirtualControl = null;
        }
    }

    private void ImageVirtualControl_CreateResources(Microsoft.Graphics.Canvas.UI.Xaml.CanvasVirtualControl sender, Microsoft.Graphics.Canvas.UI.CanvasCreateResourcesEventArgs args)
    {
        System.Diagnostics.Debug.WriteLine("VirtualBitmapControl::ImageVirtualControl_CreateResources");
        if (imageStream != null)
        {
            args.TrackAsyncAction(LoadVirtualBitmap().AsAsyncAction());
        }
    }

    private void ImageVirtualControl_RegionsInvalidated(Microsoft.Graphics.Canvas.UI.Xaml.CanvasVirtualControl sender, Microsoft.Graphics.Canvas.UI.Xaml.CanvasRegionsInvalidatedEventArgs args)
    {
        foreach (var region in args.InvalidatedRegions)
        {
            using (var ds = ImageVirtualControl.CreateDrawingSession(region))
            {
                if (virtualBitmap != null)
                    ds.DrawImage(virtualBitmap, region, region);
            }
        }
    }


    private async Task LoadVirtualBitmap()
    {
        if (virtualBitmap != null)
        {
            virtualBitmap.Dispose();
            virtualBitmap = null;
        }

        LoadedImageInfo = "";

        if (imageStream != null)
        {
            imageStream.Dispose();
            imageStream = null;
        }
        NotifyPropertyChanged();


        if (imageStream == null)
        {
            imageStream = await GetBitmapStreamFromFilePathAsync(this.FilePath);
        }
        NotifyPropertyChanged();


        virtualBitmap = await CanvasVirtualBitmap.LoadAsync(ImageVirtualControl.Device, imageStream, virtualBitmapOptions);

        if (ImageVirtualControl == null)
        {
            // This can happen if the page is unloaded while LoadAsync is running
            return;
        }

        var size = virtualBitmap.Size;
        ImageVirtualControl.Width = size.Width;
        ImageVirtualControl.Height = size.Height;
        ImageVirtualControl.Invalidate();

        LoadedImageInfo = string.Format("{0}x{1} image, is {2}CachedOnDemand",
            size.Width, size.Height, virtualBitmap.IsCachedOnDemand ? "" : "not ");

        NotifyPropertyChanged();
    }

    private void NotifyPropertyChanged()
    {
        if (PropertyChanged == null)
            return;

        foreach (var property in new string[] { "LoadedImageInfo", "IsImageLoaded", "FilePath"})
        {
            PropertyChanged(this, new PropertyChangedEventArgs(property));
        }
    }

    internal async static Task<IRandomAccessStream> GetBitmapStreamFromFilePathAsync(String filePath)
    {
        IRandomAccessStream imageStream = null;
        <trimmed for space>

        return imageStream;
    }

}

以及我如何使用我的页面XAML中的控件:

   <Grid x:Name="rootPhotoGrid" RelativePanel.Below="pageHeader"
            RelativePanel.AlignLeftWithPanel="True"
    RelativePanel.AlignRightWithPanel="True" Background="AntiqueWhite">
    <local:VirtualBitmapControl FilePath="{x:Bind ViewModel.LoadedImagePath}"/> </Grid>

如果有人能指出我正确的方向,或者告诉我我做错了什么,或者我错过了什么,那将非常感激。

谢里

1 个答案:

答案 0 :(得分:0)

在控件上挂起CreateResources事件,并使用TrackAsyncAction让它监视CanvasVirtualBitmap.LoadAsync调用的进度。

查看http://microsoft.github.io/Win2D/html/T_Microsoft_Graphics_Canvas_CanvasBitmap.htm

上的代码示例

更深入的背景信息:https://blogs.msdn.microsoft.com/win2d/2014/12/05/async-resource-loading-in-win2d/