前回に引き続き、csFileListをちゃちゃっとやっつけちゃいましょう。

 

3.csFileList.cs(メソッド編)

前回はフォーム(ウィンドウ)の見てくれの部分(どんがら)について説明しましたので、今回は機能面(メソッド)について「青字」解説します。

 

        //////////////////////////
        //ツールバー関連メソッド
        //////////////////////////

        //「ファイルリストを開く」処理
        private void OnOpen_Click(object sender, EventArgs e)
        {
            DialogResult dr = MessageBox.Show("ファイルリスト入れ替えますか(はい)、または\r\n追加しますか(いいえ)?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if(dr == DialogResult.Yes)
            {
                file_List.Items.Clear();                //入替えの場合、リストビュー内の項目を全削除
            }
            OpenFileDialog ofDlg = new OpenFileDialog();
            ofDlg.Filter = "アルバムファイル(*.alb)|*.alb|プレイリストファイル(*.plf)|*.plf|すべてのファイル(*.*)|*.*";    //ファイルフィルターの指定(解説:自作のソフトであるAlbumとDirectShowのファイルリストを扱っています。)
            ofDlg.FilterIndex = 1;                        //ファイルフィルターインデックスの指定
            ofDlg.RestoreDirectory = true;                //ダイアログボックスを閉じる前に現在のディレクトリを復元するようにする
            ofDlg.CheckFileExists = true;                //存在しないファイルの名前が指定されたとき警告を表示する
            ofDlg.CheckPathExists = true;                //存在しないパスが指定されたとき警告を表示する
            ofDlg.InitialDirectory = @".\";                //デフォルトのフォルダを指定する
            ofDlg.Title = "ファイルリストを開く";        //ダイアログのタイトルを指定する
            if(ofDlg.ShowDialog() == DialogResult.OK)    //ダイアログを表示する
            {
                //ファイル("shift_jis")を開く(解説:ファイル入出力エラーが考えられ、usingを使用しています。)
                using(StreamReader sr = new StreamReader(ofDlg.FileName, System.Text.Encoding.GetEncoding("shift_jis")))
                {
                    //1行ずつ処理
                    while(!sr.EndOfStream){
                        string line = sr.ReadLine();
                        //カンマで配列要素を分ける(最初の要素がファイルパス、名を前提とする)
                        string[] fns = line.Split(',');
                        //第1要素のファイルパス、名が'"'で括られていれば削除する
                        string fn = fns[0].Trim('"');
                        //ファイルが存在するか、フォールダーではないか、をチェックする
                        if(!File.Exists(fn))
                        {
                            MessageBox.Show("このファイルは存在しません。\r\n" + fn, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            continue;    //解説:ファイルが(または「で」)なければ、次に行きます。)
                        }
                        ListViewItem item = file_List.Items.Add(Path.GetFileName(fn));
                        item.SubItems.Add(Path.GetDirectoryName(fn));
                    }
                    sr.Close();
                }
                if(file_List.Items.Count > 0)
                {
                    //ツールバーボタンを有効化する解説:ファイルの追加の際はこれを行います。)
                    ChangeToolBarStatus(true);
                    //ファイル数を更新する解説:ファイルの追加の際はこれを行います。)
                    ShowNumOfFiles();
                 }
             }
            else
            {
                MessageBox.Show("キャンセルされました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            //オブジェクトを破棄する(解説:↓で述べる通り、C#ではダイアログは必ずDisposeする必要があります。)
            ofDlg.Dispose();
        }

        //「ファイルリストを保存」処理(解説:本来は不要ですが、「何を処理したっけ?」ということもあるので...)
        private void OnSave_Click(object sender, EventArgs e)
        {
            //選択されたファイルパス、名をfilesに纏める
            string files = "";
            foreach(ListViewItem item in file_List.Items)
            {
                files += "\"" + item.SubItems[1].Text + "\\" + item.SubItems[0].Text + "\"\r\n";
            }
            //書き込みファイルを指定
            SaveFileDialog sfDlg = new SaveFileDialog();
            sfDlg.Filter = "アルバムファイル(*.alb)|*.alb|プレイリストファイル(*.plf)|*.plf|すべてのファイル(*.*)|*.*";    //ファイルフィルターの指定
            sfDlg.FilterIndex = 3;                        //ファイルフィルターインデックスの指定
            sfDlg.RestoreDirectory = true;                //ダイアログボックスを閉じる前に現在のディレクトリを復元するようにする
            sfDlg.CheckPathExists = true;                //存在しないパスが指定されたとき警告を表示する
            sfDlg.InitialDirectory = @".\";                //デフォルトのフォルダを指定する
            sfDlg.Title = "ファイルリストを保存";        //ダイアログのタイトルを指定する
            if(sfDlg.ShowDialog() == DialogResult.OK)    //ダイアログを表示する
            {
                //ストリームで書き込む(解説:ファイル入出力エラーが考えられ、usingを使用しています。)
                using(StreamWriter sw = new StreamWriter(sfDlg.FileName, false, System.Text.Encoding.GetEncoding("shift_jis")))    //falseは「上書き指定」
                {
                    sw.Write(files);
                    sw.Close();
                }
             }
            else
            {
                MessageBox.Show("キャンセルされました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            //オブジェクトを破棄する(解説:↓で述べる通り、C#ではダイアログは必ずDisposeする必要があります。)
            sfDlg.Dispose();
        }

        //「終了」処理
        private void OnExit_Click(object sender, EventArgs e)
        {
            Close();
        }

        //「ファイルを追加」処理(解説:個別ファイルの追加です。)
        private void OnAdd_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofDlg = new OpenFileDialog();
            ofDlg.Filter = "すべてのファイル(*.*)|*.*";    //ファイルフィルターの指定
            ofDlg.FilterIndex = 1;                        //ファイルフィルターインデックスの指定
            ofDlg.RestoreDirectory = true;                //ダイアログボックスを閉じる前に現在のディレクトリを復元するようにする
            ofDlg.CheckFileExists = true;                //存在しないファイルの名前が指定されたとき警告を表示する
            ofDlg.CheckPathExists = true;                //存在しないパスが指定されたとき警告を表示する
            ofDlg.Multiselect = true;                    //複数ファイルの選択を行う(解説:追加、除外ではこうしました。)
            ofDlg.InitialDirectory = @".\";                //デフォルトのフォルダを指定する
            ofDlg.Title = "ファイルを開く";                //ダイアログのタイトルを指定する
            if(ofDlg.ShowDialog() == DialogResult.OK)    //ダイアログを表示する
            {
                foreach(string fn in ofDlg.FileNames)    //複数選択ファイル名コレクションの表示
                {
                    ListViewItem item = file_List.Items.Add(Path.GetFileName(fn));    //解説:ファイル名です。
                    item.SubItems.Add(Path.GetDirectoryName(fn));    //解説:ファイルパスです。
                }
                if(file_List.Items.Count > 0)
                {
                    //ツールバーボタンを有効化する
                    ChangeToolBarStatus(true);
                    //ファイル数を更新する
                    ShowNumOfFiles();
                 }
             }
            else
            {
                MessageBox.Show("キャンセルされました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            //オブジェクトを破棄する(解説:↓で述べる通り、C#ではダイアログは必ずDisposeする必要があります。)
            ofDlg.Dispose();
        }

        //「ファイルを除外」処理
        private void OnRemove_Click(object sender, EventArgs e)
        {
            if(file_List.SelectedItems.Count == 0)
            {
                MessageBox.Show("項目が選択されていません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            else
            {
                DialogResult dr = MessageBox.Show("選択項目" + file_List.FocusedItem.Text + "(等)を削除しますか?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if(dr == DialogResult.Yes)
                {
                    foreach(ListViewItem item in file_List.SelectedItems)
                    {
                        //選択されたアイテムを削除する
                        item.Remove();
                    }
                    if(file_List.Items.Count == 0)
                        //ツールバーボタンを無効化する
                        ChangeToolBarStatus(false);
                    //ファイル数を更新する
                    ShowNumOfFiles();
                }
            }
        }

        //「削除」処理
        private void OnDelete_Click(object sender, EventArgs e)
        {
            DialogResult dr = MessageBox.Show("リストの全ファイルを削除しますか?\r\n注意:削除するファイルのリストは予め「ファイルリストを保存」で作成してください。", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if(dr == DialogResult.Yes)
            {
                foreach(ListViewItem item in file_List.Items)
                {
                    //全ファイルの削除(解説:削除できないファイルが存在する可能性があるので...try)
                    try
                    {
                        File.Delete(item.SubItems[1].Text + "\\" + item.SubItems[0].Text);
                        item.Remove();    //処理完了ファイルはリストから削除する
                    }
                    catch
                    {
                        MessageBox.Show(item.SubItems[1].Text + "\\" + item.SubItems[0].Text + "\r\nの削除に失敗しました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        continue;
                    }
                }
            }
        }

        //「コピー」処理
        private void OnCopy_Click(object sender, EventArgs e)
        {
            //「フォールダーを開く」処理
            FolderBrowserDialog fbDlg = new FolderBrowserDialog();
            fbDlg.Description = "コピー先のフォルダーを選択、または作成してください。";    //ダイアログの説明文
            fbDlg.SelectedPath = ".\\";                    //デフォルトのフォルダー
            if(fbDlg.ShowDialog() == DialogResult.OK)    //フォルダを選択するダイアログを表示する
            {
                foreach(ListViewItem item in file_List.Items)
                {
                    //全ファイルのコピー(解説:コピーできないファイルが存在する可能性があるので...try)
                    try
                    {
                        //既に同名ファイルがあった場合はエラーとなる(File.Copy(FromFile, ToFile, false))
                        File.Copy(item.SubItems[1].Text + "\\" + item.SubItems[0].Text, fbDlg.SelectedPath + "\\" + item.SubItems[0].Text);
                        item.Remove();    //処理完了ファイルはリストから削除する
                    }
                    catch
                    {
                        MessageBox.Show(item.SubItems[1].Text + "\\" + item.SubItems[0].Text + "\r\nのコピーに失敗しました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        continue;
                    }
                }
            }
            else
            {
                MessageBox.Show("キャンセルされました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            //オブジェクトを破棄する(解説:↓で述べる通り、C#ではダイアログは必ずDisposeする必要があります。)
            fbDlg.Dispose();
        }

        //「移動」処理
        private void OnMove_Click(object sender, EventArgs e)
        {
            //「フォールダーを開く」処理
            FolderBrowserDialog fbDlg = new FolderBrowserDialog();
            fbDlg.Description = "移動先のフォルダーを選択、または作成してください。";    //ダイアログの説明文
            fbDlg.SelectedPath = ".\\";                    //デフォルトのフォルダー
            if(fbDlg.ShowDialog() == DialogResult.OK)    //フォルダを選択するダイアログを表示する
            {
                foreach(ListViewItem item in file_List.Items)
                {
                    //全ファイルの移動(解説:移動できないファイルが存在する可能性があるので...try)
                    try
                    {
                        File.Move(item.SubItems[1].Text + "\\" + item.SubItems[0].Text, fbDlg.SelectedPath + "\\" + item.SubItems[0].Text);
                        item.Remove();    //処理完了ファイルはリストから削除する
                    }
                    catch
                    {
                        MessageBox.Show(item.SubItems[1].Text + "\\" + item.SubItems[0].Text + "\r\nのコピーに失敗しました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        continue;
                    }
                }
            }
            else
            {
                MessageBox.Show("キャンセルされました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            //オブジェクトを破棄する(解説:↓で述べる通り、C#ではダイアログは必ずDisposeする必要があります。)
            fbDlg.Dispose();
        }

        //「圧縮」処理(解説:Win11ではとっても簡単です。)
        private void OnCompress_Click(object sender, EventArgs e)
        {
            //Zipファイル作成用の暫定フォールダーの作成
            string temp = ".\\Temp";
            Directory.CreateDirectory(temp);
            //全ファイルのコピー
            foreach(ListViewItem item in file_List.Items)
            {
                try
                {
                    //既に同名ファイルがあった場合もエラーを出さない為に上書きする
                    File.Copy(item.SubItems[1].Text + "\\" + item.SubItems[0].Text, temp + "\\" + item.SubItems[0].Text, true);
                    item.Remove();    //処理完了ファイルはリストから削除する
                }
                catch
                {
                    MessageBox.Show(item.SubItems[1].Text + "\\" + item.SubItems[0].Text + "\r\nの圧縮に失敗しました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    continue;
                }
            }
            //「フォールダーを開く」処理
            FolderBrowserDialog fbDlg = new FolderBrowserDialog();
            fbDlg.Description = "圧縮先のフォルダーを選択、または作成してください。";    //ダイアログの説明文
            fbDlg.SelectedPath = ".\\";                    //デフォルトのフォルダー
            if(fbDlg.ShowDialog() == DialogResult.OK)    //フォルダを選択するダイアログを表示する
            {    //Zipファイルの作成
                /*    注意:ZipFileの使用には,
                    (1) "
using System.IO.Compression;"と、
                    (2)
System.IO.Compression.FileSystemへの参照(MSCompAssで指定する)
                    を追加する必要がある。*/

                ZipFile.CreateFromDirectory(temp, fbDlg.SelectedPath + "\\CompressedFileList.zip");
            }
            else
            {
                MessageBox.Show("キャンセルされました。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            //オブジェクトを破棄する(解説:↓で述べる通り、C#ではダイアログは必ずDisposeする必要があります。)
            fbDlg.Dispose();
            //temp内全ファイルの削除
            DirectoryInfo temp_dir = new DirectoryInfo(temp);
            foreach(FileInfo file in temp_dir.GetFiles())
            {
                file.Delete();
            }
            //tempフォルダーの削除
            temp_dir.Delete(true);
        }

        ///////////////////////////
        //コントロール関連メソッド
        ///////////////////////////
        //file_Listのコラムがクリックされた時

        private void OnLV_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            //ListViewItemSorterを指定する
            file_List.ListViewItemSorter = new ListViewItemComparer(e.Column);
            //正順、逆順交互に並び替える(ListViewItemSorterを設定するとSortが自動的に呼び出される)
        }

        //file_Listのアイテムが変更された時
        private void OnLV_SelectionChanged(Object sender, ListViewItemSelectionChangedEventArgs e)
        {
            if(file_List.SelectedItems.Count == 0)            //選択状態のチェック
            {
                tssl[2].Text = "(選択ファイル名)";
                tssl[2].ToolTipText = "(選択ファイル名)";    //ToolTip設定
            }
            else
            {
                tssl[2].Text = e.Item.Text;
                tssl[2].ToolTipText = e.Item.Text;            //ToolTip設定
            }
        }

        //file_Listにドラッグされた時
        private void LV_DragEnter(object sender, DragEventArgs e)
        {
            if(e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Copy;
            else
                e.Effect = DragDropEffects.None;
        }

        //file_ListViewにドロップされたとき
        private void LV_DragDrop(object sender, DragEventArgs e)
        {
            //ドロップされたファイルパスを取得
            string[] ddlist =  (string[])e.Data.GetData(DataFormats.FileDrop, false);
            //ドロップされたデータをリストビューに追加する
            foreach(string fn in ddlist)
            {
                //ファイルが存在するか、フォールダーではないか、をチェックする
                if(!File.Exists(fn))
                {
                    MessageBox.Show("このファイルは存在しません。\r\n" + fn, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    continue;
                }
                ListViewItem item = file_List.Items.Add(Path.GetFileName(fn));
                item.SubItems.Add(Path.GetDirectoryName(fn));
             }
            //ツールバーボタンを有効化する
            ChangeToolBarStatus(true);
            //ファイル数を更新する
            ShowNumOfFiles();
        }

        ///////////////////////
        //FileList共通メソッド
        ///////////////////////

        //ツールバーボタンの状態を有効、無効化する
        private void ChangeToolBarStatus(bool flag)
        {
            this.toolStripButton[1].Enabled = flag;    //解説:「ファイルリストの保存」
            for(int i = 4; i < 9; i++)
                this.toolStripButton[i].Enabled = flag;    //解説:「ファイルの追加、除外、削除、コピー、移動、圧縮」
        }

        //ファイル数を更新する
        private void ShowNumOfFiles()
        {
            tssl[1].Text = "ファイル数:" + file_List.Items.Count.ToString();
            tssl[1].ToolTipText = "ファイル数:" + file_List.Items.Count.ToString();    //ToolTip設定
        }

        //ファイルリストデータを取得する(DLL版用-解説:CardboardBoxとcsFileList間でデータのやり取りをするので)
        public string[] GetFileList()
        {
            //ファイルリストデータ引き渡し用
            string[] flData = new string[file_List.Items.Count];
            for(int i = 0; i < file_List.Items.Count; i++)
            {
                flData[i] = file_List.Items[i].SubItems[1].Text + "\\" + file_List.Items[i].SubItems[0].Text;
            }
            return flData;
        }

/*  既にFormクラスで定義されているので、再定義エラー
    「csFileList.FileList.Dispose()' は継承メンバー 'System.ComponentModel.Component.Dispose()' を隠します。
    意図的に隠す場合はキーワード new を使用してください。」
    が出るので、ダミー引数を入れ(オーバーロードさせ)てごまかしていた。*/
        //FileListクラスインスタンスのリソース開放用Dispose()メソッド(Microsoft Docs)
        public void Dispose(int i = 0)
        {
            //アンマネージドリソースの開放
            Dispose(true);
            //ガーベージコレクション(終了抑制)
            GC.SuppressFinalize(this);
        }
 

/* 解説:FileList.Dispose()について

前回のコードを見ていただければわかりますが、このcsFileList.csはエントリーポイントをコメントアウトしてダイナミックライブラリーにしてあります。(コメントアウトしているところを復活させるとスタンドアローンプログラムになりますが。)

従って、CardboardBoxプログラムに"using csFileList;"とし、csFileList,dllを参照してこのライブラリーが使えるようになります。

実際に使う場合は、"Show()"メソッドで呼び出すモードレスダイアログではメッセージボックス表示中にプログラムが進行してしまい塩梅が悪いので、"ShowDialog()"を使ってモーダルダイアログで呼び出す必要があります。

C#でモーダルダイアログを使う場合、↑で見てきた通り、必ずユーザーがリソースを開放する必要があります。(注)その為、本csFileListもユーザー定義でDispose()メソッドを規定通り追加することとなりました。既にFormクラスで定義されているのでメソッドの定義は不要です。(実際、メソッドを追加したことで、再定義エラー「csFileList.FileList.Dispose()' は継承メンバー 'System.ComponentModel.Component.Dispose()' を隠します。意図的に隠す場合はキーワード new を使用してください。」が出た為、ダミー引数を入れ(オーバーロードさせ)てごまかしていました。ゴメンなさい。

 

            注: ShowDialog(from Microsoft Docs)
            このメソッドを使用すると、アプリケーションにモーダルダイアログボックスを表示できます。
            このメソッドが呼び出されると、ダイアログ ボックスが閉じられるまで次のコードは実行されません。
            ダイアログボックスは、フォームのDialogResultプロパティに、DialogResultの一つを割り当てるか、
            コードでDialogResultの列挙値の1つをButtonに設定することができます。
            この値は、このメソッドによって返されます。この戻り値を使用して、ダイアログボックスで発生した
            アクションを処理する方法を決定できます。たとえば、ダイアログボックスが閉じられ、このメソッドを
            使用して値DialogResult.Cancelが返された場合、呼び出しShowDialogに続くコードが実行されないように
            することができます。
            フォームがモーダルダイアログボックスとして表示されている場合、[閉じる]ボタン(フォームの右上隅にある
            Xボタン) をクリックすると、フォームが非表示になり、DialogResultプロパティがDialogResult.Cancelに設定
            されます。非モーダルフォームとは異なり、ユーザーがダイアログボックスの閉じる(Close)フォームボタン
            をクリックしたり、プロパティの値DialogResultを設定したりしても、メソッドは.NET Frameworkによって
            呼び出されません。代わりに、フォームは非表示になり、ダイアログ ボックスの新しいインスタンスを作成
            せずに再度表示できます。ダイアログ ボックスとして表示されるフォームは閉じるのではなく非表示になる
            ため、フォームがアプリケーションで不要になった場合は、フォームのメソッドを呼び出す Dispose 必要
            あります。
            このバージョンのShowDialogメソッドでは、フォームまたはコントロールを所有者として指定しません。
            このバージョンが呼び出されると、現在アクティブなウィンドウがダイアログ ボックスの所有者になります。
            特定の所有者を指定する場合は、このメソッドの他のバージョンを使用します。


        //FileListクラスインスタンスのリソース開放用Dispose()メソッド(Microsoft Docs)→不要
        public void Dispose(int i = 0)
        {
            //アンマネージドリソースの開放
            Dispose(true);
            //ガーベージコレクション(終了抑制)
            GC.SuppressFinalize(this);
        }
*/

    }
}

///////////////////////////////////////////
//ListViewの項目の並び替えに使用するクラス
///////////////////////////////////////////

public class ListViewItemComparer : IComparer
{
    private int m_column;
    private static int m_order = 1;    //毎回呼ばれるたびに正順、逆順を交代させるフラグ(解説:に説明しました。)

    //ListViewItemComparerクラスのコンストラクタ
    public ListViewItemComparer(int col)
    {
        m_column = col;
        m_order *= -1;    //ListViewItemComparerが呼ばれる度に正順、逆順が変更される
    }

    //xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返す
    public int Compare(object x, object y)
    {
        //ListViewItemの取得
        ListViewItem itemx = (ListViewItem) x;
        ListViewItem itemy = (ListViewItem) y;
        //xとyを文字列として比較する
        return string.Compare(itemx.SubItems[m_column].Text, itemy.SubItems[m_column].Text) * m_order;
    }
}

 

csFileListをMSComAssでコンパイルするには、ターゲットをlibrary(dll)にし、System.IO.Compression.FileSystem.dllを参照する必要があります。

【csFileList.opt】

[Compile Option]
Target=3
Resource=1
RscFile=C:\Users\ysama\Programing\C# Programing\Projects\FileList\DLL版\csFileList.resources
IconFile=
DbgOpt=0
WarnErr=5
Others=
RefFile=C:\Windows\Microsoft.NET\assembly\GAC_MSIL\System.IO.Compression.FileSystem\v4.0_4.0.0.0__b77a5c561934e089\System.IO.Compression.FileSystem.dll

 

これでcsFileList.dllが完成しましたので、次はメインフォーム(ウィンドウ)であるCardboardBoxに移りましょう。

 

なんか、却ってわかりずらくなりそうですが、"CardboardBox"の第5回、csFileList.dllの解説第1回、という意味です。今日からはコードも解説します。

 

1.まずはリソースから

今回のプロジェクトで使ったリソースは以下の通り。実際には(3)をフリー素材から見つけ出し、ツールで改造して全く同じサイズのビットマップを二つ(「ごみ箱」をイメージして)作成し、「空の段ボール」ビットマップ(64x64)をもとに、縮小版(32x32)も作成してBCCSkeltonの自作ソフト"Iconviewer.exe"で作成し、最後に22年前の自作ツールバービットマップ編集ソフト("TBEditor.exe")でzip圧縮ビットマップも新作してcsFileListツールバー用のものを作成しました。

(1)システムアイコン("CardboardBox.ico")

(2)ツールバービットマップ("csFileList.bmp")

(3)段ボール(空)ビットマップ("CardboardBox-Empt.bmp")、(詰め)ビットマップ("CardboardBox-Full.bmp")

この内、自作ResourceWriterでcsFileListで用のリソースファイルファイル("csFileList.resources")に入れたのは(1)と(2)です。(1)がフォームの左上のアイコンで、(2)がツールバーボタンに張り付けられます。

 

2.csFileList.cs(どんがら編)

実際の処理を行うコードは次回とし、今回は↑の見てくれの部分(フォームのどんがら)についてポイントを青字で説明します。

 

/////////////////////////////////////
// csFileList.cs
// Copyright (c) 06/19/2023 by ysama
/////////////////////////////////////

using System;
using System.Collections;        //ListViewItemComparerクラスのIComparerを使う為
using System.Windows.Forms;
using System.Drawing;
using System.Reflection;        //Assemblyを使う為
using System.Resources;            //リソース関係クラス等の使用の為
using System.IO;                //Stream関係クラス等の使用の為
using System.IO.Compression;    //ZipFileを使う為
using System.Diagnostics;        //Processを使用する為

namespace csFileList       //解説:実行ファイル名csFileListと同じになります。
{
/*  解説:スタンドアローンの時に使います。

    /////////////////////////////////////
    //エントリーポイントクラス
    //動的ライブラリー(DLL)にする場合、
    //MainAppクラスをすべて削除すること
    /////////////////////////////////////
    class MainApp
    {
        [STAThread]
        public static void Main()
        {
            //コマンドライン引数を配列で取得する
            string[] cmds = System.Environment.GetCommandLineArgs();    //先頭は実行ファイル名
            if(cmds.Length > 1)
            {
                string[] args = new string[cmds.Length - 1];
                Array.Copy(cmds, 1, args, 0, cmds.Length - 1);
                Application.Run(new FileList(args));
            }
            else
                Application.Run(new FileList());
        }
    }
*/

    ///////////////////
    // FileListクラス
    ///////////////////

    public partial class FileList : Form
    {
        //クラスメンバー変数
        ToolStrip toolStrip;                //ツールバー
        ToolStripButton[] toolStripButton;    //ツールバーボタン
        StatusStrip statusStrip;            //ステータスバー
        ToolStripStatusLabel[] tssl;        //ステータスバーラベル
        ListView file_List;                    //ファイルリスト用リストビュー(解説:これでデータを保存します)
        string[] fn_args = null;            //コンストラクター引数を参照する配列

        public FileList(string[] fns = null)    //コンストラクターにファイルパス、名データを引き渡すことが出来ます
        {
            this.Load += new EventHandler(MainForm_Load);
            this.Text = "FileList";
            this.ClientSize = new Size(640, 341);    //Size = (656, 380);
            this.MinimumSize = new Size(656, 380);    //最小サイズ
            this.BackColor = SystemColors.Window;
            if(fns != null)    //引数がある場合、クラス内で参照できる変数に保存します
                fn_args = fns;      
        }

        //WM_CREATE時処理
        private void MainForm_Load(object sender, EventArgs e)
        {
            //ツールバーとステータスバー作成
            SetBars();
            //コントロール作成
            InitControls();    //今回、Visual Studioが使うメソッド名にしてみました。
        }
/*
        //WM_CLOSE時処理-DLLの場合は不要
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            DialogResult dr = MessageBox.Show("終了しますか?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if(dr == DialogResult.No)
            {
                e.Cancel = true;
            }
        }
*/


        //ツールバーとステータスバーの設定
        protected void SetBars()
        {
            //フォームのレイアウトを一時停止
            this.SuspendLayout();
            //ツールバーの配置
            //ToolStripクラスインスタンスの生成
            this.toolStrip = new ToolStrip();
            //ツールバーのレイアウトを一時停止
            this.toolStrip.SuspendLayout();
            //ToolStripButton配列を作成
            this.toolStripButton = new ToolStripButton[9];    //解説:csFileList.bmpのボタン数です。
            //本プログラムの埋め込みリソースのリソースマネージャーを作成
            Assembly asm = Assembly.GetExecutingAssembly();    //解説:自分自身(csFileList.exe)です。
            ResourceManager rm = new ResourceManager("csFileList", asm);    //解説:csFileList.resourcesを読みます。
            //アイコンをフォームにつける
            this.Icon = (Icon)rm.GetObject("Icon");    //解説:IconはCardboardBox.icoのID名です。
            //ツールバービットマップの読み込み
            ImageList imgList = new ImageList();
            imgList.ImageSize = new Size(16, 16);    //解説:TBEditorで作ったオリジナルは16x15ですが、修正しました。
            imgList.Images.AddStrip((Bitmap)rm.GetObject("ToolBar"));    //解説:ToolBarはcsFileList.bmpのID名です。
            imgList.TransparentColor = Color.White;    //解説:背景が白なので白色部分を透過させます。
            //ToolStripButton[0]を作成
            this.toolStripButton[0] = new ToolStripButton();
            this.toolStripButton[0].Text = "ファイルリストを開く(&O)";                //テキスト設定
            this.toolStripButton[0].Image = (Bitmap)imgList.Images[0];                //画像設定
            this.toolStripButton[0].DisplayStyle = ToolStripItemDisplayStyle.Image;    //画像表示のみ
            this.toolStripButton[0].Click += OnOpen_Click;                            //Clickイベントハンドラ追加
            this.toolStrip.Items.Add(this.toolStripButton[0]);                        //ボタンを追加
            //ToolStripButton[1]を作成
            this.toolStripButton[1] = new ToolStripButton();
            this.toolStripButton[1].Text = "ファイルリストの保存(&S)";                //テキスト設定
            this.toolStripButton[1].Image = (Bitmap)imgList.Images[1];                //画像設定
            this.toolStripButton[1].DisplayStyle = ToolStripItemDisplayStyle.Image;    //画像表示のみ
            this.toolStripButton[1].Click += OnSave_Click;                            //Clickイベントハンドラ追加
            this.toolStripButton[1].Enabled = false;                                //初期状態は無効
            this.toolStrip.Items.Add(this.toolStripButton[1]);                        //ボタンを追加
            //セパレーターを挿入
            this.toolStrip.Items.Add(new ToolStripSeparator());
            //ToolStripButton[2]を作成
            this.toolStripButton[2] = new ToolStripButton();
            this.toolStripButton[2].Text = "終了(&X)";                                //テキスト設定
            this.toolStripButton[2].Image = (Bitmap)imgList.Images[2];                //画像設定
            this.toolStripButton[2].DisplayStyle = ToolStripItemDisplayStyle.Image;    //画像表示のみ
            this.toolStripButton[2].Click += OnExit_Click;                            //Clickイベントハンドラ追加
            this.toolStrip.Items.Add(this.toolStripButton[2]);                        //ボタンを追加
            //セパレーターを挿入
            this.toolStrip.Items.Add(new ToolStripSeparator());
            //ToolStripButton[3]を作成
            this.toolStripButton[3] = new ToolStripButton();
            this.toolStripButton[3].Text = "ファイルを追加(&A)";                    //テキスト設定
            this.toolStripButton[3].Image = (Bitmap)imgList.Images[3];                //画像設定
            this.toolStripButton[3].DisplayStyle = ToolStripItemDisplayStyle.Image;    //画像表示のみ
            this.toolStripButton[3].Click += OnAdd_Click;                            //Clickイベントハンドラ追加
            this.toolStrip.Items.Add(this.toolStripButton[3]);                        //ボタンを追加
            //ToolStripButton[4]を作成
            this.toolStripButton[4] = new ToolStripButton();
            this.toolStripButton[4].Text = "ファイルを除外(&R)";                    //テキスト設定
            this.toolStripButton[4].Image = (Bitmap)imgList.Images[4];                //画像設定
            this.toolStripButton[4].DisplayStyle = ToolStripItemDisplayStyle.Image;    //画像表示のみ
            this.toolStripButton[4].Click += OnRemove_Click;                        //Clickイベントハンドラ追加
            this.toolStripButton[4].Enabled = false;                                //初期状態は無効
            this.toolStrip.Items.Add(this.toolStripButton[4]);                        //ボタンを追加
            //セパレーターを挿入
            this.toolStrip.Items.Add(new ToolStripSeparator());
            //ToolStripButton[5]を作成
            this.toolStripButton[5] = new ToolStripButton();
            this.toolStripButton[5].Text = "削除(&D)";                            //テキスト設定
            this.toolStripButton[5].Image = (Bitmap)imgList.Images[5];                //画像設定
            this.toolStripButton[5].DisplayStyle = ToolStripItemDisplayStyle.Image;    //画像表示のみ
            this.toolStripButton[5].Click += OnDelete_Click;                        //Clickイベントハンドラ追加
            this.toolStripButton[5].Enabled = false;                                //初期状態は無効
            this.toolStrip.Items.Add(this.toolStripButton[5]);                        //ボタンを追加
            //ToolStripButton[6]を作成
            this.toolStripButton[6] = new ToolStripButton();
            this.toolStripButton[6].Text = "コピー(&C)";                    //テキスト設定
            this.toolStripButton[6].Image = (Bitmap)imgList.Images[6];                //画像設定
            this.toolStripButton[6].DisplayStyle = ToolStripItemDisplayStyle.Image;    //画像表示のみ
            this.toolStripButton[6].Click += OnCopy_Click;                            //Clickイベントハンドラ追加
            this.toolStripButton[6].Enabled = false;                                //初期状態は無効
            this.toolStrip.Items.Add(this.toolStripButton[6]);                        //ボタンを追加
            //ToolStripButton[7]を作成
            this.toolStripButton[7] = new ToolStripButton();
            this.toolStripButton[7].Text = "移動(&M)";                                //テキスト設定
            this.toolStripButton[7].Image = (Bitmap)imgList.Images[7];                //画像設定
            this.toolStripButton[7].DisplayStyle = ToolStripItemDisplayStyle.Image;    //画像表示のみ
            this.toolStripButton[7].Click += OnMove_Click;                            //Clickイベントハンドラ追加
            this.toolStripButton[7].Enabled = false;                                //初期状態は無効
            this.toolStrip.Items.Add(this.toolStripButton[7]);                        //ボタンを追加
            //ToolStripButton[8]を作成
            this.toolStripButton[8] = new ToolStripButton();
            this.toolStripButton[8].Text = "圧縮(&P)";                                //テキスト設定
            this.toolStripButton[8].Image = (Bitmap)imgList.Images[8];                //画像設定
            this.toolStripButton[8].DisplayStyle = ToolStripItemDisplayStyle.Image;    //画像表示のみ
            this.toolStripButton[8].Click += OnCompress_Click;                        //Clickイベントハンドラ追加
            this.toolStripButton[8].Enabled = false;                                //初期状態は無効
            this.toolStrip.Items.Add(this.toolStripButton[8]);                        //ボタンを追加
            //ツールバーの設定
            this.Controls.Add(this.toolStrip);
            //ツールバーのレイアウトを再開
            this.toolStrip.ResumeLayout(false);
            this.toolStrip.PerformLayout();
            //ステータスバーの配置
            //StatusStripクラスインスタンスの生成
            this.statusStrip = new StatusStrip();
            //ステータスバーのレイアウトを一時停止
            this.statusStrip.SuspendLayout();
            //ステータスバーにパネルとテキストを追加
            tssl = new ToolStripStatusLabel[3];
            tssl[0] = new ToolStripStatusLabel();
            tssl[0].BorderSides = ToolStripStatusLabelBorderSides.All;
            tssl[0].BorderStyle = Border3DStyle.SunkenInner;
            tssl[0].BackColor = SystemColors.Control;
            tssl[0].Text = "FileList Ver. 1.0";
            tssl[0].AutoSize = true;
            tssl[0].TextAlign = ContentAlignment.MiddleLeft;
            tssl[1] = new ToolStripStatusLabel();
            tssl[1].BorderSides = ToolStripStatusLabelBorderSides.All;
            tssl[1].BorderStyle = Border3DStyle.SunkenInner;
            tssl[1].BackColor = SystemColors.Control;
            tssl[1].Text = "(選択ファイル数)";
            tssl[1].ToolTipText = "(選択ファイル数)";    //ToolTip設定
            tssl[1].AutoSize = true;
            tssl[1].TextAlign = ContentAlignment.MiddleLeft;
            tssl[2] = new ToolStripStatusLabel();
            tssl[2].BorderSides = ToolStripStatusLabelBorderSides.All;
            tssl[2].BorderStyle = Border3DStyle.SunkenInner;
            tssl[2].BackColor = SystemColors.Control;
            tssl[2].Text = "(選択ファイル名)";
            tssl[2].ToolTipText = "(選択ファイル名)";    //ToolTip設定
            tssl[2].Spring = true;
            tssl[2].TextAlign = ContentAlignment.MiddleLeft;
            statusStrip.Items.AddRange(tssl);
            statusStrip.ShowItemToolTips = true;        //ToolTip表示
            this.Controls.Add(this.statusStrip);        //StatusStrip(ステータスバー)を追加
            //ステータスバーのレイアウトを再開
            this.statusStrip.ResumeLayout(false);
            this.statusStrip.PerformLayout();
            //メインフォームのレイアウトを再開
            this.ResumeLayout(false);
            this.PerformLayout();
        }

        //コントロールの設定
        protected void InitControls()
        {
            /////////////////////
            //コントロールの配置
            /////////////////////
            //リストビューの生成とプロパティの設定

            file_List = new ListView();
            file_List.Size = new Size(ClientSize.Width - 20, ClientSize.Height - toolStrip.Height - 10);    //サイズ
            file_List.Location = new Point(10,  toolStrip.Height);    //位置
            file_List.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right);
            file_List.FullRowSelect = true;                //一行選択
            file_List.GridLines = true;                    //グリッドラインの表示
            file_List.MultiSelect = true;                //複数選択を許す(解説:ファイルの追加と除外で複数ファイルに対応)
            file_List.HoverSelection = false;            //ポイントで選択できるようにする
            file_List.Activation = ItemActivation.OneClick;    //シングルクリック選択
            file_List.CheckBoxes = false;                //チェックボックス有効化(解説:をfalseでしない、ということです。)
            file_List.View = View.Details;                //リストビューの表示方法(解説:リスト形式です。)
            //イベントハンドラの追加
            file_List.ColumnClick += new ColumnClickEventHandler(OnLV_ColumnClick);    //解説:昇順、降順でソートさせます。
            file_List.ItemSelectionChanged += new ListViewItemSelectionChangedEventHandler(OnLV_SelectionChanged);    //解説:ステータスバーのファイル数と選択ファイルパス、名を表示させます。
            //ヘッダー定義
            file_List.Columns.Add("ファイル名", 200, HorizontalAlignment.Left);    //解説:フルパス名だと見難いので
            file_List.Columns.Add("ファイルパス", 416, HorizontalAlignment.Left);    //分離しました。
            //Drag and Dropのイベントハンドラの追加
            file_List.DragEnter += new DragEventHandler(LV_DragEnter);
            file_List.DragDrop += new DragEventHandler(LV_DragDrop);
            file_List.AllowDrop = true;
            //FormにListViewを追加
            this.Controls.Add(file_List);
            //コンストラクター引数(ファイルパス、名)がある場合
            if(fn_args != null)
            {
                foreach(string fn in fn_args)
                {
                    //ファイルが存在するか、フォールダーではないか、をチェックする
                    if(!File.Exists(fn))
                    {
                        MessageBox.Show("このファイルは存在しません。\r\n" + fn, "", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        continue;
                    }
                    ListViewItem item = file_List.Items.Add(Path.GetFileName(fn));    //解説:ファイル名のみをItemに、
                    item.SubItems.Add(Path.GetDirectoryName(fn));    //解説:ファイルパスのみをSubItemに入れます。
                }
                //ツールバーボタンを有効化する(解説:引数のboolで初期状態で無効のボタンを有効にします。)
                ChangeToolBarStatus(true);
                //ファイル数を更新する(解説:ファイルの追加削除の際に再カウントします。)
                ShowNumOfFiles();
            }
            else
                //ツールバーボタンを初期化する(解説:ファイルが無ければ無効(false)にします。)
                ChangeToolBarStatus(false);
        }

 

こんな感じでしょうか?次回はListView(file_List)に投入されたファイルの処理、リストビューのソート用クラスとDLLでカスタムダイアログを作る場合の注意点等をやります。

 

前々回書いたように、少し自分なりに使ってみてcsFileListが一応思った通り動いていますので、CardboardBoxの方も実装を進めまして、その前に書いた粗仕様↓

 

(1)ウィンドウ(フォーム)は「段ボール箱」のビットマップだけを表示。(それ以外は背景を透過させたい。)

(2)ウィンドウ(フォーム)の移動はマウスでドラッグと、キーによる移動が可能にする。

(3)投げ入れるファイルは、起動時のコマンドライン引数(ファイルを投げ込んで起動する)とドラッグアンドドロップ(起動後にファイルを投げ込む)だけにしようかな、と思いましたが、BCCSkeltonで作って自分で愛用しているアルバムソフト(Album.exe)で使うファイルリスト(*.alb)は読み込めるようにするかな?

(4)「ごみ箱」と同じように、ファイルが投げ込まれているときはそれが分かる段ボール箱イメージにしたい。

(5)ファイル処理は「マウス右クリック」でポップアップメニューを出し、「終了」(、ファイルリスト(*.alb)読み込み)と「ファイルリストの表示」をさせ、「削除」、(新規作成可能なフォールダーダイアログで先を指定して)「移動」と「コピー」はマスト。

(6)追加でZIP圧縮これもフォールダーを作成してそれ毎圧縮)なんかできるとよいかな?と思います。

(7)最終的にはタスクトレィに入れておいて常時使える。

ようにしてはどうかな、と思います。(上記粗仕様のうち、私が未だC#プログラミングで経験していないものを赤にしています。従って、これはその習作ソフトになるということです。また、前に作った習作から再利用できるコードが結構ありそうです。)

 

を満たすVersion 1.0が本日仕上がりました。

(使い勝手から、メインウィンドウである「段ボール箱」は最前面表示としています。)

 

今回↑の赤字部分の他、C#のダイアログ作成上の注意(特にDospose())や圧縮を含むファイル処理等、色々と学習しましたので、おいおいcsFileListとCardboardBoxのコードを紹介しながら書いていこうかと考えます。

 

さて、一応"CardboardBox"プロジェクトの本体部分(csFileList.exe)はプロトタイプが出来上がったので、次はメインウィンドウである「段ボール箱」の仕上げを手掛けるのですが、本日本体部分のソースコード(csFileList.cs)の見直しをしましたので、その際にトピックになると思える箇所を引用して解説してみたいと思います。

 

1.メインフォームのウィンドウアイコンの設定

MSCompAssでコンパイルする際にOptionで「プログラムアイコン」を設定していると、コンストラクターかLoadイベント(WM_CREATE処理)あたりで、

        Assembly myOwn = Assembly.GetEntryAssembly();
        this.Icon = Icon.ExtractAssociatedIcon(myOwn.Location);    //プログラムアイコンをフォームにつける

として同じものを表示できますが、別途リソースに入れたものを表示することも可能です。csFileListは最終的にDLLにするのでそっちを選びました。その場合は次のようになりますね。(リソースファイルは"csFileList.resources")

        //本プログラムの埋め込みリソースのリソースマネージャーを作成
        Assembly asm = Assembly.GetExecutingAssembly();
        ResourceManager rm = new ResourceManager("csFileList", asm);
        //アイコンをフォームにつける
        this.Icon = (Icon)rm.GetObject("Icon");

 

2.コマンドラインの引数の取得

MS-DOS時代と同じく、現在もコマンドベースでは、

        "C:\(current directory)>(実行ファイルパス、名) (引数1) (引数2) (引数3) ..."

と打ちます。これと同じことをWYSWYGベースでは

        「引数となるファイル(複数可)をドラッグして、実行ファイルアイコンにドロップする」

のように行います。

そうするとシステム(OS)には↑のスペース区切り文字列が保存され、「大昔のC言語のエントリーポイント処理の定番」である、

       "int main(int argc, char** argv);"(または "int main(int argc, char *argv[]);")

でプログラマーが引数の個数(argc-実行ファイル自身もあるので必ず1以上)、二つ目から引数を受け取ってプログラムすることが出来ました。

C#でも↓のようにしてcmds[0]-実行ファイルのパス、名、cmds[1]-引数1、cmds[2]-引数2...を取得することが出来ます。

        //コマンドライン引数を配列で取得する
        string[] cmds = System.Environment.GetCommandLineArgs();    //先頭は実行ファイル名

 

3.ドラッグアンドドロップ

上記2は起動時のドラッグアンドドロップですが、一旦起動した後にドラッグアンドドロップをBCCSkelton、ECCSkeltonで行うには、WM_CREATEメッセージを受けた場合のOnCreate関数段階で、

        //ドラッグアンドドロップの受付開始
        DragAcceptFiles(m_hWnd, TRUE);

とし、WM_DROPFILESメッセージを受けた場合のOnDropFiles関数で

        //ドラッグアンドドロップされたファイル名取得と処理
        static char FileName[MAX_PATH];
        int n;                                    //ファイル数
        HDROP hDrop = (HDROP)wParam;             //HDROPを取得
        n = DragQueryFile(hDrop, -1, NULL, 0);    //ファイル数を取得
        for(int i = 0; i < n; i++) {
            DragQueryFile(hDrop, i, FileName, MAX_PATH);    //ファイル名を取得
            //
ユーザー処理

        }

        //ドラッグアンドドロップの終了
        DragFinish((HDROP)wParam);
        return TRUE;

等としましたが、C#でも簡単で、次の作法となります。

(1)フォームやコントロールの定義段階

        ①フォーム

        //Drag and Dropを受け入れる
        this.AllowDrop = true;

        ②コントロール

        //Drag and Dropのイベントハンドラの追加と受け入れ
        (Control).DragEnter += new DragEventHandler(LV_DragEnter);
        (Control).DragDrop += new DragEventHandler(LV_DragDrop);
        (Control).AllowDrop = true;

(2)メソッド定義段階

        ①フォーム(イベントのオーバーライド処理を行う)

        //ファイルをドラッグされた時
        protected override void OnDragEnter(DragEventArgs e)
        {
            base.OnDragEnter(e);
            if(e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Copy;
            else
                e.Effect = DragDropEffects.None;
        }


        //ファイルをドロップされたとき
        protected override void OnDragDrop(DragEventArgs e)
        {
            base.OnDragDrop(e);
            //ドロップされたファイルパスを取得
            string[] files =  (string[])e.Data.GetData(DataFormats.FileDrop, false);
            //
ユーザー処理
        }

        ②コントロール(イベントハンドラーでメソッドを定義する)

        //ファイルをドラッグされた時
        private void LV_DragEnter(object sender, DragEventArgs e)
        {
            if(e.Data.GetDataPresent(DataFormats.FileDrop))
                e.Effect = DragDropEffects.Copy;
            else
                e.Effect = DragDropEffects.None;
        }


        //ファイルをドロップされたとき
        private void LV_DragDrop(object sender, DragEventArgs e)
        {
            //ドロップされたファイルパスを取得
            string[] ddlist =  (string[])e.Data.GetData(DataFormats.FileDrop, false);

            //ユーザー処理
        }

 

4.ListViewクラス

リストビューはC++をSKDベースで使う時は結構面倒です。その為、BCCSkelton、ECCSkeltonでCLISTVIEWクラスを作って簡素化しました。例えば、リストビューからのメッセージを受けた際のWM_NOTIFYメッセージで次のように処理します。

        //リストビューの通知メッセージによる処理
        switch(((LPNMLISTVIEW)lParam)->hdr.code) {

        case LVN_COLUMNCLICK:    //列見出しがクリックされたら(注)
            if(sortsubno[((LPNMLISTVIEW)lParam)->iSubItem] == UP)
                sortsubno[((LPNMLISTVIEW)lParam)->iSubItem] = DOWN;
            else
                sortsubno[((LPNMLISTVIEW)lParam)->iSubItem] = UP;
            m_ListView.SortItem(CompProc, ((LPNMLISTVIEW)lParam)->iSubItem);
            break;

        case NM_DBLCLK:        //左ダブルクリックしたら

            //ユーザー処理
            break;
        case NM_RCLICK:        //右クリックしたら
            //ウインドウの位置情報を取得
            RECT rec;
            GetWindowRect(m_hWnd, &rec);
            //ポップアップメニューの表示場所を設定
            POINT pt;
            GetCursorPos(&pt);
            //ポップアップメニューの表示
            TrackPopupMenu(g_hPopup, TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON, pt.x, pt.y, 0, m_hWnd, &rec);
            break;
        }

注:↑の紫字の列見出しクリックの処理はリストビューアイテムが正順、逆順交互にソートされます。

 

C#の場合、カプセル化が進んで更に簡単になりましたが、列見出しクリックの処理でMicrosoftがサンプルとして公開しているコードが「正順にしかソートしない」ので、次の通り改造してトグル動作で正順、逆順交互にソートされる(注)ようにしました。

 

        //ListViewのコラムがクリックされた時
        private void OnLV_ColumnClick(object sender, ColumnClickEventArgs e)
        {
            //ListViewItemSorterを指定する
            (ListViewインスタンス).ListViewItemSorter = new ListViewItemComparer(e.Column);
            //
正順、逆順交互に並び替える(ListViewItemSorterを設定するとSortが自動的に呼び出される)
        }

 

///////////////////////////////////////////
//ListViewの項目の並び替えに使用するクラス
///////////////////////////////////////////
public class ListViewItemComparer : IComparer
{
    private int m_column;
    
private static int m_order = 1;    //毎回呼ばれるたびに正順、逆順を交代させるフラグ

    // ListViewItemComparerクラスのコンストラクタ
    public ListViewItemComparer(int col)
    {
        m_column = col;
        
m_order *= -1;    //ListViewItemComparerが呼ばれる度に正順、逆順が変更される
    }

    //xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返す
    public int Compare(object x, object y)
    {
        //ListViewItemの取得
        ListViewItem itemx = (ListViewItem) x;
        ListViewItem itemy = (ListViewItem) y;
        //xとyを文字列として比較する
        return string.Compare(itemx.SubItems[m_column].Text, itemy.SubItems[m_column].Text)
* m_order;
    }
}

注:コンストラクターで呼ばれるたびに"1"と"-1"に変化する静的変数m_orderを使い、ソートの際に使用されるCompareメソッドの評価結果にm_orderを乗じて符号を反転させることにより正順、逆順のトグル動作を行います。

 

まぁ、今回のプログラミングで印象に残ったのがここらへんでしたのでご紹介させていただきました。

 

前に触れたように、まーさに「走りながら考える」タイプの私らしい発展がありました。"CardboardBox"の実質的な処理部分となるのは(BCCSkeltonで書いた)"FileList"類似のウィンドウプログラム、ということで、前回「こんなイメージ」

としたC#による"csFileList"のVersion 1.0(らしきもの)の初お目見えです。

概要は、

(1)サイズ変更可能ウィンドウで、現在はスタンドアローンウィンドウプログラムですが、最終的には「段ボール箱」から呼び出す(DLLベースの)ダイアログにする予定です。

 

(2)スタンドアローンでは、

 ①起動時の引数にファイルパス、名を与える(注)

 ②ListViewにドラッグアンドドロップを行う

 ③「ファイルリストを開く」ボタン(ツールバー左端)で、第1フィールドがファイルパス、名となっているCSVファイルを読み込む。(因みに↑のサンプル画像ではBCCSkeltonのAlbum用の*.albファイルを読み込んだところです。)

 ④ファイルの追加()、除外()を「ファイルを開く」ダイアログで行う(複数選択可)

で、ファイルを放り込む仕様としています。

注:実際には投入ファイルをドラッグしてcsFileListのアイコンにドロップすることでファイルのパス、名を引数として与えられ、csFileListはListViewにおれ(ら)を表示して立ち上がります。DLLでは呼び出す際にコンストラクターにファイルパス、名の配列を引数として渡す予定です。

 

(3)ユーザーへの情報表示としては、ListViewに「ファイル名」「ファイルパス」を表示、ステータスバーに投入された「ファイル数」と選択されている「ファイル名」を表示します。(「選択される」ことは、「ファイルの除外()」でのみ意味があるだけなんですが。)

 

(4)ファイルの一括処理は「削除(ツールバー×ボタン)」「コピー(ツールバー右から3つ目)」「移動(ツールバー右から2つ目)」「Zip圧縮(ツールバー右端)」ができ、処理のUIは(今どきメニューはあまり使わないので)ツールバーボタンのみにしました。

 

取り敢えず、自分で実際に使ってみて発生する不具合や改善点を手直ししてからコードを公表する(同時にBCCForm and BCCSkeltonパッケージのMSCompAssのサンプルに追加)予定です。

 

使い道としては、

(1)「削除」ー基本的に「ごみ箱アイコン」から「段ボールのウィンドウ」になっただけなので新味はないかも、です。

(2)「コピー」、「移動」-Explorerだと「同一フォールダー」内ではShift+クリックかCtrl+クリックでファイルの複数選択ができますが、別のフォールダーのファイルを加えるとなると選択が解除されるのでどこかに暫定フォールダーを設けるなど、手立てが必要になります。そういう意味で、PC内のあちこちに散らばったファイルをポイポイと放り込んで、一括処理できるところが本ソフトの売りじゃないか、と思います。

(3)「Zip圧縮」-こいつの処理手順は、

 ①カレントフォールダーにTempというフォールダーを作り、

 ②ListViewにあるファイルをコピーし、

 ③Zipファイルの作成フォールダーを指定し、

 ④Tempを丸ごと圧縮して③ノフォールダーに作成(ファイル名は"CompressedFileList.zip")し、

 ⑤後はTemp内のファイル、次にTempフォールダーを削除する

という処理です。(これは断捨離する勇気がないけど、スペースを空けたいような場合にはよいかも?)

 

私自身は矢張り、現在実際に使っている自作ソフト(Album、DirectShow等)のファイルリスト(ファイルパス、名がCSVファイルとなっています)の処理で結構使えるかも、です。例えば↑のように、旅行写真を集めて圧縮ファイルでは人にあげるような場合は勿論別々の旅行でフォールダーが分かれている写真から、特定の被写体の人の写真を集めて圧縮ファイルにするのなんかが重宝しそうです。

 

前回紹介した「完成形イメージ」にむけて、(若い頃からの私の得意技である)「走りながら考え」始めました。

 

具体的には、

(1)「空」と「詰まった」段ボール箱のビットマップをタイトルバーのない透明のウィンドウに張り付け、「段ボール箱ウィンドウ」を試作し、マウスクリックによりビットマップを交代するようにしてみました。

(2)次にそのウィンドウをドラッグアンドドロップを可能にし、ファイルを受け付けられるようにしました。(起動時のコマンドラインからの引数取得も追加する予定です。)

(3)「段ボール箱ウィンドウ」を矢印キー又はマウスでドラッグして移動させるようにしました。

(4)この「段ボール箱ウィンドウ」の処理選択は、右マウスクリックでポップアップメニューを出すことにしました。メニューにはファイル処理や「終了」の他、ファイルを投げ入れるだけではなく、既存のファイルリスト(注)を読み込むこともできるようにしようと思います。

(5)後は実際のファイル処理になるのですが...

注:BCCSkeltonで作成したAlbumのアルバムファイル(*.alb)やDirectShowのプレイリストファイル(*.plf)を考えています。Renamerも考えましたが、パスでよいかと...

 

ここではたと考えました。「削除」、「コピー」、「移動」、「圧縮」等の実際のファイル処理は(過ってファイルを投げ入れたり、途中で気が変わることもあるでしょうから)投げ入れられたファイルをリストで目で見て確認し、最終的に処理を行うことが求められると思います。ということは結局、別途「段ボール箱ウィンドウ」以外のダイアログ等ウィンドウで行う必要があります。

 

ん?

 

要すれば「結局、リストを表示するウィンドウに、既存リストの読み込み(による追加)や、リストの追加、削除等リスト編集機能を持たせ、編集されたリストに基づき「削除」、「コピー」、「移動」、「圧縮」等のファイル処理機能を持たせなければならないんじゃないか?それって何か、昔BCCSkeltonで作った"FileList"ライブラリー(DLL)っぽいな?」という思いになってきました。

 

だったら、C#で「新」FileList.dllを作っちゃえ!

 

ということで現在そっちの開発も同時並行で(夏に向けて冷やし中華のように)始めました。

 

どうなることやら?

 

ps. 結局メインのウィンドウプログラムは、(ファイル処理を行わないので)「単にファイルを投げ込むだけの段ボール箱ウィンドウ」になる運命です。とほほ。

 

前回思い付きで書いた「Explorerで見つけたファイルをポンポンいれ、入れられたファイルを纏めて処理する段ボール箱」デスクトップツール

を"CardboardBox"と命名し、設計(変更も!)、仕様決定を含めてC#()でプログラミングし、同時進行でブログネタにしようと思います。

:"BCCForm and BCCSkeltonサポートセンター”の趣旨に合わないことは重々承知の上ですが、現在私が64bitプログラミングができる環境はcsc.exeとMSCompAssしかないのと、コンポーネントが充実しており見た目が分かりやすく簡素なので、今回もこれを使ってみることにします。

 

取り敢えずはツールの完成形イメージとして、

(1)ウィンドウ(フォーム)は「段ボール箱」のビットマップだけを表示。(それ以外は背景を透過させたい。)

(2)ウィンドウ(フォーム)の移動はマウスでドラッグと、キーによる移動が可能にする。

(3)投げ入れるファイルは、起動時のコマンドライン引数(ファイルを投げ込んで起動する)とドラッグアンドドロップ(起動後にファイルを投げ込む)だけにしようかな、と思いましたが、BCCSkeltonで作って自分で愛用しているアルバムソフト(Album.exe)で使うファイルリスト(*.alb)は読み込めるようにするかな?

(4)「ごみ箱」と同じように、ファイルが投げ込まれているときはそれが分かる段ボール箱イメージにしたい。

(5)ファイル処理は「マウス右クリック」でポップアップメニューを出し、「終了」(、ファイルリスト(*.alb)読み込み)と「ファイルリストの表示」をさせ、「削除」、(新規作成可能なフォールダーダイアログで先を指定して)「移動」と「コピー」はマスト。

(6)追加でZIP圧縮(これもフォールダーを作成してそれ毎圧縮)なんかできるとよいかな?と思います。

(7)最終的にはタスクトレィに入れておいて常時使える。

ようにしてはどうかな、と思います。(上記粗仕様のうち、私が未だC#プログラミングで経験していないものをにしています。従って、これはその習作ソフトになるということです。また、前に作った習作から再利用できるコードが結構ありそうです。)

 

そんなこんなで、(若い頃からの私の得意技である)「走りながら考える」ことで本プロジェクト(注)をスタートさせましょう。

注:私はC#プログラミングでVisual Studioを使っていないので、「プロジェクトファイル」はなく、単に「フォールダー管理」しているだけですが...(爆笑)

 

なお、↓は最初の段ボールが余り気にいらななかったので、フリー素材サイトから似たような「空の段ボール」と「詰まった段ボール」イメージを持ってきてちょっと整えたビットマップ画像です。これでファイルが入っている時の "Don't worry! I'm wearing!" 感が増したことと思います。どうでしょう?

 

>>>> 当日修正内容は末尾を参照してください。<<<<

 

C#でバイオリズムグラフ(を表示するだけ)のカスタムコントロールを作ってから大分経ったので、お久しぶり感が漂いますね。

 

現在はまた恒例の「ネタ探し」をしていますが、ちょっと思うところがあり、「Explorerで見つけたファイルをポンポンいれる段ボール箱」で「纏めて入れられたファイルを処理する」デスクトップツールを検討しています。

(絵心のない雑な段ボール箱ですね。)

 

ただ、どういうわけか、この段ボール箱のビットマップ、ResourceManagerからGetObjectやGetStreamで読みだそうと思ってもエラーがでます。なんでだろ?現在探求中です。

 

【参考迄-現在のエラーのでるコード抜粋】

        //ウィンドウ画像
        private Image img;
        private Bitmap bmp;
        // コンストラクタ
        public MainWnd()
        {
            // フォームのロードイベントメソッドを設定
            this.Load += MainWnd_Load;
            // フォームのキーダウンイベントメソッドを設定
            this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.MainWnd_KeyDown);
            //ResourceManagerインスタンスの作成
            System.Reflection.Assembly asm = System.Reflection.Assembly.GetExecutingAssembly();
            ResourceManager rm = new ResourceManager("CardboardBox.CardboardBox", asm);
            rm = new ResourceManager("CardboardBox.CardboardBox", asm);
        try {
            //data name="CBBox"というBitmapを読み込む
            bmp = (Bitmap)Image.FromStream(rm.GetStream("CBBox"));
        }
        catch {
            MessageBox.Show("Bitmapが読めませんでした", "エラー - GetStream", MessageBoxButtons.OK,

              MessageBoxIcon.Error);    //エラーが出る
        }
        try {
            //data name="CBBox"というBitmapを読み込む
            bmp = (Bitmap)rm.GetObject("CBBox");
        }
        catch {
            MessageBox.Show("Bitmapが読めませんでした", "エラー - GetObject", MessageBoxButtons.OK, 

              MessageBoxIcon.Error);    //エラーが出る
        }
            bmp.MakeTransparent(Color.White);
            img = bmp;
        }

 

当日修正:本日問題が判明して正常に動いています。その原因は、いくつかの凡ミスが複合していました。

(1)リソースファイル名が"Cardbord.resources"とスペルミスしていた。(リソースマネージャーインスタンス作成ミス)

(2)ResourceManagerのコンストラクターで「(名前空間).(拡張子抜きリソースファイル名)」としていましたが、単に「(拡張子抜きリソースファイル名)」とすべきでした。(リソースマネージャーインスタンス作成ミス)

(3)BitmapリソースをGetStreamとGetObjectで取得しようとしていますが、Bitmapリソースの取得にはGetObjectを使用すべきでした。(GetStreamを使用するのは、その他のGDI+画像であるjpg等、SystemIO.PinnedBufferMemoryStreamオブジェクトの場合に使用します。→引っ張ってきた過去のサンプルがjpg画像でした!老人のボケは怖い!!)

 

では、前回作ったBiorythm.dllを実際に使ってみます。

 

プログラムファイルはTest_Biorythm.csと言いますが、内容はウィンドウズ版のBiorythmの不要部分をそぎ落としたものです。That is(それは即ち)、ダイアログ風のフォームを作っただけです。バイオリズムの機能はすべてコントロールで完結しています。(赤字部分)DLLにすると使う側のプログラムがとても簡潔になりますし、何度も再利用ができるので助かります。

ただしっ、

Biorythm.dllは単にバイオリズム曲線(正弦波曲線)を書くだけですので、家族みんなのバイオリズム曲線を一回見たら後はもう触らないと思いますが。(爆;)まぁ、「グラフ表示」ボタンを押したら、ウィンドウサイズを色々と変えてみてまた「グラフ表示」ボタンを押してください。ちょびっと楽しいかも。

 

【Test_Biorythm.cs】

//////////////////////////////
//    C# Test_Biorythm.cs
// Biorythm Control Component
//////////////////////////////

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Reflection;                //Assemblyを使う為
using Biorythm;                            //Biorythm.dllを使う為

namespace Test_Program
{
    public partial class MyForm : Form
    {
        BrmBox brmBox;                        //BrmBox(クラス名)コントロール
        Button btn1, btn2, btn3, extbtn;    //ボタンコントロール

        [STAThread]
        public static void Main()
        {
            MyForm mf = new MyForm();
            Application.Run(mf);
        }

        public MyForm()
        {
            Assembly myOwn = Assembly.GetEntryAssembly();
            this.Icon = Icon.ExtractAssociatedIcon(myOwn.Location);    //プログラムアイコンをフォームにつける
            this.Size = new Size(640, 540);
            this.MinimumSize = new Size(320, 190);
            this.Text = "Biorythm Cycle";
            this.Load += MyForm_Load;
        }

        private void MyForm_Load(object sender, EventArgs e)
        {
            //ボタン1
            btn1 = new Button();
            btn1.Location = new Point(ClientSize.Width - btn1.Width - 10, 10);
            btn1.Text = "生年月日";
            btn1.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            btn1.Click += Button1_Click;
            this.Controls.Add(btn1);

            //ボタン2ボタン
            btn2 = new Button();
            btn2.Location = new Point(ClientSize.Width - btn2.Width - 10, btn1.Height + 20);
            btn2.Text = "指定日";
            btn2.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            btn2.Click += Button2_Click;
            this.Controls.Add(btn2);

            //ボタン3ボタン
            btn3 = new Button();
            btn3.Location = new Point(ClientSize.Width - btn3.Width - 10, btn1.Height + btn2.Height + 30);
            btn3.Text = "グラフ表示";
            btn3.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            btn3.Click += Button3_Click;
            this.Controls.Add(btn3);

            //終了ボタン
            extbtn = new Button();
            extbtn.Location = new Point(ClientSize.Width - extbtn.Width - 10, ClientSize.Height - extbtn.Height - 20);
            extbtn.Text = "終了";
            extbtn.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right);
            extbtn.Click += Button4_Click;
            this.Controls.Add(extbtn);

            //BrmBoxコントロール
            brmBox = new BrmBox();                        //BrmBoxオブジェクトの作成
            brmBox.BorderStyle = BorderStyle.Fixed3D;
            brmBox.Location = new Point(10, 10);        //位置
            brmBox.Width = ClientSize.Width - btn1.Width - 30;
            brmBox.Height = ClientSize.Height - 20;
            brmBox.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right);
            this.Controls.Add(brmBox);                    //brmBoxをコントロールに追加する
        }

        //終了処理
        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            DialogResult dr = MessageBox.Show("終了しますか?", "確認", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
            if(dr == DialogResult.No)
            {
                e.Cancel = true;
            }
        }

        private void Button1_Click(object sender, EventArgs e)
        {
            //ボタン1
            brmBox.SetDOB();
        }

        private void Button2_Click(object sender, EventArgs e)
        {
            //ボタン2
            brmBox.SetDOT();
        }

        private void Button3_Click(object sender, EventArgs e)
        {
            //ボタン3
            brmBox.Display();
        }

        private void Button4_Click(object sender, EventArgs e)
        {
            //終了処理
            Close();
        }
    }
}
 

Biorythm.dllのお話はあと一回続きますが、今日は雑談を。

 

「ボケ防止」ということで、20年以上前に公表したBCCForm and BCCSkeltonのフォローアップをと、20年ぶりにC++やWin32を再勉強し始めたのが完定(完全定年)した2020年。一応2002年のBorland C++用のファイルをEmbarcadero bcc32c.exe用に皆アップデートして再度更新版をアップしたのが2021年。その後、"stay away"していたUnicode対応版ECCSkeltonや、COMサービスのクラス化などで遊んできましたが、気が付くと世は「64bit時代」。遅れてはならじとC#も学習し、重装備のVisual Studioでなくても昔のROM Basic感覚でプログラミングできるMSCompAssでリソース周りとファイルの文字列一括置換アプリなどを作って喜んでいたら、とうとう5月14日で二年が過ぎてしまいました。

 

「よく続いたな。」「結構、頑張ったよね。」という感慨と「そろそろ息切れだよな。」という惧れの入り混じった気持ちですが、

 

ホビーとして、

パズルとして、

(新しいことを知ろうとする)「知的好奇心という欲望」を満たすため、

そして

ボケ防止

 

の為に、もう少し走り続けて(プログラミングして)みようと思う今日日(きょうび、早朝ウォーキング中にそう思った)でありました。