連載
» 2016年10月05日 05時00分 UPDATE

.NET TIPS:Xamarin.Forms:アプリのリソースを指定して画像を表示するには?

Xamarin.Formsアプリに埋め込まれた画像リソースを表示するには、ImageSourceクラスのFromResource/FromStreamメソッドが使える。

[山本康彦,BluewaterSoft/Microsoft MVP for Windows Development]
.NET TIPS
Insider.NET

 

「.NET TIPS」のインデックス

連載目次

対象:Visual Studio 2015以降


 Xamarin.Formsは、XAMLとC#を使ってAndroid/iOS/Windows向けのクロスプラットフォーム開発を行える開発環境だ。

 本稿では、Xamarin.Formsアプリで、アプリに埋め込んだリソースから画像を取得して表示する方法を解説する。

埋め込みリソースを指定して画像を表示するには?

 「リソースID」(後述)を指定して、ImageSourceクラス(Xamarin.Forms名前空間)のFromResourceメソッドを呼び出すとImageSourceオブジェクトが得られる。それをImageコントロール(Xamarin.Forms名前空間)のSourceプロパティにセットすればよい。

 リソースIDは、アセンブリ名(=既定ではプロジェクト名)と、プロジェクト内の画像ファイルのパスとを、「.」でつないだ文字列である。また、画像ファイルの[ビルド アクション]は[埋め込みリソース]と指定してビルドする必要がある。

 ImageSourceクラスのFromResourceメソッドを使って埋め込みリソースの画像を表示するコードは次のようになる。

// XAMLコード側には、「Image1」という名前のImageコントロールが配置してある

// 表示したい埋め込み画像のリソースID
string resourceID = "……省略……";
// 画像ファイルの[ビルドアクション]は[埋め込みリソース]にしておく

// ImageSourceクラスのFromResourceメソッドを使う
Image1.Source = ImageSource.FromResource(resourceID);

埋め込みリソースを指定して画像を表示する例(C#)
コードビハインドではImageSourceクラスのFromResourceメソッドを使うと簡単だ。
なお、ファイル名やURIを指定して画像を表示する場合とは異なり、暗黙の型変換は用意されていない。すなわち、ImageコントロールのSourceプロパティにリソースIDを代入するコードを書いても動作しない。

 別の方法として、リフレクションを使って埋め込みリソースを取り出すこともできる。

 リフレクションによって目的の画像リソースのストリームを取得し、それをImageSourceクラスのFromStreamメソッドに渡せばよい(次のコード)。

// XAMLコード側には、「Image2」という名前のImageコントロールが配置してある

// 表示したい埋め込み画像のリソースID
string resourceID = "……省略……";
// 画像ファイルの[ビルドアクション]は[埋め込みリソース]にしておく

// ImageSourceクラスのFromStreamメソッドを使う
Image2.Source = ImageSource.FromStream(() =>
  // このコードが含まれるアセンブリから画像リソースをストリームとして取り出す
  // (コード冒頭部に「using System.Reflection;」が必要)
  GetType().GetTypeInfo().Assembly.GetManifestResourceStream(resourceID)
);

埋め込みリソースを指定して画像を表示する例(C#)
コードビハインドではImageSourceクラスのFromStreamメソッドを使ってもできる。
FromStreamメソッドの引数には、ストリームを返すラムダ式を与える。ストリームを保持するローカル変数「st」が既に存在する場合は、
Image2.Source = ImageSource.FromStream(() => st); のように書ける。

XAMLで埋め込みリソース画像を表示するには?

 URIやファイル名を指定して画像を表示する場合と同じようにリソースIDを指定できる「ResourceImageSource」クラスのようなものがあればXAMLコードで記述できるのだが、残念ながら用意されていない。

 ないのなら、作ってしまおう。

 IMarkupExtensionインタフェース(Xamarin.Forms.Xaml名前空間)を実装して、次のコードのようなXAMLマークアップ拡張を作る。

using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace dotNetTips1161
{
  [ContentProperty("Source")]
  public class ImageResourceExtension : IMarkupExtension
  {
    // XAMLコードで設定する属性(今回はクラスにContentProperty属性も指定する)
    public string Source { get; set; }

    // IMarkupExtensionインタフェースの実装
    public object ProvideValue(IServiceProvider serviceProvider)
    {
      if (Source == null) return null;
      return ImageSource.FromResource(Source);
    }
  }
}

XAMLで埋め込みリソース画像を表示するためのXAMLマークアップ拡張の例(C#)
クラスの宣言部分に付けてあるContentProperty属性は、XAMLコードでプロパティを明示せずに値を与えたときに代入されるプロパティを表す。その効果は後述する。
使い方は、XAMLコードでSourceプロパティに値をセットする。
実行時には、XAMLコード側でオブジェクトが必要になったときに、ProvideValueメソッドが呼び出される。従って、ProvideValueメソッド内で、SourceプロパティからImageSourceオブジェクト(=XAMLコード側が必要としているオブジェクト)を作り出して返せばよいのだ。
なお、XAMLマークアップ拡張はいろいろ応用が利く。例えば、H/S/Vという3つのプロパティに値を与え、Colorオブジェクトを返すというXAMLマークアップ拡張を作っておく。すると、XAMLコードでColorオブジェクトを必要とする場面で、代わりにそのXAMLマークアップ拡張のオブジェクトを与えて、H/S/V属性で色を指定できる。

 上で作ったImageResourceExtensionクラスを使えば、XAMLコードでリソースIDを指定して画像を表示できる(次のコード)。

<Image Source="{local:ImageResource
                Source=……リソースID省略……}" />
              <!-- リソースID手前の「Source=」は省略可能 -->

リソースIDを指定して画像を表示する例(XAML)
XAMLコード側では、ImageResourceExtensionクラスの末尾「Extension」を書かない。
また、ImageResourceExtensionクラスにContentProperty属性が付けてあるので、リソースIDの手前にある「Source=」は省略できる(次の「実際の例」のXAMLコードを参照)。
なお、ここでは、自動生成されたXML名前空間「local」にImageResourceExtensionクラスが定義してあるものとしている。別の場所に定義した場合は、XML名前空間の宣言を追加し、「local」プレフィックスは追加した名前空間名に変更する。

実際の例

 実際の例として、以上で説明したコードを試してみよう。

 あらかじめ、表示に使う適当な画像ファイルを用意しておいてほしい。以下ではファイル名は「SamplePhoto01.png」としている。

 ソリューションを新しく作るときに[Blank Xaml App (Xamarin.Forms Portable)]を選ぶ。以下ではソリューション名(=PCLプロジェクトの名前)は「dotNetTips1161」としている。

 そのPCLプロジェクトに、「Images」というフォルダーを作り、用意しておいた画像を置き、プロパティの[ビルド アクション]を[埋め込みリソース]にする(次の画像)。

画像をアプリの埋め込みリソースにする(Visual Studio 2015) 画像をアプリの埋め込みリソースにする(Visual Studio 2015)
PCLプロジェクトに画像ファイルを追加したら、プロパティペインで[ビルド アクション]を[埋め込みリソース]にしておく。
[出力ディレクトリにコピー]の欄が隠れてしまっているが、ここは[いいえ]のままにしておく。

 画像ファイルのリソースIDは次のようになる。

  • アセンブリ名(=プロジェクト名)=dotNetTips1161
  • リソース中のフォルダーのパス=Images
  • 画像ファイルの名前=SamplePhoto01.png
  • リソースID=dotNetTips1161.Images.SamplePhoto01.png

 次に、PCLプロジェクトに新しくクラスのファイルを追加する。ファイル名は「ImageResourceExtension.cs」とし(上の画像にもある)、内容を前述した「ImageResourceExtension」クラス(=XAMLマークアップ拡張)に書き換える。

 そうしたら、PCLプロジェクトにあるMainPage.xamlファイルの内容を次のように変更する。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:dotNetTips1161"
             x:Class="dotNetTips1161.MainPage">
  <!--<Label Text="Welcome to Xamarin Forms!"
           VerticalOptions="Center"
           HorizontalOptions="Center" />-->
  <ContentPage.Padding>
    <OnPlatform x:TypeArguments="Thickness" iOS="0,20,0,0" />
  </ContentPage.Padding>
  <ContentPage.Resources>
    <ResourceDictionary>
      <Style TargetType="Image">
        <Setter Property="Aspect" Value="AspectFit" />
        <Setter Property="HeightRequest" Value="60" />
        <Setter Property="VerticalOptions" Value="StartAndExpand" />
      </Style>
      <Style TargetType="Label">
        <Setter Property="HorizontalOptions" Value="Center" />
        <Setter Property="VerticalOptions" Value="EndAndExpand" />
      </Style>
    </ResourceDictionary>
  </ContentPage.Resources>

  <StackLayout>
    <Label Text="Xamlで表示(ImageResourceExtensionを利用)" />
    <Image Source="{local:ImageResource dotNetTips1161.Images.SamplePhoto01.png}" />

    <Label Text="コードで表示(ImageSource.FromResource)" />
    <Image x:Name="Image1" />

    <Label Text="コードで表示(ImageSource.FromStream)" />
    <Image x:Name="Image2" />
  </StackLayout>
</ContentPage>

アプリの埋め込みリソースを指定して画像を表示するサンプルコード(XAML)
自動生成されたLabelコントロールをコメントアウトし、<ContentPage.Padding>要素以降を追加した。
Imageコントロールを3つ配置し、1つ目にはImageResourceExtensionクラスを使ってリソース文字列からImageSourceオブジェクトを作ってImage.Sourceプロパティに与えた。残り2つには「Image1」/「Image2」という名前だけを与えた(後ほど、コードビハインドからSourceプロパティをセットする)。

 次に、コードビハインドのMainPage.xaml.csファイルに次のようなコードを追加する。

public MainPage()
{
  InitializeComponent();

  // 表示したい埋め込み画像のリソースID
  string resourceID = "dotNetTips1161.Images.SamplePhoto01.png";

  // ImageSource.FromResourceメソッドを使う
  Image1.Source = ImageSource.FromResource(resourceID);

  // ImageSource.FromStreamメソッドを使う
  Image2.Source = ImageSource.FromStream(() =>
    // 冒頭に「using System.Reflection;」が必要
    GetType().GetTypeInfo().Assembly
      .GetManifestResourceStream(resourceID)
  );
}

ファイルを指定して画像を表示するサンプルコード(C#)
MainPage.xaml.csファイルのコンストラクタに、「InitializeComponent();」より下の部分を追加した。また、この他に、ファイルの先頭に「using System.Reflection;」という1行を追加している。

 これで実行してみると、次の画像のようになる。

実行結果(Visual Studio Emulator for Android)
実行結果(iOS Simulator for Windows)
実行結果(Mobile Emulator) 実行結果
上はVisual Studio Emulator for Androidでの実行結果。中はiOS Simulator for Windowsでの実行結果。下はMobile Emulator(Windows 10)での実行結果。なお、iOS Simulator for Windowsは本稿執筆段階でプレビュー段階となっている。使用方法については「XamarinアプリのMacでのビルドとiOS Simulator for Windows」を参照されたい。

まとめ

 アプリの埋め込みリソースにある画像をImageコントロールに表示するには、ImageSourceクラスのFromResourceメソッドを使うのが簡単だ。あるいは、同じクラスのFromStreamメソッドを使ってもよい。

 XAMLコードで記述するためには、XAMLマークアップ拡張をC#で書く必要がある。

 画像ファイルを埋め込みリソースにするには、そのプロパティの[ビルド アクション]を[埋め込みリソース]に変える。忘れないようにしよう。

「.NET TIPS」のインデックス

.NET TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。