読者です 読者をやめる 読者になる 読者になる

かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

「Thumb コントロールで Photoshop のナビゲーターを再現する」をWinRTでやる場合

@Grabacr07さんの素敵な記事!

grabacr.net

WinRTでやるときはこうだよっていうのをちょっとだけ。主にScrollViewerがいけてない部分を補足するだけです。ScrollViewerのScrollChangedイベントはWinRTにはないので、ViewChangedイベントと、LayoutUpdatedイベントを組み合わせて同じような動きを再現させます。コード的には以下のような感じ。

private void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
    this.UpdateViewportSize();
}

private void ScrollViewer_LayoutUpdated(object sender, object e)
{
    this.UpdateViewportSize();
}

private void UpdateViewportSize()
{
    var xfactor = this.Thumbnail.ActualWidth / this.ScrollViewer.ExtentWidth;
    var yfactor = this.Thumbnail.ActualHeight / this.ScrollViewer.ExtentHeight;

    var left = this.ScrollViewer.HorizontalOffset * xfactor;
    var top = this.ScrollViewer.VerticalOffset * yfactor;

    var width = this.ScrollViewer.ViewportWidth * xfactor;
    if (width > this.Thumbnail.ActualWidth) { width = this.Thumbnail.ActualWidth; }

    var height = this.ScrollViewer.ViewportHeight * yfactor;
    if (height > this.Thumbnail.ActualHeight) { height = this.Thumbnail.ActualHeight; }

    Canvas.SetTop(this.Viewport, top);
    Canvas.SetLeft(this.Viewport, left);

    this.Viewport.Width = width;
    this.Viewport.Height = height;
}

これだけで、WinRTでも動くようになります。(試したのはWin10TPのUAPだけどたぶん大丈夫でしょう…)

f:id:okazuki:20150411195927p:plain

コード

一応XAMLとC#のコードをはっておきます。

<Page
    x:Class="App24.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App24"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        
        <Canvas Margin="10"
                Width="{Binding Path=ActualWidth, ElementName=Thumbnail}"
                Height="{Binding Path=ActualHeight, ElementName=Thumbnail}"
                HorizontalAlignment="Center">
            <Image x:Name="Thumbnail" Width="100" Stretch="Uniform" Source="ms-appx:///Assets/tree.jpg" />
            <Thumb x:Name="Viewport"
                   DragDelta="Viewport_DragDelta">
                <Thumb.Template>
                    <ControlTemplate TargetType="Thumb">
                        <Border BorderThickness="2"
                                BorderBrush="Red"
                                Background="Transparent" />
                    </ControlTemplate>
                </Thumb.Template>
            </Thumb>
        </Canvas>
        <ScrollViewer x:Name="ScrollViewer" 
                      Grid.Column="1" 
                      VerticalScrollBarVisibility="Auto" 
                      HorizontalScrollBarVisibility="Auto"
                      ViewChanged="ScrollViewer_ViewChanged"
                      LayoutUpdated="ScrollViewer_LayoutUpdated">
            <Image Source="ms-appx:///Assets/tree.jpg" />
        </ScrollViewer>
    </Grid>
</Page>
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace App24
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
        }

        private void ScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
        {
            this.UpdateViewportSize();
        }

        private void ScrollViewer_LayoutUpdated(object sender, object e)
        {
            this.UpdateViewportSize();
        }

        private void UpdateViewportSize()
        {
            var xfactor = this.Thumbnail.ActualWidth / this.ScrollViewer.ExtentWidth;
            var yfactor = this.Thumbnail.ActualHeight / this.ScrollViewer.ExtentHeight;

            var left = this.ScrollViewer.HorizontalOffset * xfactor;
            var top = this.ScrollViewer.VerticalOffset * yfactor;

            var width = this.ScrollViewer.ViewportWidth * xfactor;
            if (width > this.Thumbnail.ActualWidth) { width = this.Thumbnail.ActualWidth; }

            var height = this.ScrollViewer.ViewportHeight * yfactor;
            if (height > this.Thumbnail.ActualHeight) { height = this.Thumbnail.ActualHeight; }

            Canvas.SetTop(this.Viewport, top);
            Canvas.SetLeft(this.Viewport, left);

            this.Viewport.Width = width;
            this.Viewport.Height = height;
        }

        private void Viewport_DragDelta(object sender, DragDeltaEventArgs e)
        {
            this.ScrollViewer.ScrollToHorizontalOffset(
                this.ScrollViewer.HorizontalOffset + (e.HorizontalChange * this.ScrollViewer.ExtentWidth / this.Viewport.ActualWidth));
            this.ScrollViewer.ScrollToVerticalOffset(
                this.ScrollViewer.VerticalOffset + (e.VerticalChange * this.ScrollViewer.ExtentHeight / this.Viewport.ActualHeight));
        }
    }
}