大晦日なので画像ビューワーを作りましょう(核爆)(錯乱)
完成するとこんな感じになります。 圧縮ファイルの中身を自動展開します。
今回のプロジェクトはこちら。
※「Windowsフォーム」は技術的に古い、みたいな話がネット上に散見されます。 本当に「Windowsフォームで作成すべきか」は一度検討してみてもよいと思います。
事前準備と画面の説明
開発環境は「Win10、Visual Studio 2017」です。
Visual Studioのインストール方法は割愛します。
プロジェクトを作成する
Visual Studioを開いて、右上の「ファイル」→「新規作成」→「プロジェクト」 でプロジェクト作成します。 今回は「Windows フォーム アプリケーション」を選択します。
フォルダを開くためにNugetからダウンロード
今回やることに必要なパッケージをNuGetからDLします。 今回使うのは下記のパッケージです。
- Microsoft.WindowsAPICodePack.Core (フォルダを開くダイアログ)
- Microsoft.WindowsAPICodePack.Shell(フォルダを開くダイアログ)
SharpCompress (圧縮ファイルの操作)
プロジェクト→NuGetパッケージの管理→参照
- パッケージソースを「すべて」にして、検索ボックスで上記のパッケージを検索+DL!
(2017になって微妙にUIが変更されててビビった)
迷ったらDL数が多いものを選んでおけばよいのではないでしょうか。
画面の説明
- 各ウインドウの位置は人によって違うと思います
- ツールボックスが表示されていない人は上バーの「表示」から表示できます。
ここで覚えておいてほしいのは
- ツールボックス
- プロパティ
- イベント
ですです。
デザイナー上でコンポーネントをダブルクリックすると、勝手に関数が作成されます。 害はありませんが、コードが汚染されるので私は嫌いです。
フォルダを選択できるようにする
ツールボックスからコンポーネントを追加
ツールボックスからMenuStrip
を探して、D&Dで追加してください。
追加後、ダブルクリックで項目を追加できます。
ここでは「フォルダを開く」という項目を追加します。
変数名の話
プロパティにて変数名を変更できます。
※GUI表示に使用されるのはText
です。
変更しないとコード内に日本語変数が紛れ込みます。 関数名に日本語が混ざります。 動作上問題はないのですが気持ち悪い・・・
イベント追加
MenuStrip
のイベントClick
に対して、関数を設定します。
Click
を選択してダブルクリックで勝手に関数が作成されます。
フォルダを開く コードの追加
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()をクリック
コンパイル
ここまでいろいろやったのでコンパイルして、動くか確認しておきましょう。
コンパイル後、「フォルダを開く」ボタン→「フォルダ選択ダイアログ」が開き、 選択後にフォームのタイトルが「選択したパス」になっていればOKです。
画像ビューワーに仕立て上げる
ガワはできたので、ここから作りこんでいきます。
ここから説明する関数は全てForm1
クラスのメンバ関数です。また、適宜イベントを設定する必要があります。
コンポーネント追加
下記の部品を追加します。
- ListView
- PictureBox
ListViewはプロパティ「View」を「List」にします(見た目が変わります)。
PictureBoxはプロパティ「SizeMode」を「Zoom」にする(画像の表示方法です)
以下の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フォームも技術的には古いらしいので、他の手法も調査もしないとなぁと思いました。
おしまい。