Visual Basic 初級講座
VB.NET 2002 対応 VB.NET 2003 対応 VB2005 対応

 

Visual Basic 中学校 > 初級講座 >

第38回 実技3 ファイル一覧の印刷

フォルダ・ファイルの一覧を表示・印刷・保存するプログラムを実際に作成する手順を紹介します。VBを起動して実際に手順どおりに作成してみてください。

概要

・フォルダ・ファイルの一覧を表示・印刷・保存するプログラムを作成する。

 

1.目標

今回は実際のアプリケーションを作成しながらVBの技術の応用方法を学んでいただくことが目的です。初級講座では3回目の実技となります。

実技の場合は、どの場合でもあてはまるのですが細かいテクニックやキーワードに惑わされないでアプリケーション作成の全体的な流れを体でつかんでいくことが目標です。

極端なことを言えば知らない関数や構文が登場しても深く考えない方が良いです。ほとんどが説明どおりのコピー&貼り付けになってしまったとしても自分の力で1つのアプリケーションを完成させることの方が今回は重要です。

今回作るソフトはフォルダ内のファイル・フォルダの一覧を表示・保存・印刷するソフトです。Windowsで普段使っているエクスプローラはフォルダをダブルクリックするなどしてどんどん一覧を表示させていくことができますが、表示されている一覧を 保存・印刷する機能がありません。

しかし、ファイルの一覧をちょっと保存・印刷しておきたいという場合もあります。そんな場合に使えるソフトを目指します。

■画像1:完成版

印刷機能は印刷とプレビューの両方をプログラムします。

■画像2:プレビュー画面

印刷物の右上にはページ数も出ます。

保存機能は表示されている一覧をcsv形式で保存する機能です。csv形式で保存したファイルはExcel等で開くことができます。

■画像3:CSV形式で保存したファイルをExcelで開いたところ

さらに複数のフォームを使うプログラムを紹介したかったので、表示設定を行う別画面もつけることにしました。

■画像4:設定画面

本来はこのような設定は[表示]メニューで該当項目をチェックしたりチェックをはずしたりするだけでできるのですが、今回は練習ということであえて専用のフォームを作成します。

もちろん、このフォームで行った設定は設定ファイルに保存されて、次回起動したときは自動的に設定が復元されます。

 

エクスプローラ形式のフォルダ情報一覧ソフトについては初級講座第25回の実技2 フォルダ情報一覧で既に説明していますから、同じようなものを作ることはせずに印刷機能の方に重点をおきます。念のために書いておきますが初級講座第25回の実技2を読んでいない方でも 今回ソフトを作るのに問題のないように説明します。

ところで、今回は完成版をダウンロードできるようにしていないので完成版を試したい方は自分で作るしかありません。それなりに四苦八苦すると思いますが良い経験になると信じています。ダウンロードを用意していないのはいじわるをしているわけではなく私が大変だからです。今回のプログラムはほんの少しだけですがバージョンによってプログラムを変えなければいけない部分があるので、ダウンロードを用意するとなるとVB.NET2002用とVB.NET2003用とVB2005用の3つを用意しなければならないのです…。

 

2.画面設計

 

早速画面設計から行いましょう。画面は意外とシンプルな配置になります。

まずは、「FileListViewer」という名前で新しいプロジェクトを作成してください。もちろんいつものようにWindowsアプリケーションです。

フォームには以下のコントロールを配置します。説明しながら配置していくのでまだプロパティは設定しないで下さい。

コントロール プロパティ
frmMain (フォーム) AllowDrop True
Text フォルダ・ファイル一覧
txtFolderName (TextBox) Anchor Top, Left, Right
Text "" (空にする)
btnFolderName (Button) Anchor Top, Right
Text ...
lvFiles (ListView) Anchor Top, Bottom, Left, Right
SmallImageList ImageList1
View Details
MenuStrip1
またはMainMenu1 (※1)
   
FolderBrowserDialog1
(※2)
Desription フォルダを選択してください。
ShowNewFolderButton False
ImageList1    
PrintDocument1    
PrintPreviewDialog1 Document PrintDocument1

■表1:配置するコントロールの一覧

※1 VB2005ではMenuStripコントロールを配置します。VB.NET2002またはVB.NET2003の場合はMainMenuコントロールを配置します。

※2 VB.NET2002の場合は何も配置しません。VB.NET2002にはFolderBrowserDialogコントロールがないのです。

■画像5:コントロールの配置イメージ。メニュー部分は後で説明する。

 

最初にMenuStrip(VB.NET2003以前ではMainMenu)を配置してください。これはフォーム上には直接貼りつかないでフォームの下のコンポーネントトレイと呼ばれる部分に貼り付きます。ただし、MenuStrip(またはMainMenu)はフォームの上の方にメニュー表示用の領域が追加表示されます。 本当は後から配置してもいいのですがやはりフォームの大きな見かけに関わる部分は先に配置して全体的なイメージをつかむというのが私のスタンスです。

メモMainMenuの場合、つまりVB.NET2002かVB.NET2003を使用している場合は、メニュー項目を追加するまではメニュー用の領域は表示されません。

他にもFolderBrowserDialog1(VB.NET2002では無視)とImageList1PrintDocument1PrintPreviewDialog1はコンポーネントトレイに貼りつきますがこちらは実際にフォーム上に表示されるわけではないのでいつでも好きな順番で配置して構いません。 実際に自分でプログラムする際には後になってからコントロールが必要になることもよくあります。とはいえ、今回は設定がはっきりしているので先にどんどん配置してしまいましょう。

ImageList1に関してはImagesプロパティにフォルダのアイコンを設定する必要があります。次のアイコンをダウンロードしてImagesプロパティにCLSDFOLD.ICOを設定してください。設定方法はわかると思いますが、念のために簡単に説明しますと、ImageList1をクリックしてプロパティウィンドウでImagesのところをクリックすると表示される「...」ボタンをクリックし、そこで表示されるダイアログにダウンロードした画像(アイコン)を追加することになります。

アイコン 767B lha形式(拡張子lzh)

■画像6:ImageListにダウンロードしたアイコンを追加する。

ダウンロードファイルはどこにおいても構いません。設定が済んだら消してしまっても大丈夫です。 なお、ダウンロードファイルには余分なアイコンがもう1つ入っていますが今回は使用しません。

 

さて、 実際にフォーム上に配置されるのはtxtFolderNamebtnFolderNamelvFilesの3つです。この3つを見栄えが良いようにきっちり配置してください。最初が肝心です。後できっちりさせるのではなく今きっちりと配置することをお勧めします。

とは言ってもマウスで操作するとなかなかきっちりといかない場合があります。

■画像7:テキストボックスとボタンの間に微妙な隙間が…

このくらいの隙間は気にしないという考え方もありますが、どうしてもぴったり合わせたい場合は細かい数値はプロパティウィンドウを使って直接数値入力するとよいでしょう。

■画像8:マウスで調節できない部分はプロパティウィンドウを使って数値で指定可能

ところで、このように手動で配置するのは原始的なように思われるかもしれませんね。確かにもっとコントロールやプロパティを駆使して自動的にぴったりと配置することもできますし、プログラムを記述してきちんと整列させることもできます。 今回はフォーム上のコントロールが3つだけなのでこのような方法をとりました。

 

配置が終わったら他のすべてのプロパティを上の表の通りに設定してください。Anchorプロパティを設定することで実行時にユーザーがフォームのサイズを変更してもコントロールの配置が乱れないようになります。

完了したらこの実際に実行してみて確認してください。フォームを大きくしても小さくしても3つのコントロールはフォームの大きさにしたがって自動的に伸縮自在に再配置されるはずです。Anchorプロパティを設定していない場合はこうはいきません。

次のステップに進む前にすべてのコントロールが表の通りのプロパティで配置されているか確認してください。もしまだなら配置してプロパティを設定してください。フォームの名前をfrmMainにするのも忘れないように気をつけてください。

 

3.メニューの実装

 

次にメニューをつけます。メニューとはよくアプリケーションの上の方に並んでいる「ファイル(F)」とか、「編集(E)」、「表示(V)」といった類のものです。いままで初級講座ではメニューの作成についてはまったくふれませんでしたが、ほとんどのアプリケーションにはメニューがついているのにあなたが作ったアプリケーションだけメニューがついていないというのは考え物です。

他のアプリケーションと操作感を統一できればユーザーにも便利なはずですから積極的にメニューの実装を検討しましょう。

今回のソフトでは次の通り「ファイル」、「表示」、「ヘルプ」の3つのメニューを実装します。

■画像9:メニューの完成イメージ

メニューを作成する手順はすぐ後で説明しますが先に完成図をご覧いただきましょう。

各メニューは次のようになります。ファイルメニューには保存や印刷など今回のメイン機能が並びます。

■画像10:ファイルメニュー ■画像11:表示メニュー ■画像12:ヘルプメニュー

 

それでは、実際のメニュー作成方法を説明しましょう。 まずは、コンポーネントトレイ上でMenuStrip1(またはMainMenu1)をクリックしてください。そうするとフォーム上のメニュー用の領域に1つ入力欄が表示されます。

■画像13:まだ何もメニューがない状態

 

ここに「ファイル(&F)」と入力してください。

■画像14:ファイルメニューの作成

 

続いてその下に「名前を付けて保存(&A)...」と入力します。

■画像15:メニュー項目の作成

 

「&」の意味

Textプロパティに「&」とアルファベットを入力するとショートカットキーを設定したことになります。

たとえば、ボタンのTextプロパティに「閉じる(&C)」と設定した場合、プログラム実行中に[Alt] + [C]を押すことでこのボタンをクリックすることができます。表示上は「&C」の部分はアンダーバーがついて表示されるので「閉じる(C)」に見えます。

日本語のアプリケーションでは文字列の後ろにかっこつきでショートカットキーを指定するのが普通ですが、ショートカットキーの指定自体はお勧めはしませんがどの位置で行っても構いません。英語のアプリケーションでは「File」や「Tool」のように先頭の文字をショートカットキーにする習慣があります。

 

次には「印刷」なのですが、「名前を付けて保存」と「印刷」の間に区切り線を入れたいので先に区切り線を挿入します。

VB2005の場合 区切り線を挿入するには「ここへ入力」というところにマウスを持っていってください。2秒くらい待つと画像で赤い四角で囲ってあるようなドリルダウンの「▼」がでてきますからクリックしてください。

■画像16:VB2005でのセパレータの挿入1

そうすると、この位置にセットすることができるアイテムの一覧が表示されます。ここでは区切り線を表すSeparator(読み方:Separator = セパレーター)を選択してください。これで区切り線のセットは完了です。

ちなみに普通のメニューはこの一覧の中でMenuItem(読み方:MenuItem = メニューアイテム)に当たります。

■画像17:VB2005でのセパレータの挿入2

VB.NET2002, VB.NET2003の場合は、「ここへ入力」の部分を右クリックして[区分線を挿入]を選択すると区切り線を挿入することができます。

■画像18:VB.NET2003での区分線の挿入

この調子で画像を参考にファイルメニューを完成させてください。

■画像19:ファイルメニュー完成

ファイルメニューが完成したら隣に表示メニューを入力します。

■画像20:表示メニュー作成

後は同じような手順で表示メニューとヘルプメニューを完成させてください。

 

メニューが完成したらプロパティを設定します。これらのメニューの項目はそれぞれが1つのコントロールですのでクリックするとプロパティウィンドウにちゃんとプロパティが表示されます。

まずは、最初に入力した「ファイル(&F)」をクリックしてください。そうするとプロパティウィンドウにはこのメニュー項目のプロパティが表示されますから、そのままNameプロパティに「mnuFile」と設定してください。

この調子ですべてのメニューのプロパティを次の通りに設定してください。

メニュー項目 プロパティ
ファイル(&F) Name mnuFile
名前を付けて保存(&A)... Name mnuFileSaveAs
ShortcutKeys Ctrl + S
印刷(&P) Name mnuFilePrint
ShortcutKeys Ctrl + P
印刷プレビュー(&V) Name mnuFilePreview
終了(&X) Name mnuFileEnd
表示(&V) Name mnuView
表示設定(&S)... Name mnuViewSettings
最新の情報に更新(&R) Name mnuViewRefresh
ShortcutKeys F5
ヘルプ(&H) Name mnuHelp
バージョン(&V)... Name mnuHelpVersion

■表2:メニューの設定

ほとんどが名前の設定です。

プロパティの設定が終わったら 練習代わりに1つメニューにプログラムしてみましょう。ファイルメニューの「終了(&X)」をダブルクリックしてください。見慣れたイベントプロシージャが表示されるはずです。

ここにアプリケーションを終了させるためのコードを書きます。以下の通り記述してください。

VB.NET 2002 対応 VB.NET 2003 対応 VB2005 対応

Private Sub mnuFileEnd_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuFileEnd.Click

    Me.Close()

End Sub

■リスト1:終了メニュー

もう1つバージョンの表示をプログラムしましょう。ここはお好みでよいのですが、とりあえずプログラム例を掲げておきます。

VB.NET 2002 対応 VB.NET 2003 対応 VB2005 対応

Private Sub mnuHelpVersion_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuHelpVersion.Click

    Dim St As String

    St = Application.ProductName & " " & Application.ProductVersion
    St &= vbNewLine & vbNewLine
    St &= Application.CompanyName

    MsgBox(St, MsgBoxStyle.Information)

End Sub

■リスト2:バージョンメニュー

博士のワンポイントレッスン
V太:博士〜 。メニューの設定がやりにくです。すごく。なかなか説明どおりのメニューになりません。
博士:なんじゃと?そんなはずはないのじゃが・・・? VBのメニューエディターは使いやすいようによく工夫されていると思うぞ。
なんか、でもマウスでクリックしたときの反応が妙な気がして…。あせって、ダブルクリックするとプログラム画面に切り替わっちゃうし。それに間違って入力しちゃったら削除したり入力しなおしたりが大変です。
B子:慣れるしかないわね。
うむ。慣れるしかないのぉ。
私にとってはとても使いやすいわ。

 

 

4.フォルダ選択の実装

 

メニューが完成したら、次にファイル一覧を表示する対象のフォルダを選択する機能をプログラムします。ユーザーがテキストボックスにフォルダ名を入力して[ENTER]キーを押すかフォーカスを移動するとそのフォルダ内のフォルダとファイルの一覧が表示されるわけですが、いちいちフォルダ名を入力するのでは面倒でなりません。そんな面倒なソフトはおそらく使ってもらえないでしょう。

今回我々が作るソフトではユーザーが自分でフォルダ名を入力する以外に次の2つの方法でフォルダを指定できるようにします。

1つ目はドラッグ&ドロップです。ユーザーがフォルダをドロップするとそのフォルダ内のファイルまたはフォルダが指定されたとみなします。

注意 注意!VB2005とWindows Vistaの組み合わせでは今回はドラッグ&ドロップできません。

この記事はVB2005+Windows XPの環境で書いたものなのですが、Windows Vistaで同じことをやってもドラッグ&ドロップが機能しません。VB2008+Windows Vistaならおそらく大丈夫なはずですが未検証です。VB2005のExpress Editionを使用している方は、これを機にVB2008のExpress Editionに乗り換えてみませんか?

2つ目はフォルダ選択ダイアログです。テキストボックスの右側にある「...」ボタンを押すことでフォルダ選択用のダイアログを表示し、このダイアログからもフォルダを選択できるようにします。VB.NET2002を使用している場合は今回はこの機能はプログラムしません。

このようにユーザーの便宜のために同じことでも複数の手段を用意しておくのはよくあることです。一般的には最低でもマウスだけを使った操作とキーボードだけを使った操作の2種類の 操作が可能なようにプログラムしておくべきだと言われています。マウス派のユーザーとキーボード派のユーザーがいるからです。

とは言っても同じ処理を何回もプログラムするのでは今度はプログラムが煩雑になってしまいますから、実際にフォルダとファイルの一覧を読み込む部分と表示する部分は それぞれ1つのメソッドにまとめて各個所からはそれを呼び出すようにします。

まずは、この読み込みと表示を担当する空のメソッドを宣言しておきます。

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■ReadFileList
''' <summary>ファイル・フォルダの情報を読み込む</summary>
Private Sub ReadFileList(ByVal FolderName As String)

    MsgBox(FolderName)

End Sub

'■ShowFileList
''' <summary>ファイル・フォルダの情報の一覧を表示する</summary>
Private Sub ShowFileList()

End Sub

■リスト3:空のメソッドを用意する

空とは言いながらReadFileListメソッドには引数とメッセージボックスをプログラムしておきました。ファイル・フォルダの情報を読み込む対象のフォルダを指定する必要があるのでFolderName引数は必要だからです。メッセージボックスはテストでこのメソッドを 呼んだときに正しくフォルダ名が設定されているか確認するためです。

このようにプログラム途中では将来必要になるメソッドや引数をなんとなく空のままで宣言しておくことはよくあります。このようにしておくことでだんだんと全体の骨格が見えていくことになります。もちろん、このような空の宣言は後で修正しなければならないことも多いのですが、今回は宣言についてはこれ以上手を加える必要はありません。

さて、それではフォルダがドロップされた場合の処理を記述します。フォームのAllowDropプロパティがTrueに設定されていることを確認してから次の通りプログラムしてください。

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■frmMain_DragEnter
''' <summary>フォルダのドロップに応答する</summary>
Private Sub frmMain_DragEnter(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles MyBase.DragEnter

    'ドラッグされている内容がファイルである場合
   
If e.Data.GetDataPresent(DataFormats.FileDrop) Then
       
'コピーを許可するようにドラッグ元に通知する
       
e.Effect = DragDropEffects.Copy
    End
If

End Sub

'■frmMain_DragDrop
''' <summary>フォルダのドロップに応答する</summary>
Private Sub frmMain_DragDrop(ByVal sender As Object, ByVal e As System.Windows.Forms.DragEventArgs) Handles MyBase.DragDrop

    Dim Name As String

    'ドロップされた内容を取得

    Name = e.Data.GetData(DataFormats.FileDrop)(0)

    If IO.Directory.Exists(Name) Then
       
'ドロップされたのがフォルダならばそのまま表示する
       
txtFolderName.Text = Name
   
Else
       
'ドロップされたのがファイルならば親フォルダを表示する
       
txtFolderName.Text = IO.Path.GetDirectoryName(Name)
    End
If

    '▼フォルダとファイルの一覧を表示
   
ReadFileList(txtFolderName.Text)
    ShowFileList()

End Sub

■リスト4:ドロップによるフォルダの指定

この部分はこれで完成です。試しにいろいろなファイルやフォルダをドロップしてみてください。正しく表示できることが確認できます。

次にフォルダ選択ダイアログを使ったフォルダの指定をプログラムします。VB.NET2002にはフォルダ選択ダイアログがないので残念ながらこの機能は実装できません。実装する方法がないわけでもないのですがどちらにせよ手軽にプログラムすることはできません。

この部分はVB2005またはVB.NET2003の方だけプログラムしてください。次の通りです。

VB.NET2003対応 VB2005対応

'■[フォルダ選択]ボタン
Private Sub btnFolderName_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnFolderName.Click

    If FolderBrowserDialog1.ShowDialog = Windows.Forms.DialogResult.OK Then
       
txtFolderName.Text = FolderBrowserDialog1.SelectedPath
       
'フォルダとファイルの一覧を表示
       
ReadFileList(txtFolderName.Text)
        ShowFileList()
    End
If

End Sub

■リスト5:フォルダ選択ダイアログによるフォルダの指定

これだけで済むのですから便利になったものです。VB6やVB.NET2002の時代はこのフォルダ選択ダイアログにはなかなかてこずらされたものでした。

最後にユーザーが自分でフォルダ名を入力して[ENTER]キーを押すか、またはフォーカスを移動した場合のプログラムをします。この場合はユーザーがまちがったフォルダ名を入力してしまうかもしれないのでフォルダ名が正しいかどうかのチェックが必要となります。次の通りです。

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■[フォルダ名]テキストボックス
''' <summary>[ENTER]キーが押されたらフォーカスを移動する。</summary>
Private Sub txtFolderName_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles txtFolderName.KeyPress

    If Asc(e.KeyChar) = Keys.Enter Then
       
Me.SelectNextControl(txtFolderName, True, True, True, True)
        e.Handled =
True
   
End If

End Sub

'■[フォルダ名]テキストボックス
''' <summary>正しいフォルダ名が入力されていたらフォルダ一覧を表示する。</summary>
Private Sub txtFolderName_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtFolderName.Leave

    If Len(Trim(txtFolderName.Text)) > 0 Then

        '末尾に\を付加
       
If Strings.Right(txtFolderName.Text, 1) <> "\" Then
           
txtFolderName.Text &= "\"
       
End If

        'フォルダとファイルの一覧を表示
       
If IO.Directory.Exists(txtFolderName.Text) = True Then

            ReadFileList(txtFolderName.Text)
            ShowFileList()

        Else

            Dim St As String

            lvFiles.Clear()

            '存在しない場合
           
St = "存在するフォルダ名を正しく入力してください。" & vbNewLine
            St &= vbNewLine
            St &=
"・フォルダをドロップして指定することもできます。" & vbNewLine
            St &=
"・右側のボタンをクリックしてフォルダ選択画面を利用することもできます。" & vbNewLine
            MsgBox(St, MsgBoxStyle.Exclamation)

        End If

    Else

        lvFiles.Clear()

    End If

End Sub

■リスト6:テキストボックスへの入力によるフォルダの指定

KeyPressイベントとLeaveイベントだけでプログラムしてしまいました。最初はValidatingイベントを活用しようと思ったのですが、Enterキーを押したときのチェックとValidatingイベントでのチェックをかみ合わせるのがなかなか骨だったので止めました。Validatingイベントは使いにくいです。

なお、テキストボックスが空にされるか、存在しないフォルダ名が指定された場合ファイル・フォルダの一覧も空になるようにlvFiles.Clearを呼び出しています。

 

5.設定画面

 

次に設定画面と設定の保存・復元をプログラムします。フォルダを指定できるようになったのですぐにでもファイルの一覧を表示する部分をプログラムしたくなることとは思いますがもう少し我慢してください。今回のソフトは表示設定によってファイル一覧に表示する内容が変るようにしますから、どうしても設定周りのプログラムを先に済ます必要があるのです。

先に、この表示設定がどのような機能が詳しく説明しておきます。

このソフトではファイル・フォルダの一覧を表示・保存・印刷する機能がありますが、この「一覧」に何を含めるかが表示設定となります。

よくあるのは名前、サイズ、更新日時ですね。名前は必ず一覧に含めると思うので、その他の部分は一覧に含めるか含めないかユーザーが選択できるようにします。たとえば、作成日時は普通は必要ないですが何かの事情で作成日時を一覧で見たいユーザーもいることでしょう。また、「隠しファイル」や「読み取り専用」といった「属性」も見たい場合があるかもしれません。

そこで、次の4つの要素に関しては一覧に含めるか含めないか設定画面を使ってユーザーが指定できるようにします。

■画像21:設定画面

この機能を装備するためには、上記のようなフォームと、設定を記録しておくためのクラス、それに設定をファイルに保存したりファイルから設定を読み込む仕組みが必要となります。

まずは、設定を記録しておくためのクラスから作成します。Settings.vbというクラスをプロジェクトに追加してください。

クラスの追加の仕方は大丈夫ですよね。ソリューションエクスプローラでプロジェクトを右クリックして[追加] - [クラス]でOKです。

他の方法もありますが、とにかく追加するときには下の画面が表示されます。

■画像22:クラスの追加

ここでテンプレートから「クラス」を選択し、ファイル名に「Settings」と入力してから[追加]ボタンをクリックしてください。

■画像23:ソリューションエクスプローラ上のクラス

追加するとソリューションエクスプローラにちゃんとそのクラスが表示されます。

このクラスをダブルクリックして次の通りプログラムしてください。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Public Class Settings

    Public Shared IsSize As Boolean                   'サイズを表示するか
   
Public Shared IsLastWriteTime As Boolean          '更新日時を表示するか
   
Public Shared IsCreationTime As Boolean           '作成日時を表示するか
   
Public Shared IsAttributes As Boolean             '属性を表示するか
   
Private Const FileName As String = "Settings.txt" '設定ファイル

    '■Read
   
''' <summary>設定ファイルから設定を読み込む</summary>
   
Public Shared Sub Read()

        Dim Reader As New IO.StreamReader(Application.StartupPath & "\" & FileName)

        IsSize = CBool(Reader.ReadLine)
        IsLastWriteTime =
CBool(Reader.ReadLine)
        IsCreationTime =
CBool(Reader.ReadLine)
        IsAttributes =
CBool(Reader.ReadLine)

        Reader.Close()

    End Sub

    '■Write
   
''' <summary>設定ファイルに設定を書き込む</summary>
   
Public Shared Sub Write()

        Dim Writer As New IO.StreamWriter(Application.StartupPath & "\" & FileName)

        Writer.WriteLine(CStr(IsSize))
        Writer.WriteLine(
CStr(IsLastWriteTime))
        Writer.WriteLine(
CStr(IsCreationTime))
        Writer.WriteLine(
CStr(IsAttributes))

        Writer.Close()

    End Sub

    '■Exists
   
''' <summary>設定ファイルが存在するか確認する</summary>
   
''' <returns>Trueの場合設定ファイルは存在する</returns>
   
Public Shared Function Exists() As Boolean

        Return IO.File.Exists(Application.StartupPath & "\" & FileName)

    End Function

    '■SetDefault
   
''' <summary>設定を既定の設定にセットする</summary>
   
Public Shared Sub SetDefault()

        IsSize = True
       
IsLastWriteTime = True
       
IsCreationTime = False
       
IsAttributes = True

    End Sub

End Class

■リスト7:クラスのプログラム。設定内容の記録、読み込み、保存等の機能。

このクラスでは4つの変数を使って設定を記録します。たとえば、ファイル・フォルダの一覧にサイズを含める場合は変数IsSize = Trueとなります。逆にIsSize= Falseの場合はサイズは含めないという意味になります。

もちろん、このクラスは設定を記録しておくだけですから実際にサイズを含ませたり含ませなかったりするためには一覧を表示・保存・印刷する部分でプログラムしなければなりません。そのプログラムはもう少し後で登場します。

他の3つの変数IsLastWriteTime, IsCreationDate, IsAttributesも同じようにそれぞれ更新日時、作成日時、属性の表示設定を記録します。

このSettingsクラスには設定を変数に記録するだけではなく、設定ファイルに書き込んだり、設定ファイルから設定を読み込む機能もつけました。このクラス自体は特に難しいことはやっていないのでプログラムをざっとみていただければわかると思います。他に設定ファイルの存在を確認するExistsメソッドと、デフォルトの設定を行うSetDefaultメソッドをもあります。

 

次はいよいよこれら設定を行うフォームを作成します。frmSettings.vbという名前で新しいフォームをプロジェクトに追加してください。

■画像24:フォームの追加

今度は追加するとフォームのアイコンでソリューションエクスプローラに表示されます。

■画像25:ソリューションエクスプローラ上のフォーム

このフォームはかなり単純なフォームでチェックボックスが4つとボタンが2つあるだけです。少し上にある画像を参考にコントロールを配置してください。プロパティは次の表の通り設定してください。

コントロール プロパティ
frmSettings (Form) Text 表示設定
AcceptButton btnOK
CancelButton btnCancel
MaximizeBox False
MinimizeBox False
FormBorderStyle FixedDialog
chkSize (CheckBox) Text サイズ
chkLastWriteTime (CheckBox) Text 更新日時
chkCreationTime (CheckBox) Text 作成日時
chkAttributes (CheckBox) Text 属性
btnOK Text OK
DialogResult OK
btnCancel Text キャンセル
DialogResult Cancel

■表3:配置するコントロールの一覧

プログラムもいたって単純です。先に用意しておいたSettingsクラスを読み書きするだけです。次のようになります。

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■フォーム
Private Sub frmSettings_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    Call ReadSettings()

End Sub

'■[OK]ボタン
Private Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnOK.Click

    Call WriteSettings()
    Settings.Write()

End Sub

'■ReadSettings
''' <summary>現在の設定を画面に表示する</summary>
Private Sub ReadSettings()

    chkSize.Checked = Settings.IsSize
    chkLastWriteTime.Checked = Settings.IsLastWriteTime
    chkCreationTime.Checked = Settings.IsCreationTime
    chkAttributes.Checked = Settings.IsAttributes

End Sub

'■WriteSettings
''' <summary>画面の内容を設定として記録する</summary>
Private Sub WriteSettings()

    Settings.IsSize = chkSize.Checked
    Settings.IsLastWriteTime = chkLastWriteTime.Checked
    Settings.IsCreationTime = chkCreationTime.Checked
    Settings.IsAttributes = chkAttributes.Checked

End Sub

■リスト8:設定フォームのプログラム。Settingsクラスの内容を読み書きするだけのきわめてシンプルなもの。

[OK]ボタンをクリックしたときにSettingsクラスのWriteメソッドを呼び出してファイルに設定を書き込む点が唯一の注意点でしょう。

 

後はメインのフォームからこの設定画面を読み込めるようにします。まずは[表示]メニューの[表示設定]メニューをダブルクリックして次のようにプログラムしてください。(frmMainの方です。frmSettingsではありません。)

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■[表示] - [表示設定]メニュー
Private Sub mnuViewSettings_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuViewSettings.Click

    Dim f As New frmSettings

    If f.ShowDialog() = Windows.Forms.DialogResult.OK Then
       
'▼フォルダとファイルの一覧を表示
       
ReadFileList(txtFolderName.Text)
        ShowFileList()
    End
If

End Sub

■リスト9:設定画面の呼び出し

このプログラムではShowDialogメソッドを使って設定画面のフォームを呼び出します。ShowDialogメソッドで呼び出すと設定画面で[OK]ボタンが押されたのか[キャンセル]ボタンが押されたのか戻り値で判断することできます。この戻り値はfrmSettings.vbでボタンのDialogResultプロパティに設定した値になります。

ここではOKがクリックされたときだけファイル・フォルダの一覧を読み込んで表示するようにしています。

VB2005の場合

上記のコードはVB2005では少しだけ楽に書くことができます。自分でインスタンスを作成しなくてもフォームを呼び出せる機能が追加されているからです。VB2005では次のような記述が可能です。

VB2005対応

'■[表示] - [表示設定]メニュー
Private Sub mnuViewSettings_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuViewSettings.Click

    If frmSettings.ShowDialog() = Windows.Forms.DialogResult.OK Then
       
'▼フォルダとファイルの一覧を表示
       
ReadFileList(txtFolderName.Text)
        ShowFileList()
    End
If

End Sub

■リスト10:VB2005では設定画面の呼び出しをこのように書くこともできる。

 

プログラムを起動した一番最初の時にも設定を読み込む必要がありますからフォームのLoadイベントには次のようにプログラムしてください。

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■frmMain_Load
Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

    If Settings.Exists Then
       
'設定ファイルが存在する場合
       
Settings.Read() '設定ファイルを読み込む
   
Else
       
'設定ファイルが存在しない場合
       
Settings.SetDefault() '既定の設定を利用する
   
End If

End Sub

■リスト11:プログラム開始時に設定を読み込む

設定ファイルが存在する場合には読み込みを、存在しない場合にはデフォルト設定を利用するようにします。

 

6.一覧の読み込み

 

これで準備万端です。後はフォルダ・ファイルの一覧を読み込んで、表示・印刷・保存するというメイン機能が残っているのみです。

読み込みについては既に空のReadFileListメソッドを作成してありますし、表示についても空のShowFileListメソッドが作成してあります。 この空のメソッドをいよいよ完成させます。

まずは読み込みを行うReadFileListメソッドです。読み込んだ一覧の情報は表示・印刷・保存の3箇所で使用するので後で使用しやすいようにコレクションとして保存しておくことにします。このコレクションはFilePropsという名前にします。

また、一覧の情報は単にファイルの名前だけでなくサイズや更新日時、属性などさまざまなものがあります。そこで一覧の情報をコンパクトに管理できるFileProp構造体を作成します。

ここまでで次の通りになります。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim FileProps As New ArrayList 'ファイル・フォルダの情報の一覧
'Dim FileProps As New Generic.List(Of FileProp) '←VB2005の場合はこちらの方が高性能

'ファイル・フォルダの情報
Private Structure FileProp
    Public Name As String
       '名前
   
Public Size As Long          'サイズ
   
Public LastWriteTime As Date '更新日時
   
Public CreationTime As Date  '作成日時
   
Public Attributes As String  '属性
   
Public IsFolder As Boolean   'フォルダか
End Structure

■リスト12:ファイル・フォルダの情報一覧を記録するための各種宣言

なお、コメントとして書いておきましたがVB2005で作成する場合はFilePropsコレクションの宣言をNew Generic.List(Of FileProp)とした方が高性能になります。FilePropという型が明示されているからです。New ArrayListと宣言した場合はコレクションに格納する型が明示されていないのでその分オーバーヘッドが発生するし、プログラム上ももやもやします。

ただし、Generic名前空間はVB2005で新しく導入されたためVB.NET2002, VB.NET2003を使用している場合はここはNew ArrayListと宣言するのがよいでしょう。

さて、これらに情報を格納するReadFileListメソッドは次のようになります。

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■ReadFileList
''' <summary>ファイル・フォルダの情報を読み込む</summary>
''' <param name="FolderName">対象のフォルダのフルパス</param>
''' <remarks>読み込んだ情報はFilePropsコレクションに蓄えられる</remarks>
Private Sub ReadFileList(ByVal FolderName As String)

    Dim ThisFolder As New IO.DirectoryInfo(FolderName)
    Dim oFolder As IO.DirectoryInfo
    Dim oFile As IO.FileInfo
    Dim Prop As FileProp

    FileProps.Clear()

    '▼フォルダの情報を追加

    For Each oFolder In ThisFolder.GetDirectories()
        Prop.Name = oFolder.Name
                           '名称
       
Prop.Size = 0                                       'サイズ
       
Prop.IsFolder = True                                '種類
       
Prop.Attributes = GetAttributes(oFolder.Attributes) '属性
       
Prop.LastWriteTime = oFolder.LastWriteTime          '更新日
       
Prop.CreationTime = oFolder.CreationTime            '作成日
       
FileProps.Add(Prop)
   
Next

    '▼ファイルの情報を追加

    For Each oFile In ThisFolder.GetFiles()
        Prop.Name = oFile.Name
                             '名称
       
Prop.Size = oFile.Length                            'サイズ
       
Prop.IsFolder = False                               '種類
       
Prop.Attributes = GetAttributes(oFile.Attributes)   '属性
       
Prop.LastWriteTime = oFile.LastWriteTime            '更新日
       
Prop.CreationTime = oFile.CreationTime              '作成日
       
FileProps.Add(Prop)
   
Next

End Sub

'■GetAttributes
''' <summary>ファイル・フォルダの属性を文字列として返す</summary>
''' <param name="Attributes">属性</param>
''' <returns>属性を表す文字列。各属性はアルファベット1文字で表される。</returns>
''' <remarks>複数の属性を一度に表現可能</remarks>
Private Function GetAttributes(ByVal Attributes As IO.FileAttributes) As String

    Dim strAttributes As String = ""

    '▼隠し属性
   
If Attributes And IO.FileAttributes.Hidden Then
       
strAttributes &= "H"
   
Else
       
strAttributes &= " "
   
End If

    '▼圧縮属性
   
If Attributes And IO.FileAttributes.Compressed Then
       
strAttributes &= "C"
   
Else
       
strAttributes &= " "
   
End If

    '▼システム属性
   
If Attributes And IO.FileAttributes.System Then
       
strAttributes &= "S"
   
Else
       
strAttributes &= " "
   
End If

    '▼読み取り専用属性
   
If Attributes And IO.FileAttributes.ReadOnly Then
       
strAttributes &= "R"
   
End If

    Return strAttributes

End Function

■リスト13:ファイル・フォルダ一覧の読み込み

初級講座もここまで読み進めばこのプログラムの説明はいらないですよね?

属性はコンパクトに表示できるようにそれぞれアルファベット一文字で表示するようにしてみました。たとえば、システム属性なら「S」、読み取り専用属性なら「R」です。システム属性かつ読み取り専用属性の場合は「SR」となります。

なお、この段階では表示設定に関わらずすべての情報を読み込んでいます。

 

ところで細かいことですがこの時点でtxtFolderNameLeaveイベントプロシージャに2行プログラムを追加しておいてください。このイベントプロシージャはテキストボックスにユーザーが直接フォルダ名を入力したときに実行されるプロシージャでしたね。

このときにテキストボックスが空にされるか、存在しないフォルダ名を指定されたら一覧をクリアするようにしておいたことを覚えているでしょうか?今、一覧の情報を保持するFilePropsというコレクションを追加しましたから、テキストボックスが空にされたときにこのコレクションもクリアすることが望ましいです。

そこでこのイベントプロシージャに2行追加してください。追加する2行は色を付けてあるのですぐにわかるでしょう。

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■[フォルダ名]テキストボックス
''' <summary>正しいフォルダ名が入力されていたらフォルダ一覧を表示する。</summary>
Private Sub txtFolderName_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles txtFolderName.Leave

    If Len(Trim(txtFolderName.Text)) > 0 Then

        '末尾に\を付加
       
If Strings.Right(txtFolderName.Text, 1) <> "\" Then
           
txtFolderName.Text &= "\"
       
End If

        'フォルダとファイルの一覧を表示
       
If IO.Directory.Exists(txtFolderName.Text) = True Then

            ReadFileList(txtFolderName.Text)
            ShowFileList()

        Else

            Dim St As String

            FileProps.Clear()    '←追加
            lvFiles.Clear()

            '存在しない場合
           
St = "存在するフォルダ名を正しく入力してください。" & vbNewLine
            St &= vbNewLine
            St &=
"・フォルダをドロップして指定することもできます。" & vbNewLine
            St &=
"・右側のボタンをクリックしてフォルダ選択画面を利用することもできます。" & vbNewLine
            MsgBox(St, MsgBoxStyle.Exclamation)

        End If

    Else

        FileProps.Clear()    '←追加
        lvFiles.Clear()

    End If

End Sub

■リスト14:先ほどのコードに2行追加する。

 

7.一覧の表示

 

ここまで準備が整えば、フォルダ・ファイルの一覧をリストビューに表示するくらいは簡単でしょう。

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■ShowFileList
''' <summary>ファイル・フォルダの情報の一覧を表示する</summary>
''' <remarks>情報はFilePropsコレクションから読み込まれる。
''' FilePropsコレクションに情報をセットするにはReadFileListメソッドを使う。
''' Settingsクラスの設定によって表示する項目が変る。
</remarks>
Private Sub ShowFileList()

    Dim Prop As FileProp
    Dim ListRow As ListViewItem

    lvFiles.BeginUpdate()
    lvFiles.Clear()

    '▼列の追加

    lvFiles.Columns.Add("名前", 200, HorizontalAlignment.Left)

    If Settings.IsSize Then
       
lvFiles.Columns.Add("サイズ", 80, HorizontalAlignment.Right)
    End
If

    If Settings.IsLastWriteTime Then
       
lvFiles.Columns.Add("更新日時", 120, HorizontalAlignment.Left)
    End
If

    If Settings.IsCreationTime Then
       
lvFiles.Columns.Add("作成日時", 120, HorizontalAlignment.Left)
    End
If

    If Settings.IsAttributes Then
       
lvFiles.Columns.Add("属性", 60, HorizontalAlignment.Left)
    End
If

    '▼行の追加

    For Each Prop In FileProps

        ListRow = lvFiles.Items.Add(Prop.Name)

        If Prop.IsFolder Then
           
ListRow.ImageIndex = 0 'フォルダの場合はアイコンを表示
       
End If

        If Settings.IsSize Then
           
If Prop.IsFolder Then
               
ListRow.SubItems.Add("")
           
Else
               
ListRow.SubItems.Add(Format(Prop.Size \ 1024, "#,0 KB")) 'サイズ
           
End If
       
End If

        If Settings.IsLastWriteTime Then
           
ListRow.SubItems.Add(Prop.LastWriteTime) '更新日時
       
End If

        If Settings.IsCreationTime Then
           
ListRow.SubItems.Add(Prop.CreationTime) '更新日時
       
End If

        If Settings.IsAttributes Then
           
ListRow.SubItems.Add(Prop.Attributes) '属性
       
End If

    Next

    lvFiles.EndUpdate()

End Sub

■リスト15:ファイル・フォルダの一覧をリストビューに表示する

表示設定ごとにIf文を使って判断しているのでプログラムはなんとなく長くなってしまいますがやっていることは単純です。少しだけ特別な点をあげるとするとリストビューの「列」を動的に追加している点でしょうか?

今回のプログラムでは表示設定によってサイズとか更新日時とかの列が必要か必要でないかが変ってしまうため、プロパティウィンドウで列を指定するのではなくプログラムでIf文で判定しながら列を追加しているわけです。

さて、ここまで完了すると不完全ながらもフォルダ・ファイルの一覧が表示できるようになります。実行していろいろやってみてください。フォルダをドロップしてもいいですし、フォルダ名を直接入力しても良いです。フォルダを選択して正しく一覧が表示されるか確認してみましょう。

また、表示設定をいじって設定どおりに表示が切り替わるかも確認してみましょう。

印刷・保存以外はほとんどの機能がうまく動作するはずです。

 

8.印刷

 

次に印刷をプログラムします。正直言って印刷はちょっとやっかいです。まずは完成版をご覧いただきましょう。

VB.NET2002対応 VB.NET2003対応 VB2005対応

Dim PrintPropIndex As Integer '複数ページ印刷の場合、どこまで印刷したか記録するために使用
Dim PageCount As Integer
'印刷ページ数

'■[ファイル] - [印刷]メニュー
Private Sub mnuFilePrint_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuFilePrint.Click

    PrintDocument1.Print()

End Sub

'■[ファイル] - [印刷プレビュー]メニュー
Private Sub mnuFilePreview_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuFilePreview.Click

    PrintPreviewDialog1.ShowDialog()

End Sub

'■PrintDocument1_PrintPage
''' <summary>ファイル・フォルダの情報の一覧を印刷(またはプレビュー)する</summary>
''' <remarks>情報はFilePropsコレクションから読み込まれる。
''' FilePropsコレクションに情報をセットするにはReadFileListメソッドを使う。
''' Settingsクラスの設定によって印刷する項目が変る。
</remarks>
Private Sub PrintDocument1_PrintPage(ByVal sender As System.Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs) Handles PrintDocument1.PrintPage

    Dim LineTop As Integer         '現在の行の印刷開始縦位置
   
Dim LineLeft As Integer        '現在の行の印刷開始横位置(実はどの行でも同じ値)
   
Dim LineText As String         '現在の行の印刷内容
   
Dim Name As String             '現在の行に印刷すべきファイル・フォルダ名
   
Dim NameWidth As Integer = 250 'ファイル・フォルダ名の印刷領域の幅
   
Dim LineHeight As Single       '現在の行の高さ
   
Dim Prop As FileProp           '現在の行に印刷すべきファイル・フォルダの情報
   
Dim HeaderTop As Integer       'ヘッダ部の印刷開始縦位置
   
Dim ListTop As Integer         '明細部の印刷開始縦位置
   
Dim i As Integer
   
Dim HeaderFont As New Font("MS Pゴシック", 14, FontStyle.Regular)
    Dim ListFont As New Font("MS ゴシック", 12, FontStyle.Regular)
    Dim NameFont As New Font("MS Pゴシック", 12, FontStyle.Regular)

    '▼ヘッダ部

    PageCount += 1
    HeaderTop = e.MarginBounds.Y
    LineLeft = e.MarginBounds.X
    LineHeight = HeaderFont.GetHeight(e.Graphics)

    '印刷対象のフォルダ名を印字。その背景を四角で囲む。
   
e.Graphics.FillRectangle(Brushes.LightBlue, New Rectangle(LineLeft, HeaderTop, e.MarginBounds.Width, LineHeight))
    e.Graphics.DrawRectangle(Pens.Black,
New Rectangle(LineLeft, HeaderTop, e.MarginBounds.Width, LineHeight))
    e.Graphics.DrawString(txtFolderName.Text, HeaderFont, Brushes.Black, LineLeft, HeaderTop)

    '現在のページ数を印字
   
e.Graphics.DrawString(PageCount, HeaderFont, Brushes.Black, LineLeft + e.MarginBounds.Width - 30, HeaderTop)

    '▼明細部

    ListTop = e.MarginBounds.Y + LineHeight + 10
    LineHeight = ListFont.GetHeight(e.Graphics)
    LineTop = ListTop

    For i = PrintPropIndex To FileProps.Count - 1

        Prop = FileProps(i)

        '▼名前 名前はすぐに印字する

        If Prop.IsFolder Then
           
Name = CalcString(Prop.Name, NameWidth - 17, e.Graphics, NameFont)
            e.Graphics.DrawImage(ImageList1.Images(0), LineLeft + 1, LineTop)
            e.Graphics.DrawString(Name, NameFont, Brushes.Black, LineLeft + 17, LineTop)
       
Else
           
Name = CalcString(Prop.Name, NameWidth, e.Graphics, NameFont)
            e.Graphics.DrawString(Name, NameFont, Brushes.Black, LineLeft, LineTop)
        End
If

        LineText = ""

        '▼サイズ
       
If Settings.IsSize Then
           
If Prop.IsFolder Then
               
LineText = New String(" ", 14)
           
Else
               
LineText = Format(Prop.Size \ 1024, "#,0 KB").PadLeft(14)
            End
If
       
End If

        '▼更新日時
       
If Settings.IsLastWriteTime Then
           
LineText &= " " & Format(Prop.LastWriteTime, "yyyy/MM/dd HH:mm:ss")
        End
If

        '▼作成日時
       
If Settings.IsCreationTime Then
           
LineText &= " " & Format(Prop.CreationTime, "yyyy/MM/dd HH:mm:ss")
        End
If

        '▼属性
       
If Settings.IsAttributes Then
           
LineText &= " " & Prop.Attributes
        End
If

        'この行の情報を印字
       
e.Graphics.DrawString(LineText, ListFont, Brushes.Black, LineLeft + NameWidth, LineTop)
        LineTop += LineHeight

        '次の行が印刷可能領域内に入らない場合は次のページの印刷を行う。
       
If LineTop + LineHeight > e.MarginBounds.Y + e.MarginBounds.Height Then
           
e.HasMorePages = True
           
Exit For
       
End If

    Next

    '▼明細部全体を四角で囲む

    Dim Rect As New Rectangle(LineLeft, ListTop, e.MarginBounds.Width, LineTop - ListTop)

    e.Graphics.DrawRectangle(Pens.Black, Rect)

    PrintPropIndex = i + 1

    If e.HasMorePages = False Then
       
'最後のページの場合はページ数と印刷情報をリセットする。
        'リセットしないと次の印刷がうまくできない。
       
PageCount = 0
        PrintPropIndex = 0
    End
If

End Sub

'■CalcString
''' <summary>文字列を領域内に入りきるように末尾をカットする</summary>
''' <param name="str">対象の文字列</param>
''' <param name="MaxWidth">領域の幅</param>
''' <param name="g">対象のデバイスコンテキスト</param>
''' <param name="f">使用するフォント</param>
''' <returns>領域内に入りきるようにカットされた文字列</returns>
''' <remarks>できるだけカットする文字数が少なくなるようにする。
''' カットする場合は末尾に「...」を付加する。</remarks>
Private Function CalcString(ByVal str As String, ByVal MaxWidth As Integer, ByVal g As Graphics, ByVal f As Font) As String

    Dim Width As Integer
   
Dim TempStr As String
   
Dim i As Integer

    For i = Len(str) To 1 Step -1

        TempStr = Strings.Left(str, i)
        Width = g.MeasureString(TempStr, f).Width

        If Width < MaxWidth Then
           
If i = Len(str) Then
               
Return TempStr
           
Else
               
Return Strings.Left(str, i - 1) & "..."
           
End If
       
End If

    Next

    Return ""

End Function

■リスト16:ファイル・フォルダの一覧を印刷する

印刷の詳細については初級講座第35回 印刷をご覧下さい。

ここでは特徴的な点をピックアップして説明します。

一番気を使うのはファイルの情報がぴったりとそろって印刷されることです。たとえば、サイズを印刷するときの末尾のKBはどの行でも同じ位置に印刷したいですし、日付を表す「2005」等の部分も上の行と下の行とでぴったりと同じ位置に印刷しないとでこぼこして見難い印刷物となってしまいます。

そろえて印刷するには座標をきちきちと指定して印刷すればよいのですが、あまりきちきちやるとプログラムが煩雑になってしまうし適切な座標を計算するのも面倒くさいので嫌です。

しかしながら今回の印刷は文字だらけですから各行で文字数さえ一致させればうまく印刷できそうな気がしませんか?

たとえば、次のように印字されればぴったりとそろっているように見えるでしょう。

Folder1            2003/05/20 11:11:11   HR
File1      200 KB  2005/12/01 10:10:10   H
File2       25 KB  2005/10/10 09:09:09

■非プロポーショナルフォントの例

これはもちろん座標を指定しているのではなく文字数を調節しているのです。

ということでここの印刷もこの文字数調節を使って位置をそろえていくことにします。文字数を使って位置を調節するときは非プロポーショナルフォントを使うように注意しなければなりません。非プロポーショナルフォントとはフォント名に「P」が付いていないフォントのことで、どの文字も同じ幅で表示されるフォントのことです。

たとえば、すぐ上の例をプロポーショナルフォントで書くと次のようにガタガタに見えてしまいます。

Folder1            2003/05/20 11:11:11   HR
File1      200 KB  2005/12/01 10:10:10   H
File2       25 KB  2005/10/10 09:09:09

■プロポーショナルフォントの例

上の例も下の例も同じ内容で同じ文字数なのですよ。フォントが違うだけで見かけは大違いです。

ところで、1つ問題があります。ファイルやフォルダの名前を非プロポーショナルフォントで印字するとどうもやたらと幅を取ってしまうのです。ときおり見かける長い名前のファイルなどはとても表示しきれません。 上の2つの例を比べてもプロポーショナルフォントの方がガタガタではありますが、幅は狭く済んでいるのがわかります。

そこで、ファイル名・フォルダ名だけはプロポーショナルフォントで印刷します。ただし、そうするとがたがたになってしまいますから、名称の部分だけ幅をきちんと座標で指定することにします。変数で言うとNameWidth変数が名前を表示する部分の幅です。上掲のプログラムでは250ピクセル分を名前用として設定しています。

どうしても250ピクセルでは表示しきれない名前はあきらめて後ろを切り捨てることにします。その処理を行っているのがCalcStringメソッドです。

他の点でも多分印刷に関しては皆さん??という心境かもしれませんね。でも、多少でもこの印刷プログラムを改造してあなたの好みを反映させることができるのではないですか?また、この部分をコピーしてあなたが制作中の別のソフトに印刷を装備することはできますか?

一度にすべて理解できなくてもこのように徐々に慣らして言って自分のものにするようにすればよいと思います。

今は完成した印刷ロジックを実行して楽しんでください。

ところで、余白の設定や印刷するページの指定、印刷部数の設定なども実はプログラム可能です。余裕のある方はチャレンジしてみてください。

メモ:表示設定ですべての項目を表示するようにした場合は、印刷したときにページに入りきらなくなってはみだした部分が切り捨てられてしまいます。

はみ出した部分は別のページにするというのはかなり厄介なプログラムとなってしまいます。今回はあきらめますが、それでもこの印刷機能はなかなか気持ちよくつかえると思います。

 

 

9.保存

 

最後にCSV形式でのファイルの保存をプログラムします。印刷に比べればどうということもありません。次の通りです。

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■[ファイル] - [名前を付けて保存]メニュー
Private Sub mnuFileSaveAs_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuFileSaveAs.Click

    Dim Dialog As New SaveFileDialog

    Dialog.Filter = "CSVファイル(*.csv)|*.csv|すべてのファイル|*.*"
   
Dialog.DefaultExt = "csv"

    If Dialog.ShowDialog = Windows.Forms.DialogResult.OK Then
       
SaveFileList(Dialog.FileName)
    End
If

End Sub

'■SaveFileList
''' <summary>ファイル・フォルダの情報の一覧をcsv形式で保存する</summary>
''' <param name="FileName">保存するcsvファイルのフルパス</param>
''' <remarks>情報はFilePropsコレクションから読み込まれる。
''' FilePropsコレクションに情報をセットするにはReadFileListメソッドを使う。
''' Settingsクラスの設定によって保存する項目が変る。
</remarks>
Private Sub SaveFileList(ByVal FileName As String)

    Dim Prop As FileProp
    Dim LineText As
String
   
Dim Writer As New IO.StreamWriter(FileName, False, System.Text.Encoding.GetEncoding("Shift-JIS"))

    For Each Prop In FileProps

        LineText = Prop.Name & ","

        If Prop.IsFolder Then
           
LineText &= "フォルダ" & ","
       
Else
           
LineText &= "ファイル" & ","
       
End If

        'サイズ
       
If Settings.IsSize Then
           
If Prop.IsFolder Then
               
LineText &= "0,"
           
Else
               
LineText &= Prop.Size \ 1024 & ","
           
End If
       
End If

        '更新日時
       
If Settings.IsLastWriteTime Then
           
LineText &= Format(Prop.LastWriteTime, "yyyy/MM/dd HH:mm:ss") & ","
       
End If

        '作成日時
       
If Settings.IsCreationTime Then
           
LineText &= Format(Prop.CreationTime, "yyyy/MM/dd HH:mm:ss") & ","
       
End If

        '属性
        If Settings.IsAttributes Then
           
LineText &= Prop.Attributes
       
End If

        Writer.WriteLine(LineText)

    Next

    Writer.Close()

End Sub

■リスト17:csv形式でファイルに保存する

ファイルを保存する部分はやはり単純なものとなります。

 

10.仕上げ

 

後はいくつかの細かい機能を追加して仕上げとしましょう。

まず、最後までプログラムがされていない[最新の情報に更新]メニューをプログラムしましょう。現在表示されているフォルダにファイルやフォルダが追加されたり削除されたりしても一覧に自動的に反映されるわけではありません。この[最新の情報に更新]メニューをクリックしてはじめて更新されるのです。このメニューにはショートカットとして[F5]が付いていますから、実際にはプログラム実行中に[F5]を押すといつでも最新の情報に更新されます。

これはWindowsのエクスプローラ等でよく見られる仕様ですね。この部分のプログラムは既に完成しているほかの機能を呼び出すだけですから自分で挑戦してみるのも一興だと思います。

正解は次の通りです。

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■[表示] - [最新の情報に更新]メニュー
Private Sub mnuViewRefresh_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles mnuViewRefresh.Click

    '▼フォルダとファイルの一覧を表示
   
ReadFileList(txtFolderName.Text)
    ShowFileList()

End Sub

■リスト18:ファイル一覧の再読み込みと再表示

 

次に状況に応じていくつかのメニューを使用不可にする必要があります。いろいろ操作してみた方は気が付いたかもしれませんが、たとえば、フォルダが選択されていない状態で印刷を実行するとエラーになってしまいます。フォルダが選択されていない場合は印刷が実行できないようにしなければなりません。

印刷の他にも保存・印刷プレビュー・表示設定・最新の情報に更新するメニューがこれに該当します。

フォルダが選択されているかどうかを判断する方法はいくつかありますが、情報の一覧が変数FilePropsに記録されていることから、FilePropsコレクションに項目が1つでもあるかないかで判断するのがシンプルだと思います。

ところが 親メニューをクリックして子メニューが表示される ときに発生するイベントがVB.NET2002,VB.NET2003とVB2005では異なるので注意です。だいたいVB.NET2002,VB.NET2003ではメインメニューがMainMenuコントロールですが、VB2005ではMenuStripコントロールに変ってしまったので似ているけど別物なのです。このことは少しばかり混乱をもたらします。

VB2005ではDropDownOpeningイベント(読み方:DropDownOpening = ドロップダウンオープニング)が発生しますから、プログラムはここに記述することになります。

VB2005対応

'■[ファイル]メニュー
Private Sub mnuFile_DropDownOpening(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFile.DropDownOpening

    mnuFileSaveAs.Enabled = FileProps.Count > 0
    mnuFilePrint.Enabled = FileProps.Count > 0
    mnuFilePreview.Enabled = FileProps.Count > 0

End Sub

'■[表示]メニュー
Private Sub mnuView_DropDownOpening(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuView.DropDownOpening

    mnuViewSettings.Enabled = FileProps.Count > 0
    mnuViewRefresh.Enabled = FileProps.Count > 0

End Sub

■リスト19:都合の悪いメニューは使用できないようにする(VB2005)

 

VB.NET2002,VB.NET2003ではPopupイベント(読み方:Popup = ポップアップ)が発生しますから、プログラムは次のようになります。

VB.NET2002対応 VB.NET2003対応

'■[ファイル]メニュー
Private Sub mnuFile_Popup(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuFile.Popup

    mnuFileSaveAs.Enabled = FileProps.Count > 0
    mnuFilePrint.Enabled = FileProps.Count > 0
    mnuFilePreview.Enabled = FileProps.Count > 0

End Sub

'■[表示]メニュー
Private Sub mnuView_Popup(ByVal sender As Object, ByVal e As System.EventArgs) Handles mnuView.Popup

    mnuViewSettings.Enabled = FileProps.Count > 0
    mnuViewRefresh.Enabled = FileProps.Count > 0

End Sub

■リスト20:都合の悪いメニューは使用できないようにする(VB.NET2002、VB.NET2003)

これで多分完璧だと思います。しかし使っているうちに表示されている一覧でフォルダをダブルクリックした場合、そのフォルダの内容を一覧表示できるようにしたら便利じゃないかと思い至りましたので、その機能もつけてみました。

VB.NET2002対応 VB.NET2003対応 VB2005対応

'■[ファイル一覧]リストビュー
''' <summary>ダブルクリックしたフォルダ内のファイル・フォルダ一覧を表示する。</summary>
Private Sub lvFiles_DoubleClick(ByVal sender As Object, ByVal e As System.EventArgs) Handles lvFiles.DoubleClick

    Dim ClickPath As String

    ClickPath = txtFolderName.Text & lvFiles.SelectedItems(0).Text & "\"

    If IO.Directory.Exists(ClickPath) Then
       
txtFolderName.Text = ClickPath
        ReadFileList(txtFolderName.Text)
        ShowFileList()
    End
If

End Sub

■リスト21:一覧のダブルクリックに対応

 

みなさんも自分のアイディアで他にも機能を追加してみると面白いと思いますよ。ここまで頑張って読んで実際に手順に従って作成してみた方は無理にでも1つくらい自分で機能を追加してみることをお勧めします。そうですね、ファイルをダブルクリックした場合そのファイルを開く機能があると便利かもしれませんね。一覧の内容を自由に並び替えられるようにしてもいいかもしれません。 プログラムのアイコンにフォルダをドロップして起動できるようにしても便利ですし、一覧を「コピー」してそのままテキストファイルやExcelに貼り付けることができるというのも素敵です。

 

11.最後に

 

今回はかなり長い記事になってしまいました。正直私も疲れました。みなさんもここまで読み進めるのに疲れたことでしょう。しかし、このようなWindowsには搭載されていない機能でもアイディアがあれば自分で作れるというプログラムの醍醐味は味わっていただけたでしょうか?

技術的な面でもファイル・フォルダの扱いはもちろん、印刷あり、コレクションあり、2つ目のフォームありとこれまでの初級講座で出てきた内容がうまく具合にちりばめられていて丁度良 かったと思います。 もちろんメニューの追加やクラスの作成などこれまでの初級講座ではあまり触れていない側面もありました。

さて、初級講座の第25回ではエクスプローラ形式でファイル・フォルダの一覧を表示し、しかもフォルダのサイズや格納しているファイル数等までも一覧で見られるというプログラムを作りました。この機能と今回作成したプログラムの機能を合体させて1つのプログラムに仕立てることができればなかなか便利なファイル・フォルダ情報表示プログラムになると思います。

実技としてはファイル・フォルダのプログラムが続いてしまって少し単調だったと思っています。VBではいろいろな種類のプログラムが作れますからみなさんも積極的にチャレンジしてみてください。積極的なチャレンジこそが上達への近道です。