ゲームエフェクトデザイナーのブログ | A Real-Time VFX Artist's Blog

About Making Materials on UE, Making Tools with C#, etc

C#でサブフォルダの総サイズのリストアップ(ListBox編)

お次は、あるフォルダ直下にあるサブフォルダ個々の総ファイルサイズをリストアップするツールについての備忘録です。リストアップを表示するパーツをここでは「ListBox」で試してみました。

下図のような外観で、フォルダパスを入力後にEnterキーでリストアップを実行します。
フォルダのドラッグ&ドロップにも対応する場合は前回の記事が参考になるかと思います。

f:id:moko_03_25:20170812032439j:plain

 

実装したもの


今回実装したのは以下になります。
かなりまどろっこしい感じになってる気がします。
もっと簡潔で良い書き方が色々ありそうですが‥

・あるフォルダ以下にある全てのサブフォルダのパスを配列で取得するメソッド
・あるフォルダ以下の総ファイルサイズを取得するメソッド
・サブフォルダ1つ1つの総ファイルサイズを配列で取得するメソッド

・パス入力欄でEnterを押すとサブフォルダ個々の総サイズをリストアップする

・ListBoxの内容のクリップボードへのコピーとクリア


1つずつ見ていきます。

あるフォルダ以下にある全てのサブフォルダのパスを配列で取得するメソッド


こちらは前回の記事で出てきたもので、やはりDOBON.NETさんをそのままコピーさせて頂いたものです。
青文字はクラス名・メソッド名・変数名です)

//あるフォルダ以下にある全てのサブフォルダのパスを配列で取得する
public string GetSubFolderPath(string path)
{
  string subFolders = Directory.GetDirectories(

    path, "*",
    SearchOption.TopDirectoryOnly); //サブフォルダTOP階層のみ取得

  return subFolders;
}


実質2行でシンプルです。

あるフォルダ以下の総ファイルサイズを取得するメソッド


こちらに関しては完全に下記の記事をまるまるコピーさせていただきました。

フォルダのサイズを取得する: .NET Tips: C#, VB.NET

こんな感じです。

//あるフォルダ以下の総ファイルサイズを取得するメソッド
public static long GetDirectorySize(DirectoryInfo dirInfo)
{
  long size = 0;

  //1つのサブフォルダ内の全ファイルの合計サイズを計算する
  foreach (FileInfo fi in dirInfo.GetFiles())
    size += fi.Length;

  //全サブフォルダのサイズを合計していく
  foreach (DirectoryInfo di in dirInfo.GetDirectories())
    size += GetDirectorySize(di);

  //結果を返す
  return size;
}


サブフォルダの合計をしていく場所で自身のメソッド名が登場していますが、入れ子で実行しているということですかね? まず指定パス直下のファイルを合計してからサブフォルダ1つずつの中を掘り下げて合計していっている感じでしょうか。

そんな記述ができるんですね。。

ただ、変数sizeが繰り返し 0 で初期化されたり return されたりしていて、ステップ実行してもどういう流れになってるのか把握し辛いですね‥

サブフォルダ1つ1つの総ファイルサイズを配列で取得するメソッド


ここが今回の要で、1つめのメソッドで得たサブフォルダのパス情報を引数として受け取って、for文でパス1つ1つに対して2つめのメソッドを実行して総ファイルサイズを順に得ていく感じです。

//サブフォルダ1つ1つの総ファイルサイズを配列で取得
public string GetSubFolderSize(string subFolders)
{
  //サブフォルダの数分の配列"size"と"sizeSt"を定義
  int el = subFolders.Length; //サブフォルダの要素数(element)
  long size = new long[el]; //整数の配列
  string sizeSt = new string[el]; //文字列の配列

  string fn; //フォルダネームを一時的に保存する変数

  //配列の要素の数だけ処理
  for (int i = 0; i < el; i++)
  {
    //入力したパスのフォルダ名を取得
    fn = Path.GetFileName(subFolders[i]);

    //入力したパスをDIrectoryInfoクラスのオブジェクトに代入
    DirectoryInfo chkPath = new DirectoryInfo(subFolders[i]);
    //総ファイルサイズをMiB単位で配列"size"に代入
    size[i] = GetDirectorySize(chkPath) / 1024 / 1024;
    //文字列に変換して配列"sizeSt"に代入
    sizeSt[i] = fn + "\t" + size[i].ToString("D") + " MB";
  }

  return sizeSt;
}


得られるファイルサイズはバイト単位になるので、ここでは1024で割ってKiBに、さらに1024で割ってMiB単位に変えた上で、ToString("D")で文字列(整数)に変換しています。

パス入力欄でEnterを押すとサブフォルダ個々の総サイズをリストアップする


実際にパスを入力してEnterで決定した際の挙動です。

一応ちゃんとパス先にフォルダがあるかチェックしてからGetSubFolderSizeメソッドを実行しています。

//パス入力欄でのキー入力イベント
private void textBoxPath_KeyDown(object sender, KeyEventArgs e)
{
  //Ctrl + A で全選択可能にする
  if (e.Control && e.KeyCode == Keys.A)
  {
    textBoxPath.SelectAll();
  }

  //Enterでパスを取得
  if (e.KeyCode == Keys.Enter)
  {
    string path = textBoxPath.Text;

    //textBoxPathのパス先が存在しているかどうか判定
    if (Directory.Exists(path))
    {
      //サブフォルダのパスを全て取得
      string subFolders = GetSubFolderPath(path);

      //サブフォルダの数分の配列"sizeSt"を定義
      string sizeSt = new string[subFolders.Length];

      //全てのサブフォルダごとの合計サイズを取得
      sizeSt = GetSubFolderSize(subFolders);

      //リストに代入していく
      List<string> lt = new List<string>(sizeSt);

      foreach (var item in lt)
      {
        listBoxResult.Items.Add(item);
      }
    }
    else
    {
      MessageBox.Show("\"" + path + "\"" +
        " は存在しないフォルダです");
    }
  }
}


総サイズを配列で受け取った後はリストに代入して、リストをListBoxに追加しています。
ここは色んな方法がありそうですがListクラスも使ってみたいと思って使ってみた感じです。とは言えここもコピペです。下記の記事を参考にさせて頂きました。

C# 配列を List に変換する | ITLAB51.COM

ListBoxは初めて使ってみましたが、複数行を選択させたいなら「SelectionMode」プロパティをMultiExtendedにすると良いです。MultiSimpleというのもありますがこちらはクリックで選択状態になったあと別の行を選択しても選択状態が解除されないのが気持ち悪いので個人的には「これは使わないなあ」と思いました。

f:id:moko_03_25:20170812063347j:plain

 

ListBoxの内容のクリップボードへのコピーとクリア


最後におまけです。
ListBoxの内容を全てクリップボードへコピーする方法と、全て消去する方法です。

//「クリップボードへコピーする」ボタンの挙動
private void buttonCopy_Click(object sender, EventArgs e)
{
  //LINQでListBoxの中身を1行ずつ配列に代入
  string[] list = this.listBoxResult.Items.Cast<string>().ToArray();
  //文字列を改行を挟みつつ連結
  string st = string.Join("\r\n", list);
  //クリップボードへコピー
  Clipboard.SetText(st);
}

//「クリア」ボタンでListBoxの内容を消去
private void buttonClear_Click(object sender, EventArgs e)
{
  listBoxResult.Items.Clear(); //内容をクリア
}


ListBoxの内容全てを1行ずつ配列の形で取得してから全ての行を連結させています。
もっと直接クリップボードにコピーできないのかなと思いましたがちょこっとググってみただけでは分かりませんでした。

ちなみにListBoxの内容を配列で取得する方法は、こちらのYahoo!知恵袋のベストアンサーの記述方法をそのまま参考にさせて頂きました。

C#のリストボックスの項目取得で困っています。選択されていない項目も含めて... - Yahoo!知恵袋

これがLINQというやつですか。。覚えるととても便利そう!ですね。

それから、文字列の配列を改行を挟みつつ連結する方法はこちらの記事を参考にさせて頂きました。

文字列を連結する: .NET Tips: C#, VB.NET

内容をクリアはメソッドが用意されていますね。

プロジェクトデータと実行ファイル


Googleドライブにアップしました。ライセンスはMITです。

MyTool_FolderSizeCheck_ListBox.zip - Google ドライブ

binフォルダの中にexeが入っています。