Zoom/Pinch detection in a WPF UserControl

2019-08-12 15:47发布

问题:

I want to know how can I detect a Zoom/pinch gesture made on UserControl? Following is my XAML code. How can i detect this in "Grid_OnManipulationDelta" or "Grid_OnManipulationCompleted" methods? Thanx for the help in advance.

<UserControl x:Class="HCMainWPF.Views.MainView" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:views="clr-namespace:HCMainWPF.Views" mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" Name="ZMainView">
    <Canvas x:Name="grid">
        <Canvas.RenderTransform>
            <ScaleTransform x:Name="zoom" ScaleX="1" ScaleY="1" />
        </Canvas.RenderTransform>
        <Canvas.Triggers>
            <EventTrigger RoutedEvent="Grid.ManipulationCompleted">
                <EventTrigger.Actions>
                    <BeginStoryboard>
                        <Storyboard>
                            <DoubleAnimation Duration="00:00:02" From="320" Storyboard.TargetName="ZMainView"
                        Storyboard.TargetProperty="Height"  To="960"/>
                            <DoubleAnimation Duration="00:00:02" From="270" Storyboard.TargetName="ZMainView"
                        Storyboard.TargetProperty="Width" To="810"/>
                            <DoubleAnimation Duration="00:00:02" From="1" Storyboard.TargetName="zoom"
                        Storyboard.TargetProperty="ScaleX" To="3"/>
                            <DoubleAnimation Duration="00:00:02" From="1" Storyboard.TargetName="zoom"
                        Storyboard.TargetProperty="ScaleY" To="3"/>
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger.Actions>
            </EventTrigger>
        </Canvas.Triggers>
        <Grid Height="280" Width="280" IsManipulationEnabled="True"
            ManipulationStarting="Grid_OnManipulationStarting"
            ManipulationDelta="Grid_OnManipulationDelta" 
            ManipulationCompleted="Grid_OnManipulationCompleted">
            <Grid.RowDefinitions>
                <RowDefinition Height="1.66*"/>
                <RowDefinition Height="1.66*"/>
                <RowDefinition Height="1.66*"/>
                <RowDefinition Height="1.66*"/>
                <RowDefinition Height="1.66*"/>
                <RowDefinition Height="1.66*"/>
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="3.33*"/>
                <ColumnDefinition Width="3.33*"/>
                <ColumnDefinition Width="3.33*"/>
            </Grid.ColumnDefinitions>
        </Grid>
    </Canvas>
</UserControl>

回答1:

In short, yes. The quickest way to get up and running with a pinch/zoom gesture is to attach the 'TranslateZoomRotateBehavior' to the usercontrol or element you want to control. This will allow you to use single and multitouch events to control the pan, scale and rotation of an element.

If you want to do this manually, then I suggest you have a look at a custom version of this behaviour I've posted on Codeplex.

In particular, the ManipulationDeltaHandler in this class.



回答2:

 public class GestureDetector
{
    private readonly uint _pixelPerCm = 38;
    private bool _isGestureDetected = false;

    public bool IsPanningAllowed { get; private set; }
    public bool IsScalingAllowed { get; private set; }
    public bool IsRotatingAllowed { get; private set; }

    public GestureDetector(FrameworkElement uiElement)
    {
        IsPanningAllowed = false;
        IsScalingAllowed = false;
        IsRotatingAllowed = false;

        uiElement.ManipulationStarted += (sender, args) =>
        {
            IsPanningAllowed = true;
        };

        double scale = 0.0d;
        double rot = 0.0d;

        uiElement.ManipulationDelta += (sender, args) =>
        {
            const double MIN_SCALE_TRIGGER = 0.05;
            const int MIN_ROTATIONANGLE_TRIGGER_DEGREE = 10;
            const int MIN_FINGER_DISTANCE_FOR_ROTATION_CM = 2;

            var manipulatorBounds = Rect.Empty;
            foreach (var manipulator in args.Manipulators)
            {
                manipulatorBounds.Union(manipulator.GetPosition(sender as IInputElement));
            }

            var distance = (manipulatorBounds.TopLeft - manipulatorBounds.BottomRight).Length;
            var distanceInCm = distance /_pixelPerCm;

            scale += 1-(args.DeltaManipulation.Scale.Length / Math.Sqrt(2));

            rot += args.DeltaManipulation.Rotation;

            if (Math.Abs(scale) > MIN_SCALE_TRIGGER && Math.Abs(rot) < MIN_ROTATIONANGLE_TRIGGER_DEGREE)
            {
                ApplyScaleMode();
            }

            if (Math.Abs(rot) >= MIN_ROTATIONANGLE_TRIGGER_DEGREE && distanceInCm > MIN_FINGER_DISTANCE_FOR_ROTATION_CM)
            {
                ApplyRotationMode();
            }
        };

        uiElement.ManipulationCompleted += (sender, args) =>
        {
            scale = 0.0d;
            rot = 0.0d;
            IsPanningAllowed = false;
            IsScalingAllowed = false;
            IsRotatingAllowed = false;
            _isGestureDetected = false;
        };
    }

    private void ApplyScaleMode()
    {
        if (!_isGestureDetected)
        {
            _isGestureDetected = true;
            IsPanningAllowed = true;
            IsScalingAllowed = true;
            IsRotatingAllowed = false;
        }
    }

    private void ApplyRotationMode()
    {
        if (!_isGestureDetected)
        {
            _isGestureDetected = true;
            IsPanningAllowed = true;
            IsScalingAllowed = true;
            IsRotatingAllowed = true;
        }
    }
}

Usage:

 private void uielement_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
    {
        {
            if (_gestureDetector.IsPanningAllowed)
            {
               // Translate
            }
        }

        {
            if (_gestureDetector.IsScalingAllowed)
            {
               // Scale
            }
        }

        {
            if (_gestureDetector.IsRotatingAllowed)
            {
                // Rotate
            }
        }
    }