しっぽを追いかけて

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

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

StorageFile のサムネイル画像を表示する Image コントロールもどき

Image コントロールの Source に画像ファイルの Uri を指定するとその画像を表示してくれますが、ファイルのサムネイル画像を表示する機能がありません・・・

f:id:matatabi_ux:20140131233650p:plain

ファイル一覧をストアアプリで表示するような場合・・・

サムネイル画像をアイテムとして描画するには StorageFile を取得して、GetThumbnailImageAsync で Stream を取得してBitmapImageに変換して Image コントロールの Source に設定するという非常にめんどくさい手順を踏まないといけません;

通常の画像ファイルと同じように StorageFile の Uri を指定するだけでサムネイル画像を表示してくれるコントロールがほしいと思い作ってみました!

<UserControl x:Class="WindowsStoreApp3.Controls.ThumbnailImage"
             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:WindowsStoreApp3.Controls"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             d:DesignHeight="300"
             d:DesignWidth="400"
             mc:Ignorable="d">

    <Image x:Name="Image_Part" />
</UserControl>

Image コントロールは継承不可能なのでとりあえず UserControl に内包させて

/// <summary>
/// 指定したファイルのサムネイル画像を表示するコントロール
/// </summary>
public sealed partial class ThumbnailImage : UserControl
{
    #region NineGrid 依存関係プロパティ
    /// <summary>
    /// 9 グリッド 依存関係プロパティ
    /// </summary>  
    public static readonly DependencyProperty NineGridProperty
        = DependencyProperty.Register("NineGrid", typeof(Thickness), typeof(ThumbnailImage), new PropertyMetadata(new Thickness(0), (s, e) =>
    {
        var control = s as ThumbnailImage;
        if (control != null)
        {
            control.OnNineGridChanged();
        }
    }));

    /// <summary>
    /// 9 グリッド 変更イベントハンドラ
    /// </summary>
    private void OnNineGridChanged()
    {
        this.Image_Part.NineGrid = this.NineGrid;
    }

    /// <summary>
    /// 9 グリッド
    /// </summary>
    public Thickness NineGrid
    {
        get { return (Thickness)this.GetValue(NineGridProperty); }
        set { this.SetValue(NineGridProperty, value); }
    }
    #endregion //NineGrid 依存関係プロパティ

    #region Source 依存関係プロパティ
    /// <summary>
    /// イメージのソース 依存関係プロパティ
    /// </summary>
    public static readonly DependencyProperty SourceProperty
        = DependencyProperty.Register("Source", typeof(string), typeof(object), new PropertyMetadata(null, async (s, e) =>
    {
        var control = s as ThumbnailImage;
        if (control != null)
        {
            await control.OnSourceChanged();
        }
    }));

    /// <summary>
    /// イメージのソース 変更イベントハンドラ
    /// </summary>
    /// <returns>Task</returns>
    private async Task OnSourceChanged()
    {
        try
        {
            this.Image_Part.Source = null;

            if (this.Source is string || this.Source is Uri)
            {
                Uri uri = null;
                if (this.Source is string)
                {
                    uri = new Uri((string)this.Source, UriKind.RelativeOrAbsolute);
                }
                else
                {
                    uri = (Uri)this.Source;
                }

                if (this.Source.ToString().ToLower().StartsWith("ms-app"))
                {
                    var file = await StorageFile.GetFileFromApplicationUriAsync(uri);
                    if (file == null)
                    {
                        return;
                    }
                    uint requestedSize = 0;
                    if (this.RequestSize.HasValue)
                    {
                        requestedSize = (uint)this.RequestSize;
                    }
                    else
                    {
                        this.Measure(new Size(double.MaxValue, double.MaxValue));
                        requestedSize = (uint)Math.Max(this.DesiredSize.Width, this.DesiredSize.Height);
                    }
                    var stream = await file.GetScaledImageAsThumbnailAsync(this.ThumbnailMode, requestedSize, this.ThumbnailOptions);

                    var bitmap = new BitmapImage();
                    await bitmap.SetSourceAsync(stream);
                    this.Image_Part.Source = bitmap;
                }
                else
                {
                    this.Image_Part.Source = new BitmapImage(uri);
                }
            }

            if (this.Source is StorageFile)
            {
                var file = (StorageFile)this.Source;

                uint requestedSize = 0;
                if (this.RequestSize.HasValue)
                {
                    requestedSize = (uint)this.RequestSize;
                }
                else
                {
                    this.Measure(new Size(double.MaxValue, double.MaxValue));
                    requestedSize = (uint)Math.Max(this.DesiredSize.Width, this.DesiredSize.Height);
                }
                var stream = await file.GetScaledImageAsThumbnailAsync(this.ThumbnailMode, requestedSize, this.ThumbnailOptions);

                var bitmap = new BitmapImage();
                await bitmap.SetSourceAsync(stream);
                this.Image_Part.Source = bitmap;
            }

            if (this.Source is ImageSource)
            {
                this.Image_Part.Source = (ImageSource)this.Source;
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.ToString());
        }
    }

    /// <summary>
    /// イメージのソース
    /// </summary>
    public object Source
    {
        get { return (object)this.GetValue(SourceProperty); }
        set { this.SetValue(SourceProperty, value); }
    }
    #endregion //Source 依存関係プロパティ

    #region ThumbnailMode 依存関係プロパティ
    /// <summary>
    /// サムネイルモード 依存関係プロパティ
    /// </summary>
    private static readonly DependencyProperty ThumbnailModeProperty
        = DependencyProperty.Register("ThumbnailMode", typeof(ThumbnailMode), typeof(ThumbnailImage), new PropertyMetadata(ThumbnailMode.DocumentsView, async (s, e) =>
    {
        var control = s as ThumbnailImage;
        if (control != null)
        {
            await control.OnThumbnailModeChanged();
        }
    }));

    /// <summary>
    /// サムネイルモード 変更イベントハンドラ
    /// </summary>
    /// <returns>Task</returns>
    private async Task OnThumbnailModeChanged()
    {
        await this.OnSourceChanged();
    }

    /// <summary>
    /// サムネイルモード
    /// </summary>
    public ThumbnailMode ThumbnailMode
    {
        get { return (ThumbnailMode)this.GetValue(ThumbnailModeProperty); }
        set { this.SetValue(ThumbnailModeProperty, value); }
    }
    #endregion //ThumbnailMode 依存関係プロパティ

    #region ThumbnailOptions 依存関係プロパティ
    /// <summary>
    /// サムネイルオプシション 依存関係プロパティ
    /// </summary>
    private static readonly DependencyProperty ThumbnailOptionsProperty
        = DependencyProperty.Register("ThumbnailOptions", typeof(ThumbnailOptions), typeof(ThumbnailImage), new PropertyMetadata(ThumbnailOptions.None, async (s, e) =>
    {
        var control = s as ThumbnailImage;
        if (control != null)
        {
            await control.OnThumbnailOptionsChanged();
        }
    }));

    /// <summary>
    /// サムネイルオプシション 変更イベントハンドラ
    /// </summary>
    /// <returns>Task</returns>
    private async Task OnThumbnailOptionsChanged()
    {
        await this.OnSourceChanged();
    }

    /// <summary>
    /// サムネイルオプシション
    /// </summary>
    public ThumbnailOptions ThumbnailOptions
    {
        get { return (ThumbnailOptions)this.GetValue(ThumbnailOptionsProperty); }
        set { this.SetValue(ThumbnailOptionsProperty, value); }
    }
    #endregion //ThumbnailOptions 依存関係プロパティ

    #region Stretch 依存関係プロパティ
    /// <summary>
    /// Stretch 依存関係プロパティ
    /// </summary>
    private static readonly DependencyProperty StretchProperty
        = DependencyProperty.Register("Stretch", typeof(Stretch), typeof(ThumbnailImage), new PropertyMetadata(Stretch.Uniform, (s, e) =>
    {
        var control = s as ThumbnailImage;
        if (control != null)
        {
            control.OnStretchChanged();
        }
    }));

    /// <summary>
    /// Stretch 変更イベントハンドラ
    /// </summary>
    private void OnStretchChanged()
    {
        this.Image_Part.Stretch = this.Stretch;
    }

    /// <summary>
    /// Stretch
    /// </summary>
    public Stretch Stretch
    {
        get { return (Stretch)this.GetValue(StretchProperty); }
        set { this.SetValue(StretchProperty, value); }
    }
    #endregion //Stretch 依存関係プロパティ

    #region RequestSize 依存関係プロパティ
    /// <summary>
    /// RequestSize 依存関係プロパティ
    /// </summary>
    private static readonly DependencyProperty RequestSizeProperty
        = DependencyProperty.Register("RequestSize", typeof(uint?), typeof(ThumbnailImage), new PropertyMetadata(null, async (s, e) =>
    {
        var control = s as ThumbnailImage;
        if (control != null)
        {
            await control.OnRequestSizeChanged();
        }
    }));

    /// <summary>
    /// RequestSize 変更イベントハンドラ
    /// </summary>
    /// <returns>Task</returns>
    private async Task OnRequestSizeChanged()
    {
        await this.OnSourceChanged();
    }

    /// <summary>
    /// RequestSize
    /// </summary>
    public uint? RequestSize
    {
        get { return (uint?)this.GetValue(RequestSizeProperty); }
        set { this.SetValue(RequestSizeProperty, value); }
    }
    #endregion //RequestSize 依存関係プロパティ

    /// <summary>
    /// コンストラクタ
    /// </summary>
    public ThumbnailImage()
    {
        this.InitializeComponent();
    }
}

こんな感じで Source に StorageFile とかが設定された場合はサムネイル画像を内部の Image に描画させるようにしてみました

これで ViewModel にファイルの uri 文字列を持たせるだけで済みますね!