我有一个必须响应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)
所以,我不是 wpf 程序员。但有一个可能对您有用的建议/解决方法。
您可以按如下方式编写代码:
设置 IsManipulationEnabled="True" (在这种情况下,浅绿色网格不会触发 OnTouchUp)
设置为在其中一个或上面的 thisOnTouchUp上触发(而不是为)Viewbox x:Name="Viewbox"GridViewbox800x800 Grid
因此,现在只要您触摸 Viewbox 中的任何位置(不仅仅是 LightGreen 区域内),OnTouchUp 就会被触发
现在触发 OnTouchUp 时,只需检查坐标是否在 LightGreen 框的区域内。如果是 -> 更新时间,如果否,则保持时间不变。
我明白这是一个解决方法。仍然发布了答案,以防万一它有用。