のにっき

【VS】リストボックスの背景色をノードごとに変更する

Windowsフォームアプリのリストボックスです。
リストボックスの背景色を変更するのは意外とめんどくさかったので
手法をまとめようと思います。

フォームを作ってみた

f:id:apuridasuo:20200729131910p:plain
図:背景色を変更させたListBox

上図が、実際に作成したフォームの画像になります。
ノード背景を1つずつ変更させることで、見やすい背景色になってます。
背景色を変更させるためには、
プロパティ設定イベント組み込みを行う必要があります。

プロパティ設定

DrawMode切り替え
f:id:apuridasuo:20200729132246p:plain
図:切り替え後のプロパティ画面(プロパティ)

上図のように、背景色を変更したいListBoxのプロパティ設定の項目
「 DrawMode = OwnerDrawFixed 」
に変更します。
この設定を変更することで、後に記述します「 DrawItem
イベントを有効にすることが出来ます。

DrawItemイベント設定
f:id:apuridasuo:20200729132912p:plain
図:切り替え後のプロパティ画面(イベント)

上図のように、イベントを設定します。
※関数名は任意で構いません
※関数の内容は後に記述します

イベント関数組み込み

フォーム起動時にリストボックスにノードを入れる処理
/***********************************************************************
    【Ev】フォーム起動時イベント
***********************************************************************/
private void Form1_Load(object sender, EventArgs e)
{
    //リストにノード追加
    listBox1.Items.Clear();
    string[] setNd = new string[] 
        {"ノードA","ノードB","ノードC","ノードD","ノードE",
            "ノードF","ノードG","ノードH","ノードI","ノードJ" };
    listBox1.Items.AddRange(setNd);
}
リストボックス描画イベント内容
/***********************************************************************
    【Ev】リストボックスノード描写処理
***********************************************************************/
private void ListBox_Cmn_DrawItem(object sender, DrawItemEventArgs e)
{
    // 背景描画
    e.DrawBackground();
    // 描画許可判定
    if (e.Index < 0)
    {
        return;
    }
    // 描画用変数設定
    Brush NdClrWd = new SolidBrush(e.ForeColor);
    string NdWord = ((ListBox)sender).Items[e.Index].ToString();
    // 奇数行の場合は背景色を変更し、縞々に見えるようにする
    Color backcolor;
    if (e.Index % 2 == 0)
    {
        backcolor = Color.FromArgb(220, 220, 220);
    }
    else
    {
        backcolor = Color.FromArgb(255, 255, 255);
    }
    // ノード作成
    e = new DrawItemEventArgs(e.Graphics, e.Font, e.Bounds, e.Index, e.State, e.ForeColor, backcolor);
    e.DrawBackground();
    e.Graphics.DrawString(NdWord, e.Font, NdClrWd, e.Bounds, StringFormat.GenericDefault);
    NdClrWd.Dispose();
    e.DrawFocusRectangle();
}

このようにイベント内容を組み込むと、
画像のように背景色が縞々になります。
// 奇数行の場合は背景色を変更し、縞々に見えるようにする
この部分を自由に変更することで、自分好みの形式を設定することが出来ます。
DrawItem」イベントはListBoxのノードが変更されるたびに呼ばれるイベントなので、
常に最新のノードに対する描画が行われます。
描画処理を忘れることが無いのでぜひ、ご活用ください。

最近同じ夢ばかり見る。。。

ブログに書くことでもないのですが、

とても不思議で誰かに言いたい!

だけど知り合いに報告すると正気を疑われそうなので書かせてください。

 怖い現象

ここ半年で覚えているだけでも10回以上、同じ内容の夢を見ます。

いつもは見た夢なんて忘れるし、

今までの人生で

また同じ夢だ・・・っ!」ってなったことがなかったので少し怖いです。

 

 夢の内容

・大学生として生活している

・周りの環境は学生の頃だけど、何故か今働いている会社員でもある

・時期は、大学卒業する1年前(就活が始まるぐらい)

・場面は、友達に授業の代返を頼みながら

 (あれ?この授業結構休んでるけど、単位に必要な出席数って大丈夫かな?)

   と自問自答しているところから始まってる

・その後、就活について思いを馳せて、

 (今働いてるけど俺の身分は会社員なの?学生なの?)

 (プログラマとして経験積んでるから頑張れば結構いいとこいけるかもなー)

  と、学生なのに意味のわからない自問自答をする

・最後に、今の会社の残件を思い出しながら

(あれ?俺って会社で働いてるよね?なんで大学生なの?)

  と現実を思い出しつつ起床する

 

以上が夢の内容です。

細かい内容は覚えていないのですが、

頭の中のやり取りは忘れずに記憶に残っていて上記の内容を毎回考えています。

 

もしこれを見た

精神科医等の専門的な方がいらっしゃいましたら、

私は正常なのかコメントで教えていただけるととても嬉しいです。

 

以上、どうか温かい目でよろしくお願いいたします。

【C#】修飾子ごとに出来る事 ※. private、public、static 編

今日、後輩に「staticって何ですか?」と聞かれて、激テンパったので調べてみました。
結果として、口で説明することは私には無理だ。とさじを投げたのですが、
各修飾子の挙動をまとめることで後輩に何とか説明できました。
ブログのネタになりそうでしたのでまとめます。

構文エラーで挙動を確認してみた

C#でクラス内に各修飾子の変数を定義し、クラスを跨いで 変数にアクセスした時に、構文エラーが出るものをまとめてみました

確認用コード

▼ 変数を定義したクラス

namespace WindowsFormsApp1
{
    class Cmn_Script
    {
        //テスト用クラス
        private string Hensu_Private;
        public string Hensu_Public;
        public static string Hensu_Static;

        //メソッドから変数をいじる用
        public void SetHensu_Public()
        {
            Hensu_Private = "";
            Hensu_Public = "";
            Hensu_Static = "";
        }
        public static void SetHensu_Static()
        {
            Hensu_Private = "";
            Hensu_Public = "";
            Hensu_Static = "";
        }
    }
}

▼ 変数をいじる関数を定義したクラス

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        // A)クラスを直接いじる
        private void Event_Test_A()
        {
            Cmn_Script.Hensu_Private = "";
            Cmn_Script.Hensu_Public = "";
            Cmn_Script.Hensu_Static = "";
        }
        // B)クラス型変数を作って中身をいじる   
        private void Event_Test_B()
        {
            Cmn_Script Cls_Set = new Cmn_Script();
            Cls_Set.Hensu_Private = "";
            Cls_Set.Hensu_Public = "";
            Cls_Set.Hensu_Static = "";
        }
        // C)クラス内メソッドを作って中身をいじる   
        private void Event_Test_C()
        {
            Cmn_Script.SetHensu_Public();
            Cmn_Script.SetHensu_Static();
            Cmn_Script Cls_Set = new Cmn_Script();
            Cls_Set.SetHensu_Public();
            Cls_Set.SetHensu_Static();
        }
    }
}

こんな感じでソースを組みました。
Windowsフォームアプリ内で作成したのでクラス名が変なのは気にしないでください。
同一namespace内の別クラスからアクセスしている。という事が重要です。

実際の構文エラー

エディターの画像をそのまま貼り付けます。

f:id:apuridasuo:20200717234954p:plain
図:エディター画像
画像のとなっている部分が構文エラーになっています。
構文エラーになるという事は、修飾子のルールにのっとっていない。という事です。
つまり、エラーの有無で修飾子の特性が分かってきます。

エラーから見える修飾子の特性

エラー情報から各修飾子の特性を見ていきます

①. Static(ソース上:SetHensu_Static)

Static変数は、「 A)クラスを直接いじる 」場合と、
同一クラスのメソッドからいじる 」場合にのみエラーになっていません。
つまり、Static変数の特性は
・同一クラスからのアクセスは可能
・クラスへ直接アクセスしてからのアクセス時は可能
という事です。
このおかげで、Static変数は大きな特性がありました。
クラス型の変数を作ろうが、クラスに直接アクセスしようが同じ値になる
という特性です。
他の修飾子の変数は、クラス型の変数(ソース上:Cls_Set)を作ると、
その変数ごとに値が変わっていくと思います。
Static変数はそれを許しません。
どんな状況でもアクセスできるけど、どんな状況でも同じ値になる
というのが特性の様です。

②. Private(ソース上:Hensu_Private)

Privateの変数を追ってみると、とても分かりやすい挙動になっています。
同一クラス内のメソッド以外からのアクセスは全てエラーになっています。
また、同一クラス内のメソッドでも
staticのメソッドのアクセスもエラーになっている点に注目です!
つまり、
同一クラス内からのみアクセス可能。かつ、
クラス型の変数を作れば、変数ごとに値も変わっていく
という特性になります。

③. Public(ソース上:Hensu_Public)

PublicはStaticとPrivateの自由な部分を併せ持った感じですね。
Staticのメソッド 」、「 A)クラスを直接いじる
つまり、
クラス型変数を作って、その中で変数をいじることで
クラス変数ごとに別々の値を持ち、自由にアクセスできる。

しかし、
自由に値を変えれるため、クラスを直接アクセスする方法は禁止されている。

以上です。
全然違う説明を行っていたら大変申し訳ございません。
素人の独学だと細かい知識がうっすらし過ぎていて恥ずかしい限りです。
違っている部分があれば、ご指摘いただけると
本当に勉強になりますので嬉しいです。

【C#】フォルダの高速コピー

今回は、フォルダの高速コピーをご紹介します。
フォルダのコピーとは、
サブフォルダも含めたすべてのファイルをコピーする
やり方になります。
再帰処理と並列処理を同関数で行っているので、
勉強中の方は処理の動きをご参考いただければ嬉しいです!

参考サイト

基本的な構文はこのサイトを参考にしました。
並列処理を追加したら高速になるなーの考えでちょっとだけ改造してます。
dobon.net

ソースコード

        /// <summary>
        /// サブフォルダを含むフォルダのコピー
        /// </summary>
        /// <param name="CopyFldName">コピーするフォルダパス</param>
        /// <param name="PasteFldName">コピー先のフォルダパス</param>
        public static void Set_CopyDirectory(string CopyFldName, string PasteFldName)
        {
            //コピー先のディレクトリがないときは作成
            if (!Directory.Exists(PasteFldName))
            {
                Directory.CreateDirectory(PasteFldName);              
                File.SetAttributes(PasteFldName, File.GetAttributes(CopyFldName));
            }
            //コピー先のディレクトリ名の末尾に"\"をつける
            if (PasteFldName[PasteFldName.Length - 1] != Path.DirectorySeparatorChar)
            {
                PasteFldName = PasteFldName + Path.DirectorySeparatorChar;
            }
            //コピー元のディレクトリにあるファイルをコピー ※ここで並列処理
            string[] files = Directory.GetFiles(CopyFldName);
            Parallel.ForEach(files, (file) =>
            {
                File.Copy(file, PasteFldName + Path.GetFileName(file), true);
            });
            //コピー元のディレクトリにあるディレクトリについて、再帰的に呼び出す
            string[] dirs = Directory.GetDirectories(CopyFldName);
            foreach (string dir in dirs)
            {
                Set_CopyDirectory(dir, PasteFldName + Path.GetFileName(dir));
            }
        }

以上がコピー処理を行う関数になります。
サブフォルダを考慮して再帰処理を行っている点と、
ファイルのコピーに並列処理を行っている点です。

以上、フォルダの高速コピーでした。 

【C#】自作アプリの自動アップデート機能

社内用にアプリを作成して配布するとき、
配布後にアップデートするのがとても面倒だったりしませんか?
作って終わりじゃないのが業務改善のメンドクサイ所ですよね。。。
今回は、自作アプリの自動アップデート機能の組み方をご紹介します。

前提条件

配布された人たちが全員アクセスできる共有フォルダがあることが条件
※このフォルダを配布先にしておくと便利

組み込み方法

アプリ内定数

まずは、作成するアプリ内に以下の定数を定義します

// バージョン情報
public const string Df_BasePath = @"アプリを置くフォルダのパス";
public const string Df_Version= "01_000";

Df_BasePath
最新バージョンのアプリを配布するフォルダのパスを記入します。
Df_Version
今、作成中のバージョンを記入します。
※このバージョンが「 Df_BasePath 」のパス内にあるファイルと一致していなかったら
 バージョンアップ処理を行う仕様になります。

アプリ起動時の処理

アプリ起動時に以下の処理を行います
①.「 Df_BasePath 」フォルダ存在判定
②.自分のバージョンと配布先のバージョンの一致判定
③.バージョンアップ処理

/*************************************************************************
    【例】フォーム起動時イベント
**************************************************************************/
private void Form1_Load(object sender, EventArgs e)
{
    // ①.フォルダ存在判定  
    if (IsFolderSonzai_Out(Df_BasePath))
    {
        // ②.自分のバージョンと配布先のバージョンの一致判定  
        string basePath = GetAllFileInFolder(Df_BasePath, "exe")[0];
        if (GetFileNameInPath(basePath, true).Split('v')[1] != Df_Version)
        {
            // ③.バージョンアップ処理
            Set_AutoUpdate(Df_BasePath);
            this.Close();
        }
    }
}

以上がイベントの内容です。細かく見ていきましょう。

①.フォルダ存在判定
/// <summary>
/// フォルダ存在判定【タイムアウト付き】
/// </summary>
/// <param name="path">検索されるフォルダパス</param>
/// <returns>検索結果( True = 存在する )</returns>
public static bool IsFolderSonzai_Out(string path)
{
    int i_Wait = 1000;   // この値【ms】だけ処理待ち
    bool exists = true;
    Thread t = new Thread
    (
        new ThreadStart(delegate ()
        {
            exists = Directory.Exists(path);
        })
    );
    t.Start();
    bool completed = t.Join(i_Wait);
    if (!completed)
    {
        exists = false;
        t.Abort();
    }
    return exists;
}

こちらが関数の中身です。
Df_BasePath 」フォルダが存在しない場合はバージョンアップを行いません。
配布先は共有フォルダとなるので、タイムアウト対策は必須です。

②.自分のバージョンと配布先のバージョンの一致判定

バージョンの比較方法ですが、配布先のファイル名にはバージョンを追記しておきます。
【例】ファイル名_v01_001.exe
現バージョンは定数で取得し、最新バージョンはファイル名から取得して比較します。

③.バージョンアップ処理
/// <summary>
/// 自動アップデート処理 ※これ実行した後、自分は閉じないとダメだよ
/// </summary>
/// <param name="PathBase">配布してるフォルダのパス</param>
public static void Set_AutoUpdate(string PathBase)
{
    //ベースパス取得して現階層にコピペ
    string basePath = GetAllFileInFolder(PathBase, "exe")[0];
    string pathAft;
    pathAft = GetFileNameInPath(basePath, false);
    pathAft = GetNowFolderPath() + "\\" + pathAft;
    File.Copy(basePath, pathAft, true);
    // Batファイル出力
    string text = "";
    text += @"@echo バージョンアップ中..." + "\r\n";
    text += @"@echo off" + "\r\n";
    text += @"timeout /t 5 /nobreak > nul" + "\r\n";
    text += @"del " + System.Reflection.Assembly.GetExecutingAssembly().Location + " > nul" + "\r\n";
    text += @"start " + pathAft + "\r\n";
    text += @"del Patch.bat > nul" + "\r\n";
    text += @"exit" + "\r\n";
    StreamWriter sw = new StreamWriter("Patch.bat", false, Encoding.GetEncoding("Shift_JIS"));
    sw.Write(text);
    sw.Close();
    // exe更新
    System.Diagnostics.Process p = new System.Diagnostics.Process();
    p.StartInfo.FileName = "Patch.bat";
    p.StartInfo.CreateNoWindow = true;
    p.Start();
    p.Close();
    p = null;
}
/// <summary>
/// ファイルのパスからファイル名を取得
/// </summary>
/// <param name="str_Path">パス</param>
/// <param name="IsKillKaku">拡張子を除くか?</param>
/// <returns></returns>
public static string GetFileNameInPath(string str_Path, bool IsKillKaku)
{
    // 「\」で分解してファイル名を取得
    string str_Ret = "";
    string[] str_Sepalate;
    str_Sepalate = str_Path.Split('\\');
    str_Ret = str_Sepalate[str_Sepalate.Length - 1];
    // 拡張子が不要なら除く
    if (IsKillKaku)
    {
        str_Sepalate = str_Ret.Split('.');
        str_Ret = str_Sepalate[0];
    }
    return str_Ret;
}
/// <summary>
/// 実行ファイルのパスを文字列で取得
/// </summary>
/// <returns>実行ファイルのパス ※ファイル名は含まない</returns>
public static string GetNowFolderPath()
{
    string str_Ret = "";
    str_Ret = System.Reflection.Assembly.GetExecutingAssembly().Location;
    str_Ret = GetFilePathFolder(str_Ret);
    return str_Ret;
}

少し長くなりましたがやっていることは簡単です。
・最新バージョンのファイルを起動ファイルと同じ階層にコピペ
・Batファイルを作成→起動
 ▼Batの内容
 ・5秒後に古いバージョンのファイル(起動ファイル)を削除
 ・自分(Batファイル)を削除
・最後に、「 this.Close(); 」で起動アプリを閉じる

以上です。
アプリのアップデート対策は必須なので、
自動アップデートで無駄な管理ヘイトをためないようにしましょう!

【VBA】ADOを利用したExcelアクセス時のエラー対応

Excelの解析にADOを利用したマクロを組んで配布したのですが、
私のPCではちゃんと動くのに、一部の方のPCでは動かない。。。
エラーの内容も三者三葉という気が狂うことがありました。
解決方法が結構斬新な物でしたのでご紹介します。

エラーが起きた環境

PC環境

・Windows10 64bit
Excel 2013 32bit

エラー発生ソース

Function GetFileRead_ADO(s_FilePath As String) As Variant
    ' 変数定義
    Dim strSQL As String
    Dim var_Ret() As Variant
    Dim dbCon As ADODB.Connection
    Dim dbRes As ADODB.Recordset
    ' ADO形式でシート内容取得
    Set dbCon = New ADODB.Connection
    dbCon.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
                   "Data Source=" & s_FilePath & ";" & _
                   "Extended Properties=""Excel 12.0;HDR=Yes;IMEX=1;Readonly=False"""
    strSQL = "SELECT * FROM [_ValueManage$]"

    ' 以下割愛・・・
End Function

上記ソースの「 dbCon.Open 」の部分でエラーになっていました。
エラーの内容はバラバラでしたが、エラー発生場所は共通でした。

エラー内容

・オートメーションエラー
・プロバイダーが見つかりません。正しくインストールされていない可能性があります。
・'Open'メソッドは失敗しました

エラー対応内容

CreateObjectを使用しない

参照設定を必要としなくなるのでとても便利なのですが、
ADO利用時にCreateObjectを使用するとエラーが一生直りませんでした。。。
こればっかりは原因が分かりませんでした。
以下は、「エラー発生ソース」のように
CreateObjectを使用しないソースを前提としています。

参照追加しているか確認

とりあえず参照設定を確認しましょう。
下図のチェック項目にチェックが入っているか確認!

f:id:apuridasuo:20200626144339p:plain
図:参照追加する項目

Accsessのインストール

これが驚きだったのですが、エラーの大きな原因が
Accsessがインストールされていない」でした!!
知らないよ!エラーで言え!!!と怒ったのはいい思い出です。
たまたま社内でOfficeのライセンス話があったので各人のインストール状況が
確認出来て「え!?」となって気づけたのですが、神ひらめきだったと自負してます。
という事で、社内に配布した簡単Access導入方法を紹介します。

f:id:apuridasuo:20200626145048p:plain
図:Access導入手順

Officeの修復

Accessの導入でほぼエラー問題は解決しました。
しかし、急にエラーが再発する方がちらほら。
原因は不明なのですが、解決方法は見つかりました。

www.saka-en.com

こちらのサイト様で紹介されている 「 Officeの修復をおこなう
の項目を実行すると、エラーは直りました。
エラーになった方々は直近にVisioをアンインストールした方々ばっかりでした。
おそらくですが、Officeのアプリは色々つながっていて、
一つのアプリをアンインストールすると別のアプリにも影響するのだと思います。

以上、
ADOを利用したExcelアクセス時のトラブルシューティングでした。

P.S.
オートメーションエラー考えたやつ滅びろ!
諦めずに原因教えなさいよ!!!

【Unity】AddListnerに引数が必要な場合の対処法

Unityでコンポーネントにイベントを設定するとき、AddListner()をよく使用します。
このAddListnerですが、
イベントの種類によっては引数が必要になる場面があるのをご存知でしょうか?
今回は、ラムダ式でイベント設定する時の注意をまとめたいと思います。

ボタンにイベントを設定する

/***********************************************************************
【メイン】ボタンにイベントを設定する関数
***********************************************************************/
public void SetBtnPushEventListner()
{
    // 【引数なし】ボタンイベント設定
    Button btn_Set;
    btn_Set = this.transform.Find("設定するオブジェクト名").GetComponent<Button>();
    btn_Set.onClick.AddListener(() => { SetBtnPushEvent_NoHikisuu(); });
    // 【引数あり】ボタンイベント設定
    Button btn_Set02;
    btn_Set02 = this.transform.Find("設定するオブジェクト名").GetComponent<Button>();
    btn_Set02.onClick.AddListener(() => { SetBtnPushEvent_Hikisuu("引数"); });
}

/***********************************************************************
【Ev】ボタン押下イベント ※引数なし
***********************************************************************/
public void SetBtnPushEvent_NoHikisuu()
{
    Debug.Log("押されたよ, 引数なし");
}
/***********************************************************************
【Ev】ボタン押下イベント ※引数あり
***********************************************************************/
public void SetBtnPushEvent_Hikisuu(string SetHikisu)
{
    Debug.Log("押されたよ, 引数あり:"+SetHikisu);
}

上記の書き方で、ボタンコンポーネントにクリックイベントを設定できます。

btn_Set02.onClick.AddListener(() => { SetBtnPushEvent_Hikisuu("引数"); });

この部分がイベントを設定する部分なのですが、
設定するコンポーネントの変数.イベント名.AddListener(() => { 関数名; });
という書き方で、ボタンに限らず大体のコンポーネントのイベントを設定できます。

イベント自体に引数がある場合

onClickイベントはイベント自体に引数が無いので、
イベントの引数を意識せずにAddListenerで設定することが出来ます。
しかし、イベント自体に引数がある場合は関数設定時にイベント引数を設定する必要があります。
Sliderのイベントを例に設定方法を記入します。

/***********************************************************************
【メイン】スライダーにイベントを設定する関数
***********************************************************************/
public void SetSliderChangeEventListner()
{
    // 【引数なし】ボタンイベント設定
    Slider setSlider;
    setSlider= this.transform.Find("オブジェクト名").GetComponent<Slider>();
    setSlider.onValueChanged.AddListener((value) => { SetChangeSlider_Vol((int)value); });
}

/***********************************************************************
【Ev】スライダー更新
***********************************************************************/
public static void SetChangeSlider(int SetVal)
{
    Debug.Log("スライダーの値が変更されたよ:" + SetVal.tostring());
}

上記の書き方で、スライダーコンポーネントにクリックイベントを設定できます。
onClickイベントとの違いは、AddListener((value) => { SetChangeSlider_Vol((int)value); });
この「value」の部分ですね。 これは、「 onValueChanged 」イベント自体の引数です。
value=変更されたスライダーのvalueになります。
このように、イベント自体の引数が存在する場合は、
ラムダ式の左辺に任意の変数名(例の場合はvalue部分)を記述する必要があります。

イベント名から引数があるか判定する方法

①.実際に記入してエラーで判別

イベント自体に引数があるかないかの判定は簡単です。
onClickイベントの記法で、引数が無いものとして記述してみる。
この方法で記述して、
・ビルドエラーなし・・・イベント引数ない
・ビルドエラーある・・・イベント引数あり

これで一発です。
引数がある場合は、以下の方法で引数の内容を確認しましょう。

②.Googleで検索

後はインターネッツにおまかせ! 「 Unity イベント名.AddListener
で検索しましょう。
これで大体のイベントの引数は検索できます。
私がSliderの引数の内容で詰まった時は、「 Unity onValueChanged.AddListener 」で検索しました。

ラムダ式でイベント追加を行うと、 イベント引数を簡単に持ってこれるのでとても便利です。