ScatterView なしでアイテムを移動/回転/スケーリングすることは可能ですか? ボタン、リスト、静的にする必要があるカスタム コントロールなど、他の要素の上に配置できる項目を操作したいと考えています。それらを ScatterView に追加すると、それらはすべて ScatterViewItems になり、これは望ましい効果ではありません。
2 に答える
マークの答えを少し拡張して...
はい、操作および慣性APIを使用してこれを実現できます。この概要ページを参照してください。
しばらく前に、私は独自の非常に基本的なスキャッタービューコントロールを作成しました。これは、基本的にスキャッタービューが行うことを実行しますが、次の制限があります。
- 子供が1人だけなので、ボーダーのように機能します
- SVのように、デフォルトの外観や子アイテムの特別な動作はありません
これを開発しているときに私を襲った問題の1つは、コンテナコントロールが実際の子よりも広い領域を占めるようにする必要があることです。そうしないと、指が操作対象の外側にあるときに、接触イベントを確実にキャプチャできなくなります。私がしたことは、コンテナをフルスクリーン(1024x768)にして透明にすることでしたが、私の場合は問題なく機能します。
操作自体には、Affine2DManipulationProcessorクラスのインスタンスを使用します。このクラスでは、操作を開始する必要があります。そうすると、常にデルタイベント(Affine2DManipulationDelta
)が提供されます。
ユーザーが指を離した後の操作をより現実的なものにしたい場合は、操作プロセッサと同じように機能するAffine2DInertiaProcessorクラスを使用します。基本的な設定では、操作プロセッサが完了すると(ユーザーが指を離すと)、慣性プロセッサに起動するように指示します。
いくつかのコードを見てみましょう:)コンテナクラスのセットアップメソッドは次のとおりです。
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);
}
すべてを開始するには、ContactDownをコンテナクラスにトラップします。
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に転送して、実際に何が起こったかをユーザーに示すことです。
私の場合、子オブジェクトには3つのハードコードされた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;
}
ここで注意すべきことの1つは、操作プロセッサと慣性プロセッサの両方がデルタイベントに同じコールバックを使用することです。
パズルの最後のピースは、ユーザーが指を離し、慣性プロセッサを起動したいときです。
/// <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();
}
はい、API に付属の ManipulationProcessor を使用できます