【问题标题】:Child control handling touch event affects multi-point manipulation子控件处理触摸事件影响多点操作
【发布时间】:2018-03-16 14:33:16
【问题描述】:

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

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

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

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

【问题讨论】:

    标签: wpf


    【解决方案1】:

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

    你可以编写如下代码:

    • 设置 IsManipulationEnabled="True"(在这种情况下,不会为 LightGreen 中的网格触发 OnTouchUp)

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

    • 所以现在 OnTouchUp 会在您触摸 Viewbox 中的任何位置(不仅仅是 LightGreen 区域内)时触发

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

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

    【讨论】:

      【解决方案2】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-26
        • 2012-01-12
        相关资源
        最近更新 更多