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

时间:2018-03-16 14:33:17

标签: wpf

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

如果从处理TouchUp事件的控件中删除IsManipulationEnabled =“True”,则缩放有效,但触摸事件不会触发。

在处理用户控件中的触摸事件的同时,我可以做什么来保持ViewPort中的操作?

Screen Grab

Test Solution

<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>

代码隐藏中的处理程序:

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;
}

支持班:

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;
    }
}

2 个答案:

答案 0 :(得分:1)

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

你可以按如下方式对事物进行编码:

  • 设置IsManipulationEnabled =&#34; True&#34; (在这种情况下,OnGouchUp不会被LightGreen中的网格触发)

  • OnTouchUp设置为Viewbox x:Name="Viewbox"或此Grid上方的Viewbox(而不是800x800 Grid

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

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

我知道这是一种解决方法。仍然发布了答案,以防它可能有用。

答案 1 :(得分:0)

我不确定您发布的样本是否完全反映了您的代码......但我看到的是:您没有管理ManipulationCompleted和LostMouseCapture。你也没有制作任何MouseCapture()MouseRelease(),所以当操作向窗口输出时你松开它....在这个repo上搜索“鼠标捕获”,你会看到即使没有操作事件,这是相当复杂的.. .. https://github.com/TheCamel/ArchX/search?utf8=%E2%9C%93&q=mouse+capture&type=