【问题标题】:How to add zoomin-zoomout functionalities with mouse events for the image in WPF in xamarin.froms如何在 xamarin.froms 中为 WPF 中的图像添加带有鼠标事件的 zoomin-zoomout 功能
【发布时间】:2020-01-04 22:08:34
【问题描述】:

我正在使用 xamarin.forms 在 WPF 中尝试图像查看器,但我没有找到任何可用于缩放功能的东西。我只能在 xamarin 中获得 WPF 或 Android。表单但无法找到 WPF 与 xamarin.forms 的组合。 请帮助我实现这一目标。

编辑

我已经创建了另一个视图,并且能够使用两个按钮进行放大和缩小。

 <ContentPage.Content>
    <ScrollView>
        <StackLayout>
            <Image x:Name="ImageToDisplay"
           HorizontalOptions="Center"
            VerticalOptions="Center"
            Aspect="AspectFill" >
            </Image>
            <StackLayout Orientation="Horizontal" Grid.Row="1" HorizontalOptions="CenterAndExpand">
                <Button Text="ZoomOut" Clicked="ZoomOut"/>
                <Button Text="ZoomIn" Clicked="ZoomIn"/>
            </StackLayout>
        </StackLayout>
    </ScrollView>
</ContentPage.Content>

 private void ZoomIn(object sender, EventArgs e)
    {
        var currentScale = ImageToDisplay.Scale;

        if(currentScale>=1 && currentScale < 5)
        {
            ImageToDisplay.Scale = currentScale + 0.25;
        }
    }

    private void ZoomOut(object sender, EventArgs e)
    {
        var currentScale = ImageToDisplay.Scale;

        if (currentScale >= 1 && currentScale < 5)
        {
            ImageToDisplay.Scale = currentScale - 0.25;
        }
    }

但是在这里,当图像放大时,图像的剩余部分是不可见的。滚动视图中的图像没有滚动。

【问题讨论】:

    标签: wpf image xamarin.forms zooming


    【解决方案1】:

    你可以为Image自定义Behaviors来实现这个功能。

    创建自定义行为类,MultiTouchBehavior.cs

    public class MultiTouchBehavior : Behavior<View>
    {
        #region Fields
    
        private double _currentScale = 1, _startScale = 1, _xOffset, _yOffset;
    
        private PinchGestureRecognizer _pinchGestureRecognizer;
    
        private PanGestureRecognizer _panGestureRecognizer;
    
        private ContentView _parent;
    
        private View _associatedObject;
    
        #endregion
    
        /// <summary>
        /// Occurs when BindingContext is changed: used to initialise the Gesture Recognizers.
        /// </summary>
        /// <param name="sender">The sender object.</param>
        /// <param name="e">The event parameters.</param>
        private void AssociatedObjectBindingContextChanged(object sender, EventArgs e)
        {
            _parent = _associatedObject.Parent as ContentView;
            _parent?.GestureRecognizers.Remove(_panGestureRecognizer);
            _parent?.GestureRecognizers.Add(_panGestureRecognizer);
            _parent?.GestureRecognizers.Remove(_pinchGestureRecognizer);
            _parent?.GestureRecognizers.Add(_pinchGestureRecognizer);
        }
    
        /// <summary>
        /// Cleanup the events.
        /// </summary>
        private void CleanupEvents()
        {
            _pinchGestureRecognizer.PinchUpdated -= OnPinchUpdated;
            _panGestureRecognizer.PanUpdated -= OnPanUpdated;
            _associatedObject.BindingContextChanged -= AssociatedObjectBindingContextChanged;
        }
    
        /// <summary>
        /// Initialise the events.
        /// </summary>
        private void InitializeEvents()
        {
            CleanupEvents();
            _pinchGestureRecognizer.PinchUpdated += OnPinchUpdated;
            _panGestureRecognizer.PanUpdated += OnPanUpdated;
            _associatedObject.BindingContextChanged += AssociatedObjectBindingContextChanged;
        }
    
        /// <summary>
        /// Initialise the Gesture Recognizers.
        /// </summary>
        private void InitialiseRecognizers()
        {
            _pinchGestureRecognizer = new PinchGestureRecognizer();
            _panGestureRecognizer = new PanGestureRecognizer();
        }
    
        /// <summary>
        /// Occurs when Behavior is attached to the View: initialises fields, properties and events.
        /// </summary>
        protected override void OnAttachedTo(View associatedObject)
        {
            InitialiseRecognizers();
            _associatedObject = associatedObject;
            InitializeEvents();
    
            base.OnAttachedTo(associatedObject);
        }
    
        /// <summary>
        /// Occurs when Behavior is detached from the View: cleanup fields, properties and events.
        /// </summary>
        protected override void OnDetachingFrom(View associatedObject)
        {
            CleanupEvents();
    
            _parent = null;
            _pinchGestureRecognizer = null;
            _panGestureRecognizer = null;
            _associatedObject = null;
    
            base.OnDetachingFrom(associatedObject);
        }
    
        /// <summary>
        /// Implements Pan/Translate.
        /// </summary>
        /// <param name="sender">The sender object.</param>
        /// <param name="e">The event parameters.</param>
        private void OnPanUpdated(object sender, PanUpdatedEventArgs e)
        {
            if (_parent == null)
            {
                return;
            }
    
            if (!IsTranslateEnabled)
            {
                return;
            }
    
            switch (e.StatusType)
            {
                case GestureStatus.Running:
                    _parent.Content.TranslationX = _xOffset + e.TotalX;
                    _parent.Content.TranslationY = _yOffset + e.TotalY;
                    break;
    
                case GestureStatus.Completed:
                    _xOffset = _parent.Content.TranslationX;
                    _yOffset = _parent.Content.TranslationY;
                    break;
            }
        }
    
        /// <summary>
        /// Implements Pinch/Zoom.
        /// </summary>
        /// <param name="sender">The sender object.</param>
        /// <param name="e">The event parameters.</param>
        private void OnPinchUpdated(object sender, PinchGestureUpdatedEventArgs e)
        {
            if (_parent == null)
            {
                return;
            }
    
            if (!IsScaleEnabled)
            {
                return;
            }
    
            switch (e.Status)
            {
                case GestureStatus.Started:
                    _startScale = _parent.Content.Scale;
                    _parent.Content.AnchorX = 0;
                    _parent.Content.AnchorY = 0;
    
                    break;
    
                case GestureStatus.Running:
                    _currentScale += (e.Scale - 1) * _startScale;
                    _currentScale = Math.Max(1, _currentScale);
    
                    var renderedX = _parent.Content.X + _xOffset;
                    var deltaX = renderedX / _parent.Width;
                    var deltaWidth = _parent.Width / (_parent.Content.Width * _startScale);
                    var originX = (e.ScaleOrigin.X - deltaX) * deltaWidth;
    
                    var renderedY = _parent.Content.Y + _yOffset;
                    var deltaY = renderedY / _parent.Height;
                    var deltaHeight = _parent.Height / (_parent.Content.Height * _startScale);
                    var originY = (e.ScaleOrigin.Y - deltaY) * deltaHeight;
    
                    var targetX = _xOffset - (originX * _parent.Content.Width) * (_currentScale - _startScale);
                    var targetY = _yOffset - (originY * _parent.Content.Height) * (_currentScale - _startScale);
    
                    _parent.Content.TranslationX = targetX.Clamp(-_parent.Content.Width * (_currentScale - 1), 0);
                    _parent.Content.TranslationY = targetY.Clamp(-_parent.Content.Height * (_currentScale - 1), 0);
    
                    _parent.Content.Scale = _currentScale;
    
                    break;
    
                case GestureStatus.Completed:
                    _xOffset = _parent.Content.TranslationX;
                    _yOffset = _parent.Content.TranslationY;
    
                    break;
            }
        }
    
        /// <summary>
        /// Initialize the behavior when OnAppearing is executed.
        /// </summary>
        public void OnAppearing()
        {
            AssociatedObjectBindingContextChanged(_associatedObject, null);
        }
    
        #region IsScaleEnabled property
    
        /// <summary>
        /// Identifies the <see cref="IsScaleEnabledProperty" /> property.
        /// </summary>
        public static readonly BindableProperty IsScaleEnabledProperty =
            BindableProperty.Create<MultiTouchBehavior, bool>(w => w.IsScaleEnabled, default(bool));
    
        /// <summary>
        /// Identifies the <see cref="IsScaleEnabled" /> dependency / bindable property.
        /// </summary>
        public bool IsScaleEnabled
        {
            get { return (bool)GetValue(IsScaleEnabledProperty); }
            set { SetValue(IsScaleEnabledProperty, value); }
        }
    
        #endregion
    
        #region IsTranslateEnabled property
    
        /// <summary>
        /// Identifies the <see cref="IsTranslateEnabledProperty" /> property.
        /// </summary>
        public static readonly BindableProperty IsTranslateEnabledProperty =
            BindableProperty.Create<MultiTouchBehavior, bool>(w => w.IsTranslateEnabled, default(bool));
    
        /// <summary>
        /// Identifies the <see cref="IsTranslateEnabled" /> dependency / bindable property.
        /// </summary>
        public bool IsTranslateEnabled
        {
            get { return (bool)GetValue(IsTranslateEnabledProperty); }
            set { SetValue(IsTranslateEnabledProperty, value); }
        }
    
        #endregion
    }
    

    添加 MultiTouchBehavior.cs 中使用的 DoubleExtensions.cs

    public static class DoubleExtensions
    {
        public static double Clamp(this double self, double min, double max)
        {
            return Math.Min(max, Math.Max(self, min));
        }
    }
    

    最后,在Xmal中添加两个Button

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:behaviors="clr-namespace:MultiTouch.Behaviors"
                 x:Class="MultiTouch.MainPage">
    
        <StackLayout>
            <ContentView >
                    <Image x:Name="MyImage" Source="desert.jpg">
                        <Image.Behaviors>
                            <behaviors:MultiTouchBehavior IsScaleEnabled="True" IsTranslateEnabled="True" />
                        </Image.Behaviors>
                    </Image>
            </ContentView>
    
            <Button Text="ZoomIn" Clicked="ZoomIn_Clicked"/>
            <Button Text="ZoomOut" Clicked="ZoomOut_Clicked"/>
    
        </StackLayout>
    
    </ContentPage>
    

    按钮方法:

    private void ZoomIn_Clicked(object sender, System.EventArgs e)
    {
        var currentScale = MyImage.Scale;
    
        if (currentScale >= 1 && currentScale < 5)
        {
            MyImage.Scale = currentScale + 0.25;
        }
    }
    
    private void ZoomOut_Clicked(object sender, System.EventArgs e)
    {
        var currentScale = MyImage.Scale;
    
        if (currentScale >= 1 && currentScale < 5)
        {
            MyImage.Scale = currentScale - 0.25;
        }
    }
    

    这里是Sample link

    【讨论】:

    • 非常感谢@Junior Jiang - MSFT 的帮助。我添加了两个类并更改了我的 XAML,但我没有发现图像正在滚动,您能否指定我们使用 DoubleExtensions 的位置。
    • 另外,示例链接似乎不可用。能否请您再发一次。
    • @sahithi 对不起,这个不能滚动,只能拖动图片。你可以参考这个链接(github.com/davidezordan/multi-touch)。它可以显示DoubleExtensions在哪里使用,修改代码作为我的答案可以工作。
    • @sahithi 如果有其他疑问,请告诉我,感谢您提前标记。
    猜你喜欢
    • 2019-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    相关资源
    最近更新 更多