ChangeView( double horizontalOffset, double verticalOffset, float zoomFactor)
を使います。
horizontal/verticalOffsetをNullにするとOffsetは0、画像左上をScrollViewerの左上に合わせた状態、になりますよと。
では、所謂「普通の」拡大縮小…ScrollViewer で今見ている物の中心を維持したまま拡大縮小するには?というのがこの項の話です。
これが実は結構面倒でして。完成しているものがこちらになります。(PICT8 で使っています)
private void changeViewWithKeepCurrentCenter(ScrollViewer currentSv, float newZoomFactor) { // SVのProperty解説 // Viewport SVのサイズ、表示エリア // Extent 表示されてるイメージサイズ(クリップされてるとこも含む,のでアスペクト比は一定) // VPに対して余りがある場合そこは含まれない あくまでイメージのサイズ // ZoomFactor1なら元画像サイズと同じになる .5なら半分、2なら倍 // H/VOffset Extentベースの表示オフセット値 // つまり // 1) 今表示している部分の中心座標が、元画像の何処にあたるか算出して // 2) 新しい倍率から新しいExtentのサイズを出して // 3) 1)で出した元の中心とExtentから新しいOffsetを算出 // 元画像サイズを算出 double originalWidth = currentSv.ExtentWidth / currentSv.ZoomFactor; double originalHeight = currentSv.ExtentHeight / currentSv.ZoomFactor; // 元画像スケールでの現表示の中心位置を算出する double originalCenterX = 0; double originalCenterY = 0; if( currentSv.ViewportWidth < currentSv.ExtentWidth ) { // 余りが無い状態 // 「余り」とは、ExtentがViewportより小さくなっていて、SVが額縁をつけてる状態 double eCenterX = currentSv.HorizontalOffset + currentSv.ViewportWidth / 2; // originalに変換 originalCenterX = eCenterX / currentSv.ZoomFactor; } else { // 余りアリ double eCenterX = currentSv.HorizontalOffset + currentSv.ExtentWidth / 2; originalCenterX = eCenterX / currentSv.ZoomFactor; } if (currentSv.ViewportHeight < currentSv.ExtentHeight) { // 余りが無い状態 double eCenterY = currentSv.VerticalOffset + currentSv.ViewportHeight / 2; originalCenterY = eCenterY / currentSv.ZoomFactor; } else { // 余りアリ double eCenterY = currentSv.VerticalOffset + currentSv.ExtentHeight / 2; originalCenterY = eCenterY / currentSv.ZoomFactor; } // ZoomFactor変更後の数値を出す // 新倍率ベースでの、位置の表示中心 double newExtentCenterX = originalCenterX * newZoomFactor; double newExtentCenterY = originalCenterY * newZoomFactor; // 新倍率ベースでの、Extentのサイズ double newExtentWidth = originalWidth * newZoomFactor; double newExtentHeight = originalHeight * newZoomFactor; // 新倍率ベースでのオフセットは、中心 - Extentサイズの半分 // (ここのOffset計算では余り、ExtentとViewportサイズの差は気にしなくてよい SVが勝手にやること) double newExtentOffsetX = newExtentCenterX - currentSv.ViewportWidth / 2; double newExtentOffsetY = newExtentCenterY - currentSv.ViewportHeight / 2; // デキター currentSv.ChangeView(newExtentOffsetX, newExtentOffsetY, newZoomFactor, false); }
出来上がってるのを見ると…当たり前じゃんかと言いたくなりますが…それはさておき。
これをコピペすれば動くはずです。
せっかくですので、以下はScrollViewer のプロパティについて簡単な説明です。
ScrollViewer の三つのプロパティ…Offset, ViewportWidth/Height, ExtentWidth/Height |
ScrollViewer のProperties の内、数値として取れるスクロール・位置関連のプロパティは4つで、
- ViewportWidth/Height ... ScrollViewer が開けている口、表示領域のサイズ。拡大縮小やスクロールでこのサイズは変わりません。アプリ領域変更等では変わります。
- ExtentWidth/Height ... ScrollViewer に載せているContent の、「表示上の」サイズ。倍率に応じてこのサイズは変わります。倍率1なら元Content と同じサイズ、倍率2なら2倍。また、この値はScrollViewer のViewport で表示しきれない、Clip されている部分も含んだサイズです。
- Horizontal/Vertical Offset ... Extent の左上(0, 0)から数えた、現在Viewport 上に表示している領域のオフセット。ここでの単位は「Extent での」、つまり表示画面上での単位であることに注意してください(元画像の単位では無い)。
- ZoomFactor ... ScrollViewer の現在の倍率。ちなみにこれだけ何故かFloat なので注意(他は全部double )。
上3つ、特にHorizontal /Vertical Offset については、常に画面表示上のスケールであることに注意してください。つまり、何か計算しないと元画像の幅・高さやOffset 値は出てこないよ、ということです。
また上のCode Snippetでも少し触れていますが、Viewport よりExtent が小さい場合には少し注意が必要です。
ViewportよりExtentが小さくなってる場合 |
この場合、Horizontal/Vertical Offset は両方常に0 です。この状態ではPhysicalにはOffsetあるように見えはしますが、Logicalには常に(0, 0)、左右・上下の枠は勝手にScrollViewer がつけるもので、Contentは常に画面中心に表示されます。このため、表示中の画面中心を算出するには場合分けが必要です。
以上です。
ではでは楽しいScrollViewer Lifeをお過ごしください。
0 件のコメント:
コメントを投稿