5

我正在创建一个照片查看应用程序,我想实现图片的缩放。我创建了一个 ScrollViewer 并在那里放置了一个图像。这一切都是开箱即用的。我可以做一个缩放手势,它会放大图片。现在,我要实现的下一件事是在缩放手势开始时加载图片的高分辨率版本,并在加载时动态交换 Image 控件中的位图。我希望它无缝地发生,以便用户可以继续手势并继续放大并查看更详细的图片。实现这一目标的最佳方法是什么?这是我目前拥有的代码。我的代码的问题是,当 Image.Source 被替换时,我的手势被中断并且照片被重置为原始大小。更改 ScrollViewer ZoomFactor 无济于事,因为在替换图像时它似乎会重置。我有一个带有 Image 属性的 DataModel,该属性最初返回 null,但开始以低分辨率模式从“文件”加载图片,并在加载完成时调用 OnPropertyChanged("Image")。调用 LoadFullImage() 加载全分辨率版本并在完成时调用 OnPropertyChanged("Image")。

以下是 DataModel.cs 的摘录:

public async Task LoadFullImage()
    {
        loadFullImageTask = UpdateImage(0);
        await loadFullImageTask;
    }

    public ImageSource Image
    {
        get
        {
            if (fullImage != null)
            {
                return fullImage;
            }
            else if (image != null)
            {
                return image;
            }
            else
            {
                Task loadImageTask = UpdateImage(768);

                return null;
            }
        }
    }

    public bool FullImageLoading
    {
        get { return (this.loadFullImageTask != null) && (!this.loadFullImageTask.IsCompleted); }
    }

    public bool FullImageLoaded
    {
        get { return this.fullImage != null; }
    }

这是我的 MainPage.xaml:

<Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
    <ScrollViewer x:Name="imageViewer" HorizontalAlignment="Stretch" HorizontalScrollBarVisibility="Visible" VerticalAlignment="Stretch" MinZoomFactor="1" ZoomMode="Enabled" ViewChanged="imageViewer_ViewChanged">
        <Image x:Name="image" Margin="0,0,0,0" Stretch="Uniform" Source="{Binding Image}" />
    </ScrollViewer>
</Grid>

这是我的 MainPage.xaml.cs:

protected override async void OnNavigatedTo(NavigationEventArgs e)
    {
        FileOpenPicker filePicker = new FileOpenPicker();
        filePicker.SuggestedStartLocation = PickerLocationId.ComputerFolder;
        filePicker.FileTypeFilter.Add(".jpg");
        StorageFile file = await filePicker.PickSingleFileAsync();

        data = new DataModel(file);
        imageViewer.DataContext = data; 
    }

    private async void imageViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
    {
        ScrollViewer imageViewer = (ScrollViewer)sender;

        if (imageViewer.ZoomFactor > 1)
        {
            if (!data.FullImageLoaded && (!data.FullImageLoading))
            {
                int oldHeight = ((BitmapImage)data.Image).PixelHeight;
                int oldWidth = ((BitmapImage)data.Image).PixelWidth;
                double oldHOffset = imageViewer.HorizontalOffset;
                double oldVOffset = imageViewer.VerticalOffset;

                await data.LoadFullImage();

                int newHeight = ((BitmapImage)data.Image).PixelHeight;
                int newWidth = ((BitmapImage)data.Image).PixelWidth;

                float ratio = (float)oldHeight / (float)newHeight;
                imageViewer.MaxZoomFactor = imageViewer.MaxZoomFactor * ratio;
                imageViewer.MinZoomFactor = imageViewer.MinZoomFactor * ratio;
                imageViewer.ZoomToFactor(imageViewer.ZoomFactor * ratio);
                //imageViewer.ScrollToHorizontalOffset(oldHOffset / ratio);
                //imageViewer.ScrollToVerticalOffset(oldVOffset / ratio);
            }
        }
    }

正如我已经提到的,这段代码的问题是手势被打断并且新图像没有调整大小/滚动到正确的位置,用户体验不是无缝的。感谢您提供有关如何解决此问题的任何建议。

4

2 回答 2

0

我不会为您编写此代码,因为它太多了,但这里是步骤:

  1. 图像需要将 ScaleTransform 应用于其 RenderTransform 属性。当用户捏或拉伸时,您将使用它来使其“缩放”。
  2. 使用 Manipulation 事件检测捏合手势;如果它可以帮助您减少一些代码,您甚至可以仅过滤到捏合手势。这些事件将是最可靠的。
  3. Manipulation 事件将返回发生了多少捏合或拉伸,这需要与您在数字 1 中应用到图像的 ScaleTransform 的缩放程度相对应。
  4. 在代码隐藏中,您可以创建一个 BitmapImage 指向并从服务器加载图像。加载后,您可以替换用户正在捏的图像的来源。除非高分辨率图像是数兆字节,否则这对用户来说几乎是无缝的。不要那样做。

请注意,我的步骤不包括 ScrollViewer。ScrollViewer 当然可以包装图像,但工作本身是作为图像本身的一部分完成的。

我可能还会向您MultiScaleImage指出您可能想要从中窃取一些逻辑以便做到最好的控件:http: //msdn.microsoft.com/en-us/library/system.windows.controls.multiscaleimage%28v= vs.95%29.aspx

还有一件事。这可以通过 DeepZoom 轻松完成。由于它在 XAML 中尚不可用,这并不意味着您不能利用 WebView 来完成此操作。它将为您提供一个预先构建的、可疯狂扩展(且免费)的解决方案。但选择权在你。

于 2013-01-24T19:30:49.950 回答
0

有人记得这个线程吗?=) 好的,我已经在我的应用程序
上实现了“在缩放期间替换图像” 。 正如上面提到的@Vitary,如果“内容”的大小发生变化,滚动查看器会重置缩放比例和 X/Y 偏移量。 为避免这种情况,我的应用程序 xaml 使用以下结构:

<FlipView>
  <DataTemplate>
    <ScrollViewer>
      <ViewBox> <-- This size is same with original image width/height. No change during zoom in/out.
        <Image/>  <-- This size is vary.
      </ViewBox>
    </ScrollViewer>
  </DataTemplate>
</FlipView>

通过保持 viewbox 的大小相同,可以避免滚动查看器的重置。我的实现是...

  1. 用户选择翻转视图的项目。在这个序列中,viewsource 代码将原始图像的 w/h 设置为 viewsource 成员。该成员绑定到 viewbox 的 w/h。
  2. 进行低分辨率解码。然后通过绑定设置为图像控制。如您所知,viewbox 可以自动拉伸内容以适应 viewbox 的大小。
  3. 当用户做出缩放手势时,代码隐藏通知 ViewModel。ViewModel 启动 original-res 解码,然后通过绑定将图像设置为图像控件。在这个序列中,滚动查看器可以保持偏移和缩放比例。因为,viewbox 的大小没有改变。从低分辨率到原始分辨率的切换非常顺畅且没有故障。
于 2015-02-09T12:08:18.460 回答