最近在学习WP8,想实现WP微信中查看图片时的放大缩小功能。
网上找了两个解决方案:
1 利用GestureListener
这个类在Microsoft.Phone.Controls.Toolkit中,GestureListener可以捕捉到WP手机屏幕上的手势动作。
XAML文件:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <Image x:Name="_image" Source="/Assets/test.jpg" RenderTransformOrigin="0.5,0.5"> <Image.RenderTransform> <ScaleTransform x:Name="transform"/> </Image.RenderTransform> <toolkit:GestureService.GestureListener> <toolkit:GestureListener DoubleTap="OnDoubleTap"DragDelta="OnDragDelta" Flick="OnFlick" PinchStarted="OnPinchStarted" PinchDelta="OnPinchDelta" PinchCompleted="OnPinchCompleted"/> </toolkit:GestureService.GestureListener> </Image> </Grid>
cs文件:
double initialScale; public Zoom() { InitializeComponent(); } private void OnDoubleTap(object sender, Microsoft.Phone.Controls.GestureEventArgs e) { transform.ScaleX = transform.ScaleY = 1; } private void OnDragDelta(object sender, DragDeltaGestureEventArgs e) { transform.CenterX -= e.HorizontalChange; transform.CenterY -= e.VerticalChange; } private void OnPinchStarted(object sender, PinchStartedGestureEventArgs e) { initialScale = transform.ScaleX; } private void OnPinchDelta(object sender, PinchGestureEventArgs e) { transform.ScaleX = transform.ScaleY = initialScale * e.DistanceRatio; } private void OnPinchCompleted(object sender, PinchGestureEventArgs e) { } private void OnFlick(object sender, FlickGestureEventArgs e) { }
前面是在XAML代码中构建GestureListener,在后台代码中也可以构建GestureListener。
Grid grd = new Grid { Height = 800, Width = 480, Background = new SolidColorBrush(Colors.Black), Opacity = 0.9, }; Image img = new Image { Source = source }; grd.Children.Add(img); //设置图片变换类型为缩放 ScaleTransform transform = new ScaleTransform(); img.RenderTransform = transform; var gesListener= GestureService.GetGestureListener(img); gesListener.DoubleTap += (obj, arg) => { transform.ScaleX = transform.ScaleY = 1; }; gesListener.DragDelta += (obj, arg) => { transform.CenterX -= arg.HorizontalChange; transform.CenterY -= arg.VerticalChange; }; gesListener.PinchStarted += (obj, arg) => { initialScale = transform.ScaleX; }; gesListener.PinchDelta += (obj, arg) => { transform.ScaleX = transform.ScaleY = initialScale * arg.DistanceRatio; };
2 ViewportControl
XAML文件:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <ViewportControl x:Name="viewport" ManipulationStarted="OnManipulationStarted" ManipulationDelta="OnManipulationDelta" ManipulationCompleted="OnManipulationCompleted" ViewportChanged="viewport_ViewportChanged"> <Canvas x:Name="canvas"> <Image x:Name="TestImage" Source="/Assets/test.jpg" RenderTransformOrigin="0,0" CacheMode="BitmapCache" ImageOpened="OnImageOpened"> <Image.RenderTransform> <ScaleTransform x:Name="xform"/> </Image.RenderTransform> </Image> </Canvas> </ViewportControl> </Grid>
cs文件:
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 PinchAndZoom() { InitializeComponent(); BuildLocalizedApplicationBar(); } /// <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 / TestImage.ActualWidth, center.Y / TestImage.ActualHeight); var xform = TestImage.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)TestImage.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)); } // Sample code for building a localized ApplicationBar private void BuildLocalizedApplicationBar() { // Set the page's ApplicationBar to a new instance of ApplicationBar. ApplicationBar = new ApplicationBar(); // Create a new button and set the text value to the localized string from AppResources. ApplicationBarIconButton appBarButton = new ApplicationBarIconButton(new Uri("/Assets/appbar_info.png", UriKind.Relative)); appBarButton.Click += appBarButton_Click; appBarButton.Text = AppResources.AppBarButtonInfoText; ApplicationBar.Buttons.Add(appBarButton); } void appBarButton_Click(object sender, EventArgs e) { MessageBox.Show(AppResources.PinchZoomHelpText, AppResources.InfoCaption,MessageBoxButton.OK); }
PS:测试的话应该部署到WP8手机中测试。
参考:
http://www.cnblogs.com/chengxingliang/archive/2011/08/15/2137377.html
http://www.cnblogs.com/JerryH/archive/2012/01/05/2312635.html
http://code.msdn.microsoft.com/wpapps/Image-Recipes-0c0b8fee