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>
如果有人能指出我正确的方向,或者告诉我我做错了什么,或者我错过了什么,那将不胜感激。
雪莉