儿童控制处理触摸事件会影响多点操作

And*_*eed 9 wpf

我有一个必须响应TouchUp事件的UserControl,它位于Viewbox中,需要通过捏合操作进行平移和缩放.控件上的触摸事件处理得很好.但是,如果两个夹点完全包含在用户控件或其周围的视口空间内,则夹点操作仅缩放ViewPort.如果夹点跨越用户控制边界,则ManipulationDelta丢失其中一个点并报告(1,1)的比例.

如果我从处理TouchUp事件的控件中删除IsManipulationEnabled ="True",则缩放工作但触摸事件不会触发.

如何处理用户控件中的触摸事件,我还能做什么来保持ViewPort中的操作?

屏幕抓取

测试方案

<Window x:Class="TouchTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Touch Test" 
        Height="400" 
        Width="700"
        ManipulationDelta="OnManipulationDelta"
        ManipulationStarting="OnManipulationStarting">

    <Grid Background="Transparent" 
          IsManipulationEnabled="True">

        <Viewbox x:Name="Viewbox"
                 Stretch="Uniform">

            <Viewbox.RenderTransform>
                <MatrixTransform/>
            </Viewbox.RenderTransform>

            <Grid Width="800" 
                  Height="800" 
                  Background="LightGreen"
                  IsManipulationEnabled="True"
                  TouchUp="OnTouchUp">

                <TextBlock x:Name="TimeTextBlock" 
                           FontSize="100"
                           TextAlignment="Center"
                           VerticalAlignment="Center"/>

            </Grid>

        </Viewbox>

        <TextBlock x:Name="ScaleTextBlock" 
                   FontSize="10"
                   HorizontalAlignment="Right"
                   VerticalAlignment="Bottom"/>

    </Grid>
</Window>
Run Code Online (Sandbox Code Playgroud)

代码隐藏中的处理程序:

private void OnTouchUp(object sender, TouchEventArgs e)
{
    TimeTextBlock.Text = DateTime.Now.ToString("H:mm:ss.fff");
}

private void OnManipulationStarting(object sender, ManipulationStartingEventArgs e)
{
    e.ManipulationContainer = this;
}

private void OnManipulationDelta(object sender, ManipulationDeltaEventArgs e)
{
    if (Viewbox == null)
    {
        return;
    }

    ManipulationDelta delta = e.DeltaManipulation;

    ScaleTextBlock.Text = $"Delta Scale: {delta.Scale}";

    MatrixTransform transform = Viewbox.RenderTransform as MatrixTransform;

    if (transform == null)
    {
        return;
    }

    Matrix matrix = transform.Matrix;

    Point position = ((FrameworkElement)e.ManipulationContainer).TranslatePoint(e.ManipulationOrigin, Viewbox);

    position = matrix.Transform(position);

    matrix = MatrixTransformations.ScaleAtPoint(matrix, delta.Scale.X, delta.Scale.Y, position);
    matrix = MatrixTransformations.PreventNegativeScaling(matrix);
    matrix = MatrixTransformations.Translate(matrix, delta.Translation);
    matrix = MatrixTransformations.ConstrainOffset(Viewbox.RenderSize, matrix);

    transform.Matrix = matrix;
}
Run Code Online (Sandbox Code Playgroud)

支持班:

public static class MatrixTransformations
{
    /// <summary>
    /// Prevent the transformation from being offset beyond the given size rectangle.
    /// </summary>
    /// <param name="size"></param>
    /// <param name="matrix"></param>
    /// <returns></returns>
    public static Matrix ConstrainOffset(Size size, Matrix matrix)
    {
        double distanceBetweenViewRightEdgeAndActualWindowRight = size.Width * matrix.M11 - size.Width + matrix.OffsetX;
        double distanceBetweenViewBottomEdgeAndActualWindowBottom = size.Height * matrix.M22 - size.Height + matrix.OffsetY;

        if (distanceBetweenViewRightEdgeAndActualWindowRight < 0)
        {
            // Moved in the x-axis too far left. Snap back to limit
            matrix.OffsetX -= distanceBetweenViewRightEdgeAndActualWindowRight;
        }

        if (distanceBetweenViewBottomEdgeAndActualWindowBottom < 0)
        {
            // Moved in the x-axis too far left. Snap back to limit
            matrix.OffsetY -= distanceBetweenViewBottomEdgeAndActualWindowBottom;
        }

        // Prevent positive offset
        matrix.OffsetX = Math.Min(0.0, matrix.OffsetX);
        matrix.OffsetY = Math.Min(0.0, matrix.OffsetY);

        return matrix;
    }

    /// <summary>
    /// Prevent the transformation from performing a negative scale.
    /// </summary>
    /// <param name="matrix"></param>
    /// <returns></returns>
    public static Matrix PreventNegativeScaling(Matrix matrix)
    {
        matrix.M11 = Math.Max(1.0, matrix.M11);
        matrix.M22 = Math.Max(1.0, matrix.M22);

        return matrix;
    }

    /// <summary>
    /// Translate the matrix by the given vector to providing panning.
    /// </summary>
    /// <param name="matrix"></param>
    /// <param name="vector"></param>
    /// <returns></returns>
    public static Matrix Translate(Matrix matrix, Vector vector)
    {
        matrix.Translate(vector.X, vector.Y);
        return matrix;
    }

    /// <summary>
    /// Scale the matrix by the given X/Y factors centered at the given point.
    /// </summary>
    /// <param name="matrix"></param>
    /// <param name="scaleX"></param>
    /// <param name="scaleY"></param>
    /// <param name="point"></param>
    /// <returns></returns>
    public static Matrix ScaleAtPoint(Matrix matrix, double scaleX, double scaleY, Point point)
    {
        matrix.ScaleAt(scaleX, scaleY, point.X, point.Y);
        return matrix;
    }
}
Run Code Online (Sandbox Code Playgroud)

Vis*_*hal 1

所以,我不是 wpf 程序员。但有一个可能对您有用的建议/解决方法。

您可以按如下方式编写代码:

  • 设置 IsManipulationEnabled="True" (在这种情况下,浅绿色网格不会触发 OnTouchUp)

  • 设置为在其中一个或上面的 thisOnTouchUp上触发(而不是为)Viewbox x:Name="Viewbox"GridViewbox800x800 Grid

  • 因此,现在只要您触摸 Viewbox 中的任何位置(不仅仅是 LightGreen 区域内),OnTouchUp 就会被触发

  • 现在触发 OnTouchUp 时,只需检查坐标是否在 LightGreen 框的区域内。如果是 -> 更新时间,如果否,则保持时间不变。

我明白这是一个解决方法。仍然发布了答案,以防万一它有用。