gologiusの巣

プログラミング、モデリングなどのメモです。誰かの役に立てるとうれしいです。

画像ビューワーを作る(.NET C# Windows Form)

晦日なので画像ビューワーを作りましょう(核爆)(錯乱)

完成するとこんな感じになります。 圧縮ファイルの中身を自動展開します。

f:id:gologius:20181231142756g:plain

今回のプロジェクトはこちら。

github.com

※「Windowsフォーム」は技術的に古い、みたいな話がネット上に散見されます。 本当に「Windowsフォームで作成すべきか」は一度検討してみてもよいと思います。

事前準備と画面の説明

開発環境は「Win10、Visual Studio 2017」です。

Visual Studioのインストール方法は割愛します。

プロジェクトを作成する

Visual Studioを開いて、右上の「ファイル」→「新規作成」→「プロジェクト」 でプロジェクト作成します。 f:id:gologius:20181231142929p:plain 今回は「Windows フォーム アプリケーション」を選択します。

フォルダを開くためにNugetからダウンロード

今回やることに必要なパッケージをNuGetからDLします。 今回使うのは下記のパッケージです。

  • Microsoft.WindowsAPICodePack.Core (フォルダを開くダイアログ)
  • Microsoft.WindowsAPICodePack.Shell(フォルダを開くダイアログ)
  • SharpCompress (圧縮ファイルの操作)

  • プロジェクト→NuGetパッケージの管理→参照

  • パッケージソースを「すべて」にして、検索ボックスで上記のパッケージを検索+DL!

f:id:gologius:20181231143112p:plain

f:id:gologius:20181231143045p:plain

(2017になって微妙にUIが変更されててビビった)

迷ったらDL数が多いものを選んでおけばよいのではないでしょうか。

画面の説明

f:id:gologius:20181231144230p:plain

  • 各ウインドウの位置は人によって違うと思います
  • ツールボックスが表示されていない人は上バーの「表示」から表示できます。

ここで覚えておいてほしいのは

ですです。

デザイナー上でコンポーネントをダブルクリックすると、勝手に関数が作成されます。 害はありませんが、コードが汚染されるので私は嫌いです。

フォルダを選択できるようにする

ツールボックスからコンポーネントを追加

ツールボックスからMenuStripを探して、D&Dで追加してください。 追加後、ダブルクリックで項目を追加できます。 ここでは「フォルダを開く」という項目を追加します。 f:id:gologius:20181231144840p:plain

変数名の話

プロパティにて変数名を変更できます。

GUI表示に使用されるのはTextです。 f:id:gologius:20181231144909p:plain

変更しないとコード内に日本語変数が紛れ込みます。 関数名に日本語が混ざります。 動作上問題はないのですが気持ち悪い・・・ f:id:gologius:20181231145001p:plain

イベント追加

MenuStripのイベントClickに対して、関数を設定します。 Clickを選択してダブルクリックで勝手に関数が作成されます。 f:id:gologius:20181231145307p:plain

フォルダを開く コードの追加

private void ****ToolStripMenuItem_Click(object sender, EventArgs e) という関数が自動生成されると思います。

下記のコードのように、クリックしたらフォルダ選択ダイアログが表示されるように実装します。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Microsoft.WindowsAPICodePack.Dialogs; //これを追加しないと動かない

namespace Viewer
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void DirOpenToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //「フォルダを開く」ダイアログの設定
            var dialog = new CommonOpenFileDialog();
            dialog.IsFolderPicker = true;  // フォルダーを開く設定にする
            dialog.EnsureReadOnly = true;
            dialog.AllowNonFileSystemItems = false;
            dialog.DefaultDirectory = Application.StartupPath;

            //フォルダを開く
            var Result = dialog.ShowDialog();
            if (Result == CommonFileDialogResult.Ok)
            {
              //開いた後の処理
              this.Text = dialog.FileName; //タイトル名変更
              //updateFileList(select_path); //ファイルリスト更新 ※後程使用します
            }
        }
    }
}

※Form1.csがプログラムとして開けない人は、下記の方法で開いてみてください。

Form1.Designer.cs → Form→Form1()をクリック f:id:gologius:20181231150948p:plain

コンパイル

ここまでいろいろやったのでコンパイルして、動くか確認しておきましょう。

コンパイル後、「フォルダを開く」ボタン→「フォルダ選択ダイアログ」が開き、 選択後にフォームのタイトルが「選択したパス」になっていればOKです。

f:id:gologius:20181231145640p:plain

f:id:gologius:20181231145643p:plain

画像ビューワーに仕立て上げる

ガワはできたので、ここから作りこんでいきます。

ここから説明する関数は全てForm1クラスのメンバ関数です。また、適宜イベントを設定する必要があります。

コンポーネント追加

下記の部品を追加します。

  • ListView
  • PictureBox

f:id:gologius:20181231145720p:plain

ListViewはプロパティ「View」を「List」にします(見た目が変わります)。 f:id:gologius:20181231150047p:plain

PictureBoxはプロパティ「SizeMode」を「Zoom」にする(画像の表示方法です) f:id:gologius:20181231150110p:plain

以下のURLに詳しく記載されています。

PictureBoxコントロールに簡単に画像を表示する - .NET Tips (VB.NET,C#...)

画像ビューワーっぽくなってきましたね。

ファイル一覧を表示

ListViewに、選択したフォルダ内のファイル一覧を表示させます。

//ファイルリストを更新する
private void updateFileList(string path)
{
   var fullpaths = System.IO.Directory.GetFiles(path, "*");
   foreach (string filename in fullpaths)
   {
       listView1.Items.Add(filename);
   }
}

※先ほどのDirOpenToolStripMenuItem_Click()//updateFileList(select_path);コメントアウトを外してください

圧縮ファイルを開いて画像を登録する

ListViewの項目をクリックしたら、圧縮ファイル内の「画像ファイル」のみを読み込むようにします。

※読み込みの待ち時間や、IO負荷などはあまり考慮していません。

IArchive archive = null; //圧縮ファイルの実体
List<IArchiveEntry> imgs = null; //画像ファイル群   
int lookPage = 0; //現在閲覧しているページ

//ファイルリストの項目をクリックした時
 private void listView1_Click(object sender, EventArgs e)
 {
     //項目が一つもない場合
     if (listView1.SelectedItems.Count == 0)
     {
         return;
     }

     //選択している最初の行の、最初の列の値を取得
     ListViewItem item = listView1.SelectedItems[0];
     string path = item.SubItems[0].Text;

     bool result = registerImage(path);
     if (result)
     {
         lookPage = 0;
         showImage(lookPage);
     }
 }

 //指定された圧縮ファイル内の画像を表示する
 private bool registerImage(string path)
 {
     Console.WriteLine(path);
     //初期化
     if (archive != null)
     {
         archive.Dispose();
         archive = null;
     }

     //圧縮ファイルから画像ファイルのみ取り出す
     try
     {
         archive = ArchiveFactory.Open(path);
         var entries = archive.Entries.Where(e =>
             e.IsDirectory == false && (
             Path.GetExtension(e.Key).Equals(".jpg") ||
             Path.GetExtension(e.Key).Equals(".jpeg") ||
             Path.GetExtension(e.Key).Equals(".png") ||
             Path.GetExtension(e.Key).Equals(".bmp")));

         imgs = entries.ToList();
     }
     catch (Exception e)
     {
         MessageBox.Show(path + " " + e.ToString(), "ファイル展開エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);

         if (archive != null)
         {
             archive.Dispose();
         }
         archive = null;
         return false;
     }

     //ソート
     imgs.Sort((a, b) => { return a.Key.CompareTo(b.Key); });

     return true;
 }

画像を表示させる+ページめくり機能をつける

いよいよ、画像表示機能を実装します。 といっても、登録している画像を表示させるだけですが・・・

ついでに、矢印キーでページめくりができるようにしておきます。 これは「ListView」に対して「KeyDown」イベントを設定します

//指定ページの画像を表示する
private bool showImage(int index)
{
    if (imgs.Count() == 0)
    {
        return false;
    }

    //圧縮ファイル内のファイル指定
    var entry = imgs[index];
    try
    {
        //ファイルを読み込みビューワーにセット
        pictureBox1.Image = Image.FromStream(entry.OpenEntryStream());
    }
    catch (Exception e)
    {
        MessageBox.Show(e.ToString(), "正常な画像ファイルではありません", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return false;
    }

    return true;
}

//矢印キーでのページめくり
private void listView1_KeyDown(object sender, KeyEventArgs e)
{
    if (archive == null)
    {
        return;
    }

    if (e.KeyCode == Keys.Left)
    {
        lookPage--;
        if (lookPage < 0)
        {
            lookPage = imgs.Count() - 1;
        }
        showImage(lookPage);
    }
    else if (e.KeyCode == Keys.Right)
    {
        lookPage++;
        if (lookPage >= imgs.Count())
        {
            lookPage = 0;
        }
        showImage(lookPage);
    }
}

全てのソースを合体させると、下記のようになります

Viewer/Form1.cs at master · gologius/Viewer · GitHub

まとめ

画像ビューワーを作成しました。

特に SharpCompress の使い方が分からず、最初は苦労しました。

常に圧縮ファイル内の全画像を持つようなコードになっているので、 負荷が気になるところです(といっても私のPCだと全く問題ありませんが・・・)

また、Windowsフォームも技術的には古いらしいので、他の手法も調査もしないとなぁと思いました。

おしまい。

github.com

f:id:gologius:20181231142756g:plain