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>
誰かが私を正しい方向に向けたり、私が何を間違っているのか、何が欠けているのかを教えてくれれば、それは大歓迎です.
シェリ