しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

ウィンドウサイズが更新されても Uniform が維持される拡縮可能なビューア画面を作る

文章だと分かりにくいと思いますが

f:id:matatabi_ux:20140215164242j:plain

f:id:matatabi_ux:20140215164255j:plain

ストアアプリでウィンドウ幅が上記のように変化しても全体が見えている状態で縦横比が維持される表示方法(俗にいうサイドパネルやレターボックスというもの)はよく目にします

こんな表示を行いつつ、FlipView でページめくりもできてピンチで拡縮もできるようにする場合・・・これが意外と難しい;

一応それっぽい表示になったのが次のコード

<FlipView x:Name="flipView" ItemsSource="{Binding Items}">
    <FlipView.ItemTemplate>
        <DataTemplate>
            <ctrl:UnfiormImageView />
        </DataTemplate>
    </FlipView.ItemTemplate>
</FlipView>

FlipView の中の ItemTemplate に UserControl を埋め込んで・・・

<UserControl x:Class="WindowsStoreApp.Controls.UnfiormImageView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="using:WindowsStoreApp1.Controls"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d">

    <ScrollViewer x:Name="scrollViewer"
                  HorizontalScrollBarVisibility="Auto"
                  HorizontalScrollMode="Auto"
                  VerticalScrollBarVisibility="Auto"
                  VerticalScrollMode="Auto"
                  ZoomMode="Enabled">
        <Image x:Name="image"
               HorizontalAlignment="Center"
               VerticalAlignment="Center"
               Source="ms-appx:///Assets/photo.jpg" />
    </ScrollViewer>

</UserControl>

ZoomMode="Enabled" にした ScrollViewer の中に Image を入れて

/// <summary>
/// ウィンドウサイズに合わせて Uniform を維持する画像ビューア
/// </summary>
public sealed partial class UnfiormImageView : UserControl
{
    /// <summary>
    /// コンストラクタ
    /// </summary>
    public UnfiormImageView()
    {
        this.InitializeComponent();

        Window.Current.SizeChanged += this.OnWindowSizeChanged;
        this.SizeChanged += this.OnSizeChanged;
    }

    /// <summary>
    /// 描画完了イベントハンドラ
    /// </summary>
    /// <param name="sender">イベント発行者</param>
    /// <param name="e">イベント引数</param>
    private void OnSizeChanged(object sender, SizeChangedEventArgs e)
    {
        this.SizeChanged -= OnSizeChanged;
        this.image.ImageOpened += this.OnImageOpened;
    }

    /// <summary>
    /// 元の画像サイズ
    /// </summary>
    private Size originalSize = new Size();

    /// <summary>
    /// 画像表示完了イベントハンドラ
    /// </summary>
    /// <param name="sender">イベント発行者</param>
    /// <param name="e">イベント引数</param>
    private void OnImageOpened(object sender, RoutedEventArgs e)
    {
        this.image.ImageOpened -= this.OnImageOpened;
        this.originalSize.Width = this.image.ActualWidth;
        this.originalSize.Height = this.image.ActualHeight;

        this.UpdateSize();
    }

    /// <summary>
    /// ウィンドウサイズ変更イベントハンドラ
    /// </summary>
    /// <param name="sender">イベント発行者</param>
    /// <param name="e">イベント引数</param>
    private void OnWindowSizeChanged(object sender, WindowSizeChangedEventArgs e)
    {
        this.UpdateSize();
    }

    /// <summary>
    /// サイズの更新
    /// </summary>
    private void UpdateSize()
    {
        if (this.image.ActualWidth < this.image.ActualHeight)
        {
            var height = Window.Current.Bounds.Height - 17;
            this.image.Width = Math.Floor(this.originalSize.Width * height / this.originalSize.Height);
            this.image.Height = height;
        }
        else
        {
            var width = Window.Current.Bounds.Width - 17;
            this.image.Height = Math.Floor(this.originalSize.Height * width / this.originalSize.Width);
            this.image.Width = width;
        }
    }

    /// <summary>
    /// 子要素の破棄
    /// </summary>
    protected override void OnDisconnectVisualChildren()
    {
        Window.Current.SizeChanged -= OnWindowSizeChanged;
        this.image.ImageOpened -= this.OnImageOpened;
        this.SizeChanged -= OnSizeChanged;

        base.OnDisconnectVisualChildren();
    }
}

ウィンドウサイズ変更時に無理やり Image のサイズを調整するとそれっぽい動作になりました

17 px 減算しているのはスクロールバーの幅の分だけ小さくしないとスクロールバーが表示されるためです