xaml中添加:
<ViewportControl x:Name="viewport" DoubleTap="OnDoubleTap" ManipulationStarted="OnManipulationStarted" ManipulationDelta="OnManipulationDelta" ManipulationCompleted="OnManipulationCompleted" ViewportChanged="viewport_ViewportChanged"> <Canvas x:Name="canvas"> <Image x:Name="image" RenderTransformOrigin="0,0" CacheMode="BitmapCache" ImageOpened="OnImageOpened"> <Image.RenderTransform> <ScaleTransform x:Name="xform"/> </Image.RenderTransform> </Image> </Canvas> </ViewportControl>
cs中添加:
namespace ImageExtend { public partial class ZoomImage : UserControl { public static readonly DependencyProperty SourceProperty = DependencyProperty.Register("Source", typeof(ImageSource), typeof(ZoomImage), new PropertyMetadata(OnImageSourceChanged)); private static void OnImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (d != null && d is ZoomImage) { (d as ZoomImage).SetImage((ImageSource)e.NewValue); } } public ImageSource Source { get { return (ImageSource)GetValue(SourceProperty); } set { SetValue(SourceProperty, value); } } const double MaxScale = 10; double _scale = 1.0; double _minScale; double _coercedScale; double _originalScale; Size _viewportSize; bool _pinching; Point _screenMidpoint; Point _relativeMidpoint; BitmapImage _bitmap; public ZoomImage() { InitializeComponent(); this.Loaded += ZoomImage_Loaded; } void ZoomImage_Loaded(object sender, RoutedEventArgs e) { if (Source != null) { SetImage(Source); } } void SetImage(ImageSource img) { image.Source = img; } /// <summary> /// Either the user has manipulated the image or the size of the viewport has changed. We only /// care about the size. /// </summary> void viewport_ViewportChanged(object sender, System.Windows.Controls.Primitives.ViewportChangedEventArgs e) { Size newSize = new Size(viewport.Viewport.Width, viewport.Viewport.Height); if (newSize != _viewportSize) { _viewportSize = newSize; CoerceScale(true); ResizeImage(false); } } /// <summary> /// Handler for the ManipulationStarted event. Set initial state in case /// it becomes a pinch later. /// </summary> void OnManipulationStarted(object sender, ManipulationStartedEventArgs e) { _pinching = false; _originalScale = _scale; } /// <summary> /// Handler for the ManipulationDelta event. It may or may not be a pinch. If it is not a /// pinch, the ViewportControl will take care of it. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e) { if (e.PinchManipulation != null) { e.Handled = true; if (!_pinching) { _pinching = true; Point center = e.PinchManipulation.Original.Center; _relativeMidpoint = new Point(center.X / image.ActualWidth, center.Y / image.ActualHeight); var xform = image.TransformToVisual(viewport); _screenMidpoint = xform.Transform(center); } _scale = _originalScale * e.PinchManipulation.CumulativeScale; CoerceScale(false); ResizeImage(false); } else if (_pinching) { _pinching = false; _originalScale = _scale = _coercedScale; } } /// <summary> /// The manipulation has completed (no touch points anymore) so reset state. /// </summary> void OnManipulationCompleted(object sender, ManipulationCompletedEventArgs e) { _pinching = false; _scale = _coercedScale; } /// <summary> /// When a new image is opened, set its initial scale. /// </summary> void OnImageOpened(object sender, RoutedEventArgs e) { _bitmap = (BitmapImage)image.Source; // Set scale to the minimum, and then save it. _scale = 0; CoerceScale(true); _scale = _coercedScale; ResizeImage(true); } /// <summary> /// Adjust the size of the image according to the coerced scale factor. Optionally /// center the image, otherwise, try to keep the original midpoint of the pinch /// in the same spot on the screen regardless of the scale. /// </summary> /// <param name="center"></param> void ResizeImage(bool center) { if (_coercedScale != 0 && _bitmap != null) { double newWidth = canvas.Width = Math.Round(_bitmap.PixelWidth * _coercedScale); double newHeight = canvas.Height = Math.Round(_bitmap.PixelHeight * _coercedScale); xform.ScaleX = xform.ScaleY = _coercedScale; viewport.Bounds = new Rect(0, 0, newWidth, newHeight); if (center) { viewport.SetViewportOrigin( new Point( Math.Round((newWidth - viewport.ActualWidth) / 2), Math.Round((newHeight - viewport.ActualHeight) / 2) )); } else { Point newImgMid = new Point(newWidth * _relativeMidpoint.X, newHeight * _relativeMidpoint.Y); Point origin = new Point(newImgMid.X - _screenMidpoint.X, newImgMid.Y - _screenMidpoint.Y); viewport.SetViewportOrigin(origin); } } } /// <summary> /// Coerce the scale into being within the proper range. Optionally compute the constraints /// on the scale so that it will always fill the entire screen and will never get too big /// to be contained in a hardware surface. /// </summary> /// <param name="recompute">Will recompute the min max scale if true.</param> void CoerceScale(bool recompute) { if (recompute && _bitmap != null && viewport != null) { // Calculate the minimum scale to fit the viewport double minX = viewport.ActualWidth / _bitmap.PixelWidth; double minY = viewport.ActualHeight / _bitmap.PixelHeight; _minScale = Math.Min(minX, minY); } _coercedScale = Math.Min(MaxScale, Math.Max(_scale, _minScale)); } private void OnDoubleTap(object sender, GestureEventArgs e) { e.Handled = true; _scale = 0; CoerceScale(true); _scale = _coercedScale; ResizeImage(true); } } }