午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

画像をアプリに埋め込んでアプリで利用するまでの手順メモ

dobon.net 最初はこの方法で試してみたけど、WPF+.NET Coreっていう違いのためかうまくできなくて

dobon.net ここと

smdn.jp ここを参考にしてできた
でも全く同じじゃなくて微妙に違うところもあったのでメモ

環境

埋め込む画像
f:id:gogowaten:20200324101121j:plain
ファイル名は「grayScale.bmp

f:id:gogowaten:20200402162404p:plain
新しくプロジェクト作成したところ
名前は「画像の埋め込みと取得」にした、namespaceもこれになる、今回は普段気にしないnamespaceが重要

画像ファイル埋め込み

f:id:gogowaten:20200402162740p:plain
ソリューションエクスプローラーのアプリの名前を右クリック→追加→既存の項目
または
f:id:gogowaten:20200402173328p:plain
メニューのプロジェクト→既存の項目の追加から

画像ファイルの選択
f:id:gogowaten:20200402162938p:plain
grayScale.bmpを選択して追加

f:id:gogowaten:20200402163106p:plain
ソリューションエクスプローラーで確認できる

ビルドアクションの変更
f:id:gogowaten:20200402163215p:plain
追加したファイル名の右クリックメニューからプロパティを開いて

f:id:gogowaten:20200402163427p:plain
初期状態ではビルドアクションが「なし」になっているので、ドロップダウンメニューから

f:id:gogowaten:20200402163624p:plain
「埋め込みリソース」を選択

f:id:gogowaten:20200402163658p:plain
ビルドアクションが埋め込みリソースになった、これで埋め込みは完了

取り出して表示

MyImageという名前をつけたImage要素に画像を表示する場合

string path = "画像の埋め込みと取得.grayScale.bmp";
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
using (var stream = assembly.GetManifestResourceStream(path))
{
    if (stream != null)
    {
        MyImage.Source = BitmapFrame.Create(stream);
    }
}

ファイルを指定するパスはnamespaceの後ろにピリオドをつけて、その後にファイル名にする
ファイル名はgrayScale.bmpで、namespaceは
f:id:gogowaten:20200402172425p:plain
画像の埋め込みと取得
なのでパスは
"画像の埋め込みと取得.grayScale.bmp"になる
あとはSystem.Reflection.Assemblyクラスでパスを指定するとStreamが得られるので、これをBitmapFrameのCreateに渡せばBitmapSourceが得られる

埋め込んだ画像を取り出して表示したところ
f:id:gogowaten:20200402174102p:plain
できた



階層(フォルダ)をつけて埋め込む

埋め込む画像は
f:id:gogowaten:20200402202244p:plain
ファイル名は「HSVRectHue000.png
これを
f:id:gogowaten:20200402203451p:plain
この状態にする

  • 画像の埋め込みと取得
    • Myフォルダー
      • HSVRectHue000.png

フォルダの追加
f:id:gogowaten:20200402202643p:plain
ファイルを追加した時とほとんど同じ、追加の項目から新しいフォルダーってのがある

f:id:gogowaten:20200402202823p:plain
フォルダの名前は「Myフォルダー」にしたところ

f:id:gogowaten:20200402202905p:plain
フォルダの右クリックメニューから

f:id:gogowaten:20200402202947p:plain

f:id:gogowaten:20200402203035p:plain
フォルダの中に入った

f:id:gogowaten:20200402203056p:plain
これもビルドアクションを、埋め込みリソースに変更したら埋め込み完了

取り出し
MyImageという名前をつけたImage要素に画像を表示する場合

string path = "画像の埋め込みと取得.Myフォルダー.HSVRectHue000.png";
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
using (var stream = assembly.GetManifestResourceStream(path))
{
    if (stream != null)
    {
        MyImage.Source = BitmapFrame.Create(stream);
    }
}

フォルダなしの時と違うのはパスだけ
f:id:gogowaten:20200402203451p:plain

  • 画像の埋め込みと取得
    • Myフォルダー
      • HSVRectHue000.png

これを順番につなげて、つなげる場所にはピリオドをいれて
画像の埋め込みと取得.Myフォルダー.HSVRectHue000.png
これがパスになる

Button2のクリックイベントで実行してみる
f:id:gogowaten:20200402205253p:plain
表示された

コード
MainWindow.xaml

<Window x:Class="画像の埋め込みと取得.MainWindow"
        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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:画像の埋め込みと取得"
        mc:Ignorable="d"
         Title="MainWindow" Height="300" Width="500">
  <Grid>
    <Grid.ColumnDefinitions>
      <ColumnDefinition/>
      <ColumnDefinition Width="150"/>
    </Grid.ColumnDefinitions>
    <Grid>
      <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
        <Image x:Name="MyImage" Stretch="None" UseLayoutRounding="True"/>
      </ScrollViewer>
    </Grid>
    <StackPanel Grid.Column="1">
      <Button x:Name="Button1" Content="button1" Click="Button1_Click"/>
      <Button x:Name="Button2" Content="button2" Click="Button2_Click"/>
      <!--<Button x:Name="Button3" Content="button3" Click="Button3_Click"/>-->
    </StackPanel>
  </Grid>
</Window>


MainWindow.xaml.cs

using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace 画像の埋め込みと取得
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Button1_Click(object sender, RoutedEventArgs e)
        {
            string path = "画像の埋め込みと取得.grayScale.bmp";
            var assembly = System.Reflection.Assembly.GetExecutingAssembly();
            using (var stream = assembly.GetManifestResourceStream(path))
            {
                if (stream != null)
                {
                    MyImage.Source = BitmapFrame.Create(stream);
                }
            }
        }

        private void Button2_Click(object sender, RoutedEventArgs e)
        {
            string path = "画像の埋め込みと取得.Myフォルダー.HSVRectHue000.png";
            var assembly = System.Reflection.Assembly.GetExecutingAssembly();
            using (var stream = assembly.GetManifestResourceStream(path))
            {
                if (stream != null)
                {
                    MyImage.Source = BitmapFrame.Create(stream);
                }
            }
        }

        //未使用
        private void Button3_Click(object sender, RoutedEventArgs e)
        {  
            var bmp = Properties.Resources.HSVRectH90;
            MyImage.Source = BitmapSource.Create(
                256, 256, 96, 96, PixelFormats.Bgra32, null, bmp, 256 * 4);
        }
    }
}


動作を確認

gogowaten.hatenablog.com このときの方法でアプリを発行してできたexeファイルだけを、別のフォルダにコピーして実行してみる。アプリに埋め込まれていれば、どこに移動したりコピーしたりしても表示できるはず

f:id:gogowaten:20200402213709p:plain
Dドライブのブログ用フォルダにコピーして実行

f:id:gogowaten:20200402174102p:plain
f:id:gogowaten:20200402205253p:plain
期待通り!



ファイル名:画像の埋め込みと取得.zip

github.com



失敗例

埋め込もうとした画像ファイルはこれ
f:id:gogowaten:20200402210934p:plain
256x256ピクセルのグラデーション画像

f:id:gogowaten:20200402205531p:plain
アプリのプロパティ画面のリソースの項目、ここで追加した画像ファイルはうまく取得できなかった、その手順

プロパティ画面の表示
f:id:gogowaten:20200402205623p:plain
ソリューションエクスプローラーのアプリの右クリックメニューからプロパティを選択する

または
f:id:gogowaten:20200402205714p:plain
メニューのプロジェクト→プロパティを選択
これで
f:id:gogowaten:20200402205939p:plain
アプリのプロパティ画面になるので、リソースを選択

f:id:gogowaten:20200402205803p:plain
ここをクリックしてくださいってあるからクリック

f:id:gogowaten:20200402210113p:plain
こうなって

f:id:gogowaten:20200402210206p:plain
リソースの追加→既存のファイルの追加

f:id:gogowaten:20200402210242p:plain
画像ファイルを選択して

f:id:gogowaten:20200402205410p:plain
画像が追加された

f:id:gogowaten:20200402205531p:plain
ソリューションエクスプローラーで確認してみると、アプリの下にResourcesというフォルダができていて、その中にファイルが入った状態になっていた

f:id:gogowaten:20200402210446p:plain
これも同じようにビルドアクションを埋め込みリソースに変更

f:id:gogowaten:20200402210513p:plain
ここで一旦ビルド

f:id:gogowaten:20200402210543p:plain
Properties.Resources.まで打つと、入力候補にファイル名が出てくるので、これを選択するんだけど、型がbyte[]になっている。画像ファイルだからBitmapSourceとかになっていればいいんだけどねえ。これでどんなのが得られるのか実行してみたら

f:id:gogowaten:20200402211113p:plain
なんか違う、要素の数が12360しかない。元の画像サイズは256x256ピクセルのカラー画像だから、24bitカラーだとしても25625624/8=196608byte、32bitカラーなら25625632/8=262144だから全然足りない。中に入っている値も画像の左上は黒色(RGB=0,0,0)だから0が並んだ後に1,2,3...って並ぶはずだけどぜんぜん違う。ってことでうまく取得できない。

でもVisual Studio上では普通に開くことができる?
f:id:gogowaten:20200402212308p:plain
ソリューションエクスプローラーのファイル名の右クリックメニューに開くってのがあるから開いてみると

f:id:gogowaten:20200402212415p:plain
普通に開くことができている?よくわからん



関連記事
次回のWPF記事は翌日

gogowaten.hatenablog.com 今回の画像埋め込み使ってみた

前回のWPF記事は4日前

gogowaten.hatenablog.com


2年前
gogowaten.hatenablog.com