Wpf 曲面展开:平移/旋转/缩放没有散点视图的项目

Wpf 曲面展开:平移/旋转/缩放没有散点视图的项目,wpf,rotation,scale,pixelsense,translate-animation,Wpf,Rotation,Scale,Pixelsense,Translate Animation,是否可以在没有散点视图的情况下对项目进行转换/旋转/缩放?我想操纵可以位于其他元素之上的项,例如按钮、列表或自定义控件,这些项应该是静态的。当我将它们添加到ScatterView时,它们都会变成ScatterViewItems,这不是期望的效果。是的,您可以使用API附带的操纵处理器在Mark的答案上展开一点 是的,您可以使用操纵和惯性API来实现这一点,请参见 不久前,我创建了自己的非常基本的scatterview控件,基本上实现了scatterview的功能,但有以下限制: 只有一个孩子,

是否可以在没有散点视图的情况下对项目进行转换/旋转/缩放?我想操纵可以位于其他元素之上的项,例如按钮、列表或自定义控件,这些项应该是静态的。当我将它们添加到ScatterView时,它们都会变成ScatterViewItems,这不是期望的效果。

是的,您可以使用API附带的操纵处理器

在Mark的答案上展开一点

是的,您可以使用操纵和惯性API来实现这一点,请参见

不久前,我创建了自己的非常基本的scatterview控件,基本上实现了scatterview的功能,但有以下限制:

  • 只有一个孩子,所以它更像一个边界
  • 子项没有SV那样的默认视觉外观或特殊行为
在开发这个时,我遇到的一个问题是,您必须使容器控件占据比实际子控件更大的区域。否则,当手指位于操纵对象之外时,您将无法可靠地捕获接触事件。我所做的是使我的容器全屏(1024x768)并且是透明的,对于我的案例来说它工作正常

对于操纵本身,可以使用的实例。该类要求您启动操作,然后它将不断向您提供增量事件(
Affine2DManipulationDelta

如果您希望在用户释放手指后操作具有更真实的行为,您将使用与操作处理器类似的工作方式。基本设置是,一旦操作处理器完成(用户释放手指),您就通知惯性处理器启动

让我们看一些代码:)以下是我在容器类中的设置方法:

private void InitializeManipulationInertiaProcessors()
{
    manipulationProcessor = new Affine2DManipulationProcessor(
      Affine2DManipulations.TranslateY | Affine2DManipulations.TranslateX |
      Affine2DManipulations.Rotate | Affine2DManipulations.Scale, 
      this);
    manipulationProcessor.Affine2DManipulationCompleted += new EventHandler<Affine2DOperationCompletedEventArgs>(processor_Affine2DManipulationCompleted);
    manipulationProcessor.Affine2DManipulationDelta += new EventHandler<Affine2DOperationDeltaEventArgs>(processor_Affine2DManipulationDelta);
    inertiaProcessor = new Affine2DInertiaProcessor();
    inertiaProcessor.Affine2DInertiaDelta += new EventHandler<Affine2DOperationDeltaEventArgs>(processor_Affine2DManipulationDelta);
}
protected override void OnContactDown(ContactEventArgs e)
{
    base.OnContactDown(e);
    e.Contact.Capture(this);
    // Tell the manipulation processor what contact to track and it will 
    // start sending manipulation delta events based on user motion.
    manipulationProcessor.BeginTrack(e.Contact);
    e.Handled = true;
}
就这些,现在坐下来,让操纵处理器完成它的工作。每当有新数据时,它都会引发增量事件(在用户移动手指时每秒发生几次)。请注意,作为处理器的消费者,如何处理这些值取决于您自己。它只会告诉您“用户已应用X度旋转”或“用户移动手指X,Y像素”之类的内容。然后,您通常会将这些值转发到rendertransforms,以实际向用户显示发生了什么

在我的例子中,我的子对象有三个硬编码的rendertransforms:“translate”、“rotate”和“scale”,我用处理器中的值更新它们。我还进行了一些边界检查,以确保对象没有移动到曲面之外,也没有缩放过大或过小:

/// <summary>
/// This is called whenever the manipulator or the inertia processor has calculated a new position
/// </summary>
/// <param name="sender">The processor who caused the change</param>
/// <param name="e">Event arguments containing the calculations</param>
void processor_Affine2DManipulationDelta(object sender, Affine2DOperationDeltaEventArgs e)
{            
    var x = translate.X + e.Delta.X;
    var y = translate.Y + e.Delta.Y;
    if (sender is Affine2DManipulationProcessor)
    {
        // Make sure we don't move outside the screen
        // Inertia processor does this automatically so only adjust if sender is manipulation processor
        y = Math.Max(0, Math.Min(y, this.ActualHeight - box.RenderSize.Height));
        x = Math.Max(0, Math.Min(x, this.ActualWidth - box.RenderSize.Width));
    }
    translate.X = x;
    translate.Y = y;
    rotate.Angle += e.RotationDelta;
    var newScale = scale.ScaleX * e.ScaleDelta;
    Console.WriteLine("Scale delta: " + e.ScaleDelta + " Rotate delta: " + e.RotationDelta);
    newScale = Math.Max(0.3, Math.Min(newScale, 3));
    scale.ScaleY = scale.ScaleX = newScale;
}
//
///每当操纵器或惯性处理器计算出一个新位置时,就会调用该函数
/// 
///导致更改的处理器
///包含计算的事件参数
无效处理器\u Affine2DManipulationDelta(对象发送器,Affine2DOperationDeltaEventArgs e)
{            
var x=translate.x+e.Delta.x;
var y=translate.y+e.Delta.y;
if(发送方是仿射的E2D操作处理器)
{
//确保我们不会离开屏幕
//惯性处理器会自动执行此操作,因此仅当发送器是处理器时才进行调整
y=Math.Max(0,Math.Min(y,this.ActualHeight-box.renderize.Height));
x=Math.Max(0,Math.Min(x,this.ActualWidth-box.renderize.Width));
}
X=X;
翻译Y=Y;
旋转角度+=e旋转增量;
var newScale=scale.ScaleX*e.ScaleDelta;
Console.WriteLine(“缩放增量:+e.ScaleDelta+”旋转增量:+e.RotationDelta);
newScale=Math.Max(0.3,Math.Min(newScale,3));
scale.ScaleY=scale.ScaleX=newScale;
}
这里需要注意的一点是,操纵和惯性处理器都对增量事件使用相同的回调

最后一个难题是当用户松开手指时,我想启动惯性处理器:

/// <summary>
/// This is called when the manipulator has completed (i.e. user released contacts) and we let inertia take over movement to make a natural slow down
/// </summary>
void processor_Affine2DManipulationCompleted(object sender, Affine2DOperationCompletedEventArgs e)
{
    inertiaProcessor.InitialOrigin = e.ManipulationOrigin;

    // Set the deceleration rates. Smaller number means less friction (i.e. longer time before it stops)
    inertiaProcessor.DesiredAngularDeceleration = .0010;
    inertiaProcessor.DesiredDeceleration = .0010;
    inertiaProcessor.DesiredExpansionDeceleration = .0010;
    inertiaProcessor.Bounds = new Thickness(0, 0, this.ActualWidth, this.ActualHeight);
    inertiaProcessor.ElasticMargin = new Thickness(20);

    // Set the initial values.
    inertiaProcessor.InitialVelocity = e.Velocity;
    inertiaProcessor.InitialExpansionVelocity = e.ExpansionVelocity;
    inertiaProcessor.InitialAngularVelocity = e.AngularVelocity;

    // Start the inertia.
    inertiaProcessor.Begin();
}
//
///当操纵器完成(即,用户释放触点)并且我们让惯性接管运动以自然减速时,这称为
/// 
无效处理器\u Affine2DManipulationCompleted(对象发送方,Affine2DOperationCompletedEventArgs e)
{
inertiaProcessor.InitialOrigin=e.ManufactionOrigin;
//设置减速率。数值越小,摩擦越小(即停止前的时间越长)
惯性处理器。期望角减速=.0010;
惯性处理器.DesiredDecelleration=.0010;
惯性处理器.DesiredExpansion减速=.0010;
inertiaProcessor.Bounds=新厚度(0,0,this.ActualWidth,this.ActualHeight);
惯性。弹性裕度=新厚度(20);
//设置初始值。
惯性处理器。初始速度=e.速度;
inertiaProcessor.InitialExpansionVelocity=e.ExpansionVelocity;
inertiaProcessor.InitialAngularVelocity=e.AngularVelocity;
//启动惯性。
inertiaProcessor.Begin();
}