前回まででTGEditorの機能のコア部分であるTGEライブラリーのコード解説を終了しましたので、いよいよ"wrap up"の段階に来ました。

 

出来上がったTGEライブラリーのソースコード(TGE.cs-注1)は「ダイナミックリンクライブラリー(DLL)」としてコンパイルします。(注2

注1【TurtleGraphics】ご参考-TGEのクラス定義部分【TurtleGraphics】ご参考-TGEのUI部分(1)【TurtleGraphics】ご参考-TGEのUI部分(2)【TurtleGraphics】ご参考-TGEのUI部分(3)を参照願います。

注2Nakov.TurtleGraphics.dllが必要です。ご参考までにBCCForm and BCCSkeltonのパッケージに入っている分のバッチファイルを紹介します。

@ECHO OFF
ECHO ----------------------------------
ECHO  TGEditor.bat
ECHO  Copyright (c) 2026 by Ysama
ECHO ----------------------------------
REM TGE.dllの作成
(解説:↓は「TGE.dllがなかったら、ターゲットをDLLとしてコンパイルする」、という意味です。)
if not exist "TGE.dll" "C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe" /t:library  /reference:".\Nakov.TurtleGraphics.dll" /warn:4 ".\TGE.cs"
REM TGEditorの作成
"C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe" /t:winexe /win32icon:".\Turtle.ico" /res:".\TGEditor.resources" /reference:".\TGE.dll" /warn:4 ".\TGEditor.cs"
pause

 

TGE.dllができたならば、TGEditor.csに移ります。

 

先ずはツールバービットマップを用意して

 

これをC#のリソースファイル(TGEditor.resources)にします。これは専用のツールがないとできない()ので、BCCForm and BCCSkeltonのパッケージにはTGEditor.resourceというファイルを入れてあります。

:私は昔自作したResourceWriterを使っています。

 

プログラムアイコン(ウィンドウアイコンでもある)【TurtleGraphics】宿題の答えとC#5版コンパイルの準備の時の亀さんを使います。(まぁ、無くてもコンパイルはできますが...)

 

と、いうことで、

 

いよいよTGEditor.csに話を移しますが、前にも書いた通り、このプログラムコードはここで取り上げたRichEditorからの流用ですのであまり目新しいものはありません。又量も622行だけなので、次回に(最後の)コード解説にするつもりです。

 

長々とC#のコードを並べた

 

ので一息つくために久々の食べ物の話を。

 

昨日のお昼は、最近凝っていて前に作って余りを冷凍していた

 

キノコ炊き込みご飯

 

を、これも(お酒のお供に)冷凍で取り寄せていた「ホッケの昆布醤油漬け」と(揚皮と中の豆腐の食感差が好みの)生揚げを

 

久々にご飯のおかず(笑)

 

にしてしっかりと焼き、野沢菜と一緒においしくいただきました。又、炊き込みご飯が少ないのと野菜が足りないので、

 

野菜たっぷりの中華風春雨スープ

 

も戴きました。

 

 

ps. 炊き込みご飯は具材(特にキノコ)の食感が落ちるので冷凍には向いていないことを肝に銘じました。なお、醤油の代わりに生揚げにご飯に掛けるワカメやジャコのふりかけをかけて食べるのがマイブームです。

 

で、何が面白いのかって?

 

矢張り色々と工夫して作ったり、組み合わせて「試してみる」ってことかな?

 

///////////////////////////////////////////////////////////////////////////////
//                      TGEライブラリー (TGE.dll)
//                     Copyright (c) 2026 by Ysama
//
//                               【概要】
//LOGOベースの描画命令を纏めた「命令セット(TGSet)」、その一覧である「命令リス
//ト(TGList)」、「命令リスト」から指定した命令セットで、繰り返しを含め実行順を
//定める「実行リスト(TGList)」及び編集ダイアログをまとめたNakov.TurtleGraphics
//の命令編集ライブラリー
///////////////////////////////////////////////////////////////////////////////

続きの続きの続き

 

    /////////////////////////
    // Command入力ダイアログ
    /////////////////////////

    class InputDlg : Form
    {
        //クラスフィールド
        string[] CmdNameList = new string[15] {"Nop", "Forward()", "Backward()",
                                                "MoveTo()", "Rotate()", "RotateTo()",
                                                "PenUp()", "PenDown()", "X",
                                                "Y", "Angle", "PenColor",
                                                "PenSize", "PenVisible", "Delay"};
        string[] CmdExplList = new string[15] {"何もしない", "前進する", "後退する",
                                                "絶対座標へ移動する", "現在の方向から指定角度回転", "絶対角度へ回転(↑0→90↓180←270)",
                                                "描画停止", "描画開始", "X座標指定(移動)",
                                                "X座標指定(移動)", "角度指定(↑0→90↓180←270)",
                                                "描画色指定(Color)", "描画幅指定", "可視可(1)、不可(0)", "中断時間(ms)"};
        //クラスプロパティ
        public int[] dtData {get; set;}
        public string[] dtCmd;
        //クラスコントロール
        Button BtnOK;
        Button BtnCancel;
        GroupBox gBox;
        ComboBox cbCommands;
        TextBox txbExplanation;
        Label[] labelCmd;
        TextBox[] txbStr;

        //コンストラクター(1)-解説:引数無し(新規入力)
        public InputDlg()
        {
            dtData = new int[] {-1, 0, 0, 0};        //ComboBoxが選択されていない状態のSelectedIndex値
            dtCmd = new string[] {"", ""};            //命令文と説明文
            InitializeControls();
        }

        //コンストラクター(2)-解説:引数あり(編集)
        public InputDlg(int[] arg_Data)
        {
            //引数の記録
            if(arg_Data[0] >= 0 && arg_Data[0] < CmdNameList.Length)
                dtData = (int[])arg_Data.Clone();    //arg_Data配列のクローンで初期化
            else
                dtData = new int[] {-1, 0, 0, 0};    //ComboBoxが選択されていない状態のSelectedIndex値
            dtCmd = new string[] {"", ""};            //命令文と説明文
            InitializeControls();
        }

        //コントロールの初期化
        private void InitializeControls()
        {
            //ダイアログの属性設定
            if(dtData[0] == -1)
                this.Text = "命令入力ダイアログ";
            else
                this.Text = "命令編集ダイアログ";
            this.ClientSize = new Size(320, 210);
            this.MaximizeBox = false;        //最大化ボタン
            this.MinimizeBox = false;        //最小化ボタン
            this.ShowInTaskbar = false;        //タスクバー上非表示
            this.FormBorderStyle = FormBorderStyle.FixedDialog;        //境界のスタイル
            this.StartPosition = FormStartPosition.CenterScreen;    //画面中央に配置

            //コントロールの属性設定
            BtnOK = new Button();
            BtnOK.Location = new Point(ClientSize.Width - BtnOK.Width - 20, ClientSize.Height - BtnOK.Height - 10);
            BtnOK.Text = "OK";
            BtnOK.AutoSize = true;
            BtnOK.Click += new EventHandler(OnOK_Click);

            BtnCancel = new Button();
            BtnCancel.Location = new Point(20, ClientSize.Height - BtnCancel.Height - 10);
            BtnCancel.Text = "Cancel";
            BtnCancel.AutoSize = true;
            BtnCancel.Click += new EventHandler(OnCancel_Click);

            gBox = new GroupBox();
            gBox.Location = new Point(10, 0);
            gBox.Size = new Size(ClientSize.Width - 20, ClientSize.Height - BtnOK.Height - 20);
            gBox.Text = "命令と引数1~3の設定";

            cbCommands =new ComboBox();
            cbCommands.Location = new Point(10, 16);
            cbCommands.Size = new Size(ClientSize.Width / 2, 28);
            cbCommands.DropDownWidth = ClientSize.Width / 2;
            cbCommands.DropDownHeight = 160;
            cbCommands.DropDownStyle = ComboBoxStyle.DropDownList;
            cbCommands.FormattingEnabled = true;
            cbCommands.TextChanged += Command_TextChanged;

            txbExplanation = new TextBox();
            txbExplanation.Location = new Point(10, 40);
            txbExplanation.Size = new Size(ClientSize.Width / 2, 120);
            txbExplanation.Multiline = true;
            txbExplanation.WordWrap = true;
            txbExplanation.BorderStyle = BorderStyle.Fixed3D;
            txbExplanation.ReadOnly = true;

            labelCmd = new Label[3];
            labelCmd[0] = new Label();
            labelCmd[0].Text = "引数1(定数)";
            labelCmd[0].Size = new Size(80, 18);
            labelCmd[0].Location = new Point(180, 20);
            labelCmd[1] = new Label();
            labelCmd[1].Text = "引数2(倍数)";
            labelCmd[1].Location = new Point(180, 70);
            labelCmd[1].Size = new Size(80, 18);
            labelCmd[2] = new Label();
            labelCmd[2].Text = "引数3(分子)";
            labelCmd[2].Location = new Point(180, 120);
            labelCmd[2].Size = new Size(80, 18);

            txbStr = new TextBox[3];
            txbStr[0] = new TextBox();
            txbStr[0].Location = new Point(180, 38);
            txbStr[0].Size = new Size(100, 28);
            txbStr[0].BorderStyle = BorderStyle.Fixed3D;
            txbStr[0].Text = "0";
            txbStr[0].TextAlign = HorizontalAlignment.Right;
            txbStr[0].Enter += txbStr0_Enter;
            txbStr[1] = new TextBox();
            txbStr[1].Size = new Size(100, 28);
            txbStr[1].Location = new Point(180, 88);
            txbStr[1].BorderStyle = BorderStyle.Fixed3D;
            txbStr[1].Text = "0";
            txbStr[1].TextAlign = HorizontalAlignment.Right;
            txbStr[1].Enter += txbStr1_Enter;
            txbStr[2] = new TextBox();
            txbStr[2].Size = new Size(100, 28);
            txbStr[2].Location = new Point(180, 138);
            txbStr[2].BorderStyle = BorderStyle.Fixed3D;
            txbStr[2].Text = "0";
            txbStr[2].TextAlign = HorizontalAlignment.Right;
            txbStr[2].Enter += txbStr2_Enter;

            gBox.Controls.Add(cbCommands);
            gBox.Controls.Add(txbExplanation);
            gBox.Controls.Add(labelCmd[0]);
            gBox.Controls.Add(labelCmd[1]);
            gBox.Controls.Add(labelCmd[2]);
            gBox.Controls.Add(txbStr[0]);
            gBox.Controls.Add(txbStr[1]);
            gBox.Controls.Add(txbStr[2]);
            this.Controls.Add(BtnOK);
            this.Controls.Add(BtnCancel);
            this.Controls.Add(gBox);

            //cbCommands、txbStrの初期設定
            cbCommands.Items.AddRange(CmdNameList);
            if(dtData[0] >= 0 && dtData[0] < CmdNameList.Length)
            {
                cbCommands.SelectedIndex = dtData[0];
            }
            if(dtData[1] != 0)
            {
                txbStr[0].Text = dtData[1].ToString();
            }
            if(dtData[2] != 0)
            {
                txbStr[1].Text = dtData[2].ToString();
            }
            if(dtData[3] != 0)
            {
                txbStr[2].Text = dtData[3].ToString();
            }
        }

        private void Command_TextChanged(object sender, EventArgs e)
        {
            dtData[0] = cbCommands.SelectedIndex;
            txbExplanation.Text = CmdExplList[dtData[0]];
            if(dtData[0] == 11)    //解説:元々はPenColor以外はtrueにするelse節がありました。
            {
                txbStr[1].Enabled = false;
                txbStr[2].Enabled = false;
            }
        }

        private void txbStr0_Enter(object sender, EventArgs e)
        {
            string msg = "命令に与えられる「引数1(定数) + 引数2 * 繰り返し数(倍数) + 引数3 / (最大繰り返し数 - 繰り返し数)(分子)」の定数部分です。";
            if(dtData[0] == 3)
                msg = "MoveTo(x, y)の場合引数1がx、引数2がyで、引数3の繰り返し回数の倍数が加算されます。(x = 引数1 + 引数3 x 繰り返し数、y = 引数2 + 引数3 x 繰り返し数)";
            else if(dtData[0] == 11)
            {
                msg = "PenColor()の場合、引数1にColor.FromArgb(RGB値)のRGB値が入ります。";
                ColorDialog colDlg = new ColorDialog();
                colDlg.AllowFullOpen = true;
                if(colDlg.ShowDialog() == DialogResult.OK)
                {
                    txbStr[0].Text = colDlg.Color.ToArgb().ToString();
                    colDlg.Dispose();
                }    //解説:繰り返し回数による変数変化を与えるために、TextBoxを有効化し、入力可としました。
                txbStr[1].Enabled = true;
                txbStr[2].Enabled = true;
            }
            txbExplanation.Text = msg;
        }

        private void txbStr1_Enter(object sender, EventArgs e)
        {
            string msg = "命令に与えられる「引数1(定数) + 引数2 * 繰り返し数(倍数) + 引数3 / (最大繰り返し数 - 繰り返し数)(分子)」の倍数部分です。";
            if(dtData[0] == 3)
                msg = "MoveTo(x, y)の場合引数1がx、引数2がyで、引数3の繰り返し回数の倍数が加算されます。(x = 引数1 + 引数3 x 繰り返し数、y = 引数2 + 引数3 x 繰り返し数)";
            txbExplanation.Text = msg;
        }

        private void txbStr2_Enter(object sender, EventArgs e)
        {
            string msg = "命令に与えられる「引数1(定数) + 引数2 * 繰り返し数(倍数) + 引数3 / (最大繰り返し数 - 繰り返し数)(分子)」の分子部分です。";
            if(dtData[0] == 3)
                msg = "MoveTo(x, y)の場合引数1がx、引数2がyで、引数3の繰り返し回数の倍数が加算されます。(x = 引数1 + 引数3 x 繰り返し数、y = 引数2 + 引数3 x 繰り返し数)";
            txbExplanation.Text = msg;
        }

        //OKボタンがクリックされた時
        protected void OnOK_Click(object sender, EventArgs e)
        {
            if(dtData[0] < 0)
                MessageBox.Show("命令が選択されていません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
            else
            {
                dtCmd[0] = CmdNameList[dtData[0]];        //命令文
                dtCmd[1] = CmdExplList[dtData[0]];        //説明文
                int dat = 0;
                if(!int.TryParse(txbStr[0].Text, out dat))
                    MessageBox.Show("引数1が整数ではありません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                else
                {
                    dtData[1] = dat;
                    if(!int.TryParse(txbStr[1].Text, out dat))
                        MessageBox.Show("引数2が整数ではありません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    else
                    {
                        dtData[2] = dat;
                        if(!int.TryParse(txbStr[2].Text, out dat))
                            MessageBox.Show("引数3が整数ではありません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        else
                        {
                            dtData[3] = dat;
                            Close();
                        }
                    }
                }
            }
        }

        //Cancelボタンがクリックされた時
        protected void OnCancel_Click(object sender, EventArgs e)
        {
            Close();
        }
    }
}

 

///////////////////////////////////////////////////////////////////////////////
//                      TGEライブラリー (TGE.dll)
//                     Copyright (c) 2026 by Ysama
//
//                               【概要】
//LOGOベースの描画命令を纏めた「命令セット(TGSet)」、その一覧である「命令リス
//ト(TGList)」、「命令リスト」から指定した命令セットで、繰り返しを含め実行順を
//定める「実行リスト(TGList)」及び編集ダイアログをまとめたNakov.TurtleGraphics
//の命令編集ライブラリー
///////////////////////////////////////////////////////////////////////////////

続きの続き

 

    ///////////////////
    //CmdSetダイアログ
    ///////////////////

    public class CmdSetDlg : Form
    {
        //クラスメンバーフィールド
        public TGSet Tgs;            //TGSetクラスインスタンス
        Button Btn_Add;                //命令追加ボタン
        Button Btn_Del;                //命令削除ボタン
        Button Btn_Init;            //初期化ボタン
        Button Btn_OK;                //OKボタン
        Button Btn_Cancel;            //Cancelボタン
        ListView listView;            //リストビュー
        Label lblTimes;                //繰り返し回数ラベル
        TextBox txbTimes;            //繰り返し回数入力用
        Button Btn_Name;            //名前・説明ボタン
        Label lblName;                //命令セット名ラベル
        TextBox txbName;            //命令セット名入力用
        Label lblDescription;        //説明ラベル
        TextBox txbDescription;        //説明入力用

        //コンストラクター(1)
        public CmdSetDlg()
        {
            this.ShowIcon = false;
            this.Text = "命令セット入力ダイアログ";
            this.ClientSize = new Size(594, 320);
            this.MinimumSize = new Size(430, 340);
            this.BackColor = SystemColors.Window;
            this.Load += new EventHandler(CmdSetDlg_OnLoad);
            this.Tgs = new TGSet();                    //命令セット
            this.Tgs.AddCmd(new int[] {-1,1,0,0});    //命令セット開始
        }

        //コンストラクター(2)
        public CmdSetDlg(TGSet tgs)
        {
            this.ShowIcon = false;
            this.Text = "命令セット編集ダイアログ";
            this.ClientSize = new Size(594, 320);
            this.MinimumSize = new Size(430, 340);
            this.BackColor = SystemColors.Window;
            this.Load += new EventHandler(CmdSetDlg_OnLoad);
            this.Tgs = tgs;                            //命令セット参照渡し
        }

        //OnLoad時処理
        protected void CmdSetDlg_OnLoad(object sender, EventArgs e)
        {
            Btn_Add = new Button();
            Btn_Add.Text = "命令追加";
            Btn_Add.TextAlign = ContentAlignment.MiddleCenter;
            Btn_Add.Font = new Font("MS 明朝", 8);
            Btn_Add.ForeColor = Color.Black;
            Btn_Add.Size = new Size(80, 28);
            Btn_Add.BackColor = SystemColors.Control;
            Btn_Add.Location = new Point(ClientSize.Width - Btn_Add.Width - 10, 10);
            Btn_Add.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            Btn_Add.Click += new EventHandler(OnClick_Add);    

            Btn_Del = new Button();
            Btn_Del.Text = "命令削除";
            Btn_Del.TextAlign = ContentAlignment.MiddleCenter;
            Btn_Del.Font = new Font("MS 明朝", 8);
            Btn_Del.ForeColor = Color.Black;
            Btn_Del.Size = new Size(80, 28);
            Btn_Del.BackColor = SystemColors.Control;
            Btn_Del.Location = new Point(ClientSize.Width - Btn_Del.Width - 10, 20 + Btn_Add.Height);
            Btn_Del.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            Btn_Del.Click += new EventHandler(OnClick_Del);

            Btn_Init = new Button();
            Btn_Init.Text = "命令初期化";
            Btn_Init.TextAlign = ContentAlignment.MiddleCenter;
            Btn_Init.Font = new Font("MS 明朝", 8);
            Btn_Init.ForeColor = Color.Black;
            Btn_Init.Size = new Size(80, 28);
            Btn_Init.BackColor = SystemColors.Control;
            Btn_Init.Location = new Point(ClientSize.Width - Btn_Init.Width - 10, 30 + Btn_Add.Height + Btn_Del.Height);
            Btn_Init.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            Btn_Init.Click += new EventHandler(OnClick_Init);

            Btn_OK = new Button();
            Btn_OK.Text = "OK";
            Btn_OK.TextAlign = ContentAlignment.MiddleCenter;
            Btn_OK.Font = new Font("MS 明朝", 8);
            Btn_OK.ForeColor = Color.Black;
            Btn_OK.Size = new Size(80, 28);
            Btn_OK.BackColor = SystemColors.Control;
            Btn_OK.Location = new Point(ClientSize.Width - Btn_OK.Width - 10, ClientSize.Height - Btn_OK.Height - 10);
            Btn_OK.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right);
            Btn_OK.Click += new EventHandler(OnClick_OK);

            Btn_Cancel = new Button();
            Btn_Cancel.Text = "Cancel";
            Btn_Cancel.TextAlign = ContentAlignment.MiddleCenter;
            Btn_Cancel.Font = new Font("MS 明朝", 8);
            Btn_Cancel.ForeColor = Color.Black;
            Btn_Cancel.Size = new Size(80, 28);
            Btn_Cancel.BackColor = SystemColors.Control;
            Btn_Cancel.Location = new Point(ClientSize.Width - Btn_Cancel.Width - 10, ClientSize.Height - Btn_Cancel.Height - Btn_OK.Height - 20);
            Btn_Cancel.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right);
            Btn_Cancel.Click += new EventHandler(OnClick_Cancel);

            lblTimes = new Label();
            lblTimes.Size = new Size(80, 28);
            lblTimes.Location = new Point(ClientSize.Width - lblTimes.Width - 10, 40 + Btn_Add.Height + Btn_Del.Height + Btn_Init.Height);
            lblTimes.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            lblTimes.AutoSize = false;
            lblTimes.TextAlign = ContentAlignment.MiddleCenter;
            lblTimes.Font = new Font("MS 明朝", 8);
            lblTimes.Text = "繰り返し回数";

            txbTimes = new TextBox();
            txbTimes.Size = new Size(80, 28);
            txbTimes.Location = new Point(ClientSize.Width - lblTimes.Width - 10, 40 + Btn_Add.Height + Btn_Del.Height + Btn_Init.Height + lblTimes.Height);
            txbTimes.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            txbTimes.TextAlign = HorizontalAlignment.Right;
            txbTimes.Text ="1";

            Btn_Name = new Button();
            Btn_Name.Text = "名前・説明";
            Btn_Name.TextAlign = ContentAlignment.MiddleCenter;
            Btn_Name.Font = new Font("MS 明朝", 8);
            Btn_Name.ForeColor = Color.Black;
            Btn_Name.Size = new Size(80, 28);
            Btn_Name.BackColor = SystemColors.Control;
            Btn_Name.Location = new Point(ClientSize.Width - Btn_Init.Width - 10, 50 + Btn_Add.Height + Btn_Del.Height + Btn_Init.Height + lblTimes.Height + txbTimes.Height);
            Btn_Name.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            Btn_Name.Click += new EventHandler(OnClick_Name);

            lblName = new Label();
            lblName.Size = new Size(80, 18);
            lblName.Location = new Point(10, 10);
            lblName.Anchor = (AnchorStyles.Top | AnchorStyles.Left);
            lblName.AutoSize = false;
            lblName.Font = new Font("MS 明朝", 8);
            lblName.Text = "命令セット名";
            lblName.Visible = false;

            txbName = new TextBox();
            txbName.Size = new Size(ClientSize.Width - Btn_Add.Width - 30, 28);
            txbName.Location = new Point(10, lblName.Height + 10);
            txbName.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right);
            txbName.Visible = false;

            lblDescription = new Label();
            lblDescription.Size = new Size(80, 18);
            lblDescription.Location = new Point(10, lblName.Height + txbName.Height + 20);
            lblDescription.Anchor = (AnchorStyles.Top | AnchorStyles.Left);
            lblDescription.AutoSize = false;
            lblDescription.Font = new Font("MS 明朝", 8);
            lblDescription.Text = "説明";
            lblDescription.Visible = false;

            txbDescription = new TextBox();
            txbDescription.Size = new Size(ClientSize.Width - Btn_Add.Width - 30, ClientSize.Height - lblName.Height - txbName.Height - lblDescription.Height - 30);
            txbDescription.Location = new Point(10, lblName.Height + txbName.Height + lblDescription.Height + 20);
            txbDescription.Multiline = true;
            txbDescription.WordWrap = true;
            txbDescription.ScrollBars = ScrollBars.Vertical;
            txbDescription.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right);
            txbDescription.Visible = false;

            this.Controls.Add(Btn_Add);
            this.Controls.Add(Btn_Del);
            this.Controls.Add(Btn_Init);
            this.Controls.Add(lblTimes);
            this.Controls.Add(txbTimes);
            this.Controls.Add(Btn_Name);
            this.Controls.Add(Btn_Cancel);
            this.Controls.Add(Btn_OK);
            this.Controls.Add(lblName);
            this.Controls.Add(txbName);
            this.Controls.Add(lblDescription);
            this.Controls.Add(txbDescription);

            //リストビュー
            listView = new ListView();
            listView.BorderStyle = BorderStyle.Fixed3D;                //境界
            listView.Location = new Point(10, 10);                    //位置
            listView.Width = ClientSize.Width - Btn_Add.Width - 30;    //サイズ
            listView.Height = ClientSize.Height - 20;
            listView.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right);    //アンカー固定
            listView.FullRowSelect = true;                            //一行選択
            listView.MultiSelect = false;                            //複数選択不可
            listView.GridLines = true;                                //グリッドラインの表示
            listView.HoverSelection = false;                        //ポイントで選択できるようにする
            listView.Activation = ItemActivation.OneClick;            //シングルクリック選択;
            listView.CheckBoxes = false;                            //チェックボックス無効化
            listView.View = View.Details;                            //リストビューの表示方法
            listView.AllowDrop = false;                                //listViewへのドロップを受け入れない
            //イベントハンドラー
            listView.MouseDoubleClick+= new MouseEventHandler(ListView_DoubleClick);
            //ヘッダー定義
            ColumnHeader columnCmd = new ColumnHeader{Text = "命令", Width = 80};
            ColumnHeader columnExp = new ColumnHeader{Text = "説明", Width = 220};
            ColumnHeader columnArg1 = new ColumnHeader{Text = "引数1", Width = 60};
            columnArg1.TextAlign = HorizontalAlignment.Center;
            ColumnHeader columnArg2 = new ColumnHeader{Text = "引数2", Width = 60};
            columnArg2.TextAlign = HorizontalAlignment.Center;
            ColumnHeader columnArg3 = new ColumnHeader{Text = "引数3", Width = 60};
            columnArg3.TextAlign = HorizontalAlignment.Center;
            ColumnHeader columnCmdNo = new ColumnHeader{Text = "命令番号", Width = 60};
            columnCmdNo.TextAlign = HorizontalAlignment.Center;
            ColumnHeader[] colHeaderRegValue = {columnCmd, columnExp, columnArg1, columnArg2, columnArg3, columnCmdNo};    //ヘッダー設定
            listView.Columns.AddRange(colHeaderRegValue);    //ヘッダー追加
            //編集ダイアログの場合の初期設定
            if(Tgs.Count() > 1)
            {
                string[] CmdNameList = new string[15] {"Nop", "Forward()", "Backward()",
                                                "MoveTo()", "Rotate()", "RotateTo()",
                                                "PenUp()", "PenDown()", "X",
                                                "Y", "Angle", "PenColor",
                                                "PenSize", "PenVisible", "Delay"};
                string[] CmdExplList = new string[15] {"何もしない", "前進する", "後退する",
                                                "絶対座標へ移動する", "現在の方向から指定角度回転", "絶対角度へ回転(↑0→90↓180←270)",
                                                "描画停止", "描画開始", "X座標指定(移動)",
                                                "X座標指定(移動)", "角度指定(↑0→90↓180←270)",
                                                "描画色指定(Color)", "描画幅指定", "可視可(1)、不可(0)", "中断時間(ms)"};
                for(int i = 1; i < Tgs.Count(); i++)
                {
                    int[] cmd = Tgs.GetCmd(i);
                    string[] item = {CmdNameList[cmd[0]], CmdExplList[cmd[0]],
                                    cmd[1].ToString(), cmd[2].ToString(), 
                                    cmd[3].ToString(), cmd[0].ToString()};
                    listView.Items.Add(new ListViewItem(item));
                }
                txbName.Text = Tgs.Name;                //命令セット名
                txbDescription.Text = Tgs.Description;    //説明
                int[] hedder = Tgs.GetCmd(0);            //"-1"のある最初の命令行を取得
                txbTimes.Text = hedder[1].ToString();    //"-1"の次が繰り返し回数
            }
            //FormにListViewを追加
            this.Controls.Add(listView);
        }

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

        //Addボタンがクリックされた時
        protected void OnClick_Add(object sender, EventArgs e)
        {
            //入力ダイアログによる入力
            InputDlg inpDlg = new InputDlg();
            inpDlg.ShowDialog();
            if(inpDlg.dtData[0] == -1)
                MessageBox.Show("入力がキャンセルされました。", "注意", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            else
            {
                //原本のリストに追加
                Tgs.AddCmd(inpDlg.dtData);
                //その内容をListViewに表示
                string[] item = {inpDlg.dtCmd[0], inpDlg.dtCmd[1],
                                inpDlg.dtData[1].ToString(), inpDlg.dtData[2].ToString(), 
                                inpDlg.dtData[3].ToString(), inpDlg.dtData[0].ToString()};
                listView.Items.Add(new ListViewItem(item));
            }
            inpDlg.Dispose();
        }

        //Delボタンがクリックされた時
        protected void OnClick_Del(object sender, EventArgs e)
        {
            //選択された行を確認する
            if(listView.SelectedItems.Count == 0)
                MessageBox.Show("データが選択されていません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            else
            {
                int selected_index = listView.SelectedIndices[0];
                Tgs.RemoveAt(selected_index + 1);    //Tgs.CmdSetはlistViewよりも1行多い
                listView.Items.RemoveAt(selected_index);
            }
        }

        //Initボタンがクリックされた時
        protected void OnClick_Init(object sender, EventArgs e)
        {
            Tgs.Clear();
            listView.Items.Clear();
        }

        //OKボタンがクリックされた時
        protected void OnClick_OK(object sender, EventArgs e)
        {
            int times = 0;
            if(int.TryParse(txbTimes.Text, out times))
                Tgs.SetCmd(0, -1, times, 0, 0);        //TgsのCmdSet[0]に"-1,回数,0,0"を代入"
            else
            {    //繰り返し回数が異常値の場合は終了させない
                MessageBox.Show("繰り返し回数が整数ではありません。", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return;
            }
            //受け渡しデータに値を入れる
            Tgs.Name = txbName.Text;
            Tgs.Description = txbDescription.Text;
            //このフォームの戻り値
            DialogResult = DialogResult.OK;
            Close();
        }

        //Cancelボタンがクリックされた時
        protected void OnClick_Cancel(object sender, EventArgs e)
        {
            //受け渡しデータを初期化する(編集モードでTGSetの参照入力をしている場合、注意が必要!)
            Tgs.Clear();
            Tgs.Name = String.Empty;
            Tgs.Description = String.Empty;
            //このフォームの戻り値
            DialogResult = DialogResult.Cancel;
            Close();
        }

        //Namelボタンがクリックされた時(解説:これが「早変わり」の舞台裏)
        protected void OnClick_Name(object sender, EventArgs e)
        {
            if(Btn_Name.Text == "名前・説明")
            {
                Btn_Name.Text = "命令セット";
                listView.Visible = false;
                Btn_Add.Visible = false;
                Btn_Del.Visible = false;
                Btn_Init.Visible = false;
                lblTimes.Visible = false;
                txbTimes.Visible = false;
                lblName.Visible = true;
                txbName.Visible = true;
                lblDescription.Visible = true;
                txbDescription.Visible = true;
            }
            else
            {
                Btn_Name.Text = "名前・説明";
                listView.Visible = true;
                Btn_Add.Visible = true;
                Btn_Del.Visible = true;
                Btn_Init.Visible = true;
                lblTimes.Visible = true;
                txbTimes.Visible = true;
                lblName.Visible = false;
                txbName.Visible = false;
                lblDescription.Visible = false;
                txbDescription.Visible = false;
            }
        }

        //listViewがダブルクリックされた時
        protected void ListView_DoubleClick(object sender, MouseEventArgs e)
        {
            //選択された項目があるか確認
            if(listView.SelectedIndices.Count > 0)
            {
                //入力ダイアログによる入力
                int selected_index = listView.SelectedIndices[0];
                InputDlg inpDlg = new InputDlg(Tgs.GetCmd(selected_index + 1));
                inpDlg.ShowDialog(this);
                //入力ダイアログによる入力
                if(inpDlg.dtData[0] == -1)
                    MessageBox.Show("入力がキャンセルされました。", "注意", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                else
                {
                    Tgs.SetCmd(selected_index + 1, (int[])inpDlg.dtData.Clone());    //Tgs.CmdSetはlistViewよりも1行多い
                    listView.Items[selected_index].SubItems[0].Text = inpDlg.dtCmd[0];
                    listView.Items[selected_index].SubItems[1].Text = inpDlg.dtCmd[1];
                    listView.Items[selected_index].SubItems[2].Text = inpDlg.dtData[1].ToString();
                    listView.Items[selected_index].SubItems[3].Text = inpDlg.dtData[2].ToString();
                    listView.Items[selected_index].SubItems[4].Text = inpDlg.dtData[3].ToString();
                    listView.Items[selected_index].SubItems[5].Text = inpDlg.dtData[0].ToString();
                }
                inpDlg.Dispose();
            }
        }
    }
 

前回のご参考で予告いたしましたダイアログ部分のコードを「ご参考」として掲載します。今回字数制限に引っかかり、なんと三分割となりました。ご容赦を。

尚、TGEditorの本体部分についてその解説が終わってからとなります。

 

///////////////////////////////////////////////////////////////////////////////
//                      TGEライブラリー (TGE.dll)
//                     Copyright (c) 2026 by Ysama
//
//                               【概要】
//LOGOベースの描画命令を纏めた「命令セット(TGSet)」、その一覧である「命令リス
//ト(TGList)」、「命令リスト」から指定した命令セットで、繰り返しを含め実行順を
//定める「実行リスト(TGList)」及び編集ダイアログをまとめたNakov.TurtleGraphics
//の命令編集ライブラリー
///////////////////////////////////////////////////////////////////////////////

続き

 

    ////////////////////
    //CmdListダイアログ
    ////////////////////

    public class CmdListDlg : Form
    {
        public int Selection {get; private set;}    //戻り値
        //クラスメンバーフィールド
        int ListType;                                //リストタイプ(0-単純選択、1-CmdList、2-ExeList)
        TGEdit Tge;                                    //TGEditクラス
        TGList Tgl;                                    //TGListクラス
        Button Btn_Add;                                //命令追加ボタン
        Button Btn_Del;                                //命令削除ボタン
        Button Btn_Init;                            //初期化ボタン
        Button Btn_OK;                                //OKボタン
        Button Btn_Cancel;                            //Cancelボタン
        ListView listView;                            //リストビュー

        //コンストラクター
        public CmdListDlg(TGEdit tge, int type = 0)    //解説:引数のtgeはデータ参照、typeは用途分けの為
        {
            ListType = (type < 0 || type > 2) ? 1 : type;    //規定値はCmdList
            Tge = tge;                                        //参照渡し
            switch(ListType)
            {    //リストタイプによるタイトルとデータソースの決定(解説:このように使い分けている)
                case 0:        //値渡しによる選択
                    this.Text = "命令セット選択";
                    Tgl = new TGList(Tge.CmdList);
                    break;
                case 1:        //参照渡しによる直接編集
                    this.Text = "命令セットライブラリー";
                    Tgl = Tge.CmdList;
                    break;
                case 2:        //参照渡しによる直接編集
                    this.Text = "実行命令セット一覧";
                    Tgl = Tge.ExeList;
                    break;
            }
            this.ShowIcon = false;
            this.ClientSize = new Size(640, 320);
            this.MinimumSize = new Size(480, 240);
            this.BackColor = SystemColors.Window;
            this.Load += new EventHandler(CmdListDlg_OnLoad);
        }

        //OnLoad時処理
        protected void CmdListDlg_OnLoad(object sender, EventArgs e)
        {
            Btn_Add = new Button();
            Btn_Add.Text = "命令セット追加";
            Btn_Add.TextAlign = ContentAlignment.MiddleCenter;
            Btn_Add.Font = new Font("MS 明朝", 8);
            Btn_Add.ForeColor = Color.Black;
            Btn_Add.Size = new Size(100, 28);
            Btn_Add.BackColor = SystemColors.Control;
            Btn_Add.Location = new Point(ClientSize.Width - Btn_Add.Width - 10, 10);
            Btn_Add.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            Btn_Add.Click += new EventHandler(OnClick_Add);
            Btn_Add.Visible = (ListType == 0) ? false : true;    //解説:引数typeにより表示したり、しなかったり。(以下略)

            Btn_Del = new Button();
            Btn_Del.Text = "命令セット削除";
            Btn_Del.TextAlign = ContentAlignment.MiddleCenter;
            Btn_Del.Font = new Font("MS 明朝", 8);
            Btn_Del.ForeColor = Color.Black;
            Btn_Del.Size = new Size(100, 28);
            Btn_Del.BackColor = SystemColors.Control;
            Btn_Del.Location = new Point(ClientSize.Width - Btn_Del.Width - 10, 20 + Btn_Add.Height);
            Btn_Del.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            Btn_Del.Click += new EventHandler(OnClick_Del);
            Btn_Del.Visible = (ListType == 0) ? false : true;

            Btn_Init = new Button();
            Btn_Init.Text = "命令リスト初期化";
            Btn_Init.TextAlign = ContentAlignment.MiddleCenter;
            Btn_Init.Font = new Font("MS 明朝", 8);
            Btn_Init.ForeColor = Color.Black;
            Btn_Init.Size = new Size(100, 28);
            Btn_Init.BackColor = SystemColors.Control;
            Btn_Init.Location = new Point(ClientSize.Width - Btn_Init.Width - 10, 30 + Btn_Add.Height + Btn_Del.Height);
            Btn_Init.Anchor = (AnchorStyles.Top | AnchorStyles.Right);
            Btn_Init.Click += new EventHandler(OnClick_Init);
            Btn_Init.Visible = (ListType == 0) ? false : true;

            Btn_OK = new Button();
            Btn_OK.Text = "OK";
            Btn_OK.TextAlign = ContentAlignment.MiddleCenter;
            Btn_OK.Font = new Font("MS 明朝", 8);
            Btn_OK.ForeColor = Color.Black;
            Btn_OK.Size = new Size(100, 28);
            Btn_OK.BackColor = SystemColors.Control;
            Btn_OK.Location = new Point(ClientSize.Width - Btn_OK.Width - 10, ClientSize.Height - Btn_OK.Height - 10);
            Btn_OK.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right);
            Btn_OK.Click += new EventHandler(OnClick_OK);

            Btn_Cancel = new Button();
            Btn_Cancel.Text = "Cancel";
            Btn_Cancel.TextAlign = ContentAlignment.MiddleCenter;
            Btn_Cancel.Font = new Font("MS 明朝", 8);
            Btn_Cancel.ForeColor = Color.Black;
            Btn_Cancel.Size = new Size(100, 28);
            Btn_Cancel.BackColor = SystemColors.Control;
            Btn_Cancel.Location = new Point(ClientSize.Width - Btn_Cancel.Width - 10, ClientSize.Height - Btn_Cancel.Height - Btn_OK.Height - 20);
            Btn_Cancel.Anchor = (AnchorStyles.Bottom | AnchorStyles.Right);
            Btn_Cancel.Click += new EventHandler(OnClick_Cancel);

            this.Controls.Add(Btn_Add);
            this.Controls.Add(Btn_Del);
            this.Controls.Add(Btn_Init);
            this.Controls.Add(Btn_Cancel);
            this.Controls.Add(Btn_OK);

            //リストビュー
            listView = new ListView();
            listView.BorderStyle = BorderStyle.Fixed3D;                    //境界
            listView.Location = new Point(10, 10);                        //位置
            listView.Width = ClientSize.Width - Btn_Add.Width - 30;        //サイズ
            listView.Height = ClientSize.Height - 20;
            listView.Anchor = (AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Bottom | AnchorStyles.Right);    //アンカー固定
            listView.FullRowSelect = true;                                //一行選択
            listView.MultiSelect = true;                                //複数選択を許す
            listView.GridLines = true;                                    //グリッドラインの表示
            listView.HoverSelection = false;                            //ポイントで選択できるようにする
            listView.Activation = ItemActivation.OneClick;                //シングルクリック選択;
            listView.CheckBoxes = false;                                //チェックボックス無効化
            listView.View = View.Details;                                //リストビューの表示方法
            listView.AllowDrop = false;                                    //listViewへのドロップを受け入れない
            //イベントハンドラの追加
            listView.MouseDoubleClick+= new MouseEventHandler(ListView_DoubleClick);
            if(ListType == 1)    //CmdListの場合
            {
                listView.ColumnClick += new ColumnClickEventHandler(ListView_ColumnClick);
            }
            //ヘッダー定義
            ColumnHeader columnIndex = new ColumnHeader{Text = "Index", Width = 40};
            ColumnHeader columnCmdName = new ColumnHeader{Text = "命令セット名", Width = 100};
            ColumnHeader columnDescription = new ColumnHeader{Text = "説明", Width = 366};
            ColumnHeader[] colHeaderRegValue = {columnIndex, columnCmdName, columnDescription};    //ヘッダー設定
            listView.Columns.AddRange(colHeaderRegValue);    //ヘッダー追加
            //Tglに値がある場合リストビューに読み込む
            int n = Tgl.CountSets();
            if(n > 0)
            {
                for(int i = 0; i < n; i++)
                {
                    string[] item = {i.ToString(), Tgl.GetSet(i).Name, Tgl.GetSet(i).Description};
                    listView.Items.Add(new ListViewItem(item));
                }
            }
            //FormにListViewを追加
            this.Controls.Add(listView);
        }

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

        //Addボタンがクリックされた時
        protected void OnClick_Add(object sender, EventArgs e)
        {

             //解説:引数typeにより追加の「仕方」が変わる。
            if(ListType == 1)        //CmdListの場合
            {
                //CmdSetDlgによる命令セットの作成
                CmdSetDlg csd = new CmdSetDlg();    //引数無し(新規作成)
                if(csd.ShowDialog(this) == DialogResult.OK)
                {
                    listView.BeginUpdate();            //描画停止
                    Tgl.AddSet(csd.Tgs);
                    int last_one = Tgl.CountSets() - 1;    //Tgl最後のTGSet == 最新の命令セット
                    string[] item = {last_one.ToString(), Tgl.GetSet(last_one).Name, Tgl.GetSet(last_one).Description};
                    listView.Items.Add(new ListViewItem(item));
                    listView.EndUpdate();            //描画再開
                }
                csd.Dispose();
            }
            else if(ListType == 2)    //ExeListの場合
            {
                //CmdListDlgによる単純選択
                CmdListDlg cld = new CmdListDlg(Tge, 0);
                if(cld.ShowDialog(this) == DialogResult.OK)
                {
                    listView.BeginUpdate();            //描画停止
                    Tgl.AddSet(Tge.CmdList.GetSet(cld.Selection));    //TgeのCmdListからDeep copyを追加する
                    int last_one = Tgl.CountSets() - 1;    //Tgl最後のTGSet == 最新の命令セット
                    string[] item = {last_one.ToString(), Tgl.GetSet(last_one).Name, Tgl.GetSet(last_one).Description};
                    listView.Items.Add(new ListViewItem(item));
                    listView.EndUpdate();            //描画再開
                }
                cld.Dispose();
            }
        }

        //Delボタンがクリックされた時
        protected void OnClick_Del(object sender, EventArgs e)
        {
            //選択された行を確認する
            if(listView.SelectedItems.Count == 0)
                MessageBox.Show("データが選択されていません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            //選択があり、CmdList、ExeListの直接編集であるので
            else if(ListType > 0)
            {
                DialogResult dr = MessageBox.Show("命令リストのソースデータが失われます。\r\n本当によろしいですか?", "確認",
                                    MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if(dr == DialogResult.Yes)
                {
                    int selected_index = listView.SelectedIndices[0];
                    listView.Items.RemoveAt(selected_index);
                    Tgl.RemoveAt(selected_index);
                }
            }
        }

        //Initボタンがクリックされた時
        protected void OnClick_Init(object sender, EventArgs e)
        {

             //解説:引数typeにより処理を変える。
            if(ListType == 1)        //tge.CmdList(命令セットライブラリー)→システム使用命令リスト
            {
                MessageBox.Show("命令リストライブラリーの消去はできません。", "禁止事項", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
            else if(ListType == 2)    //tge.ExeList(実行命令セット)→個別ユーザーリスト
            {
                DialogResult dr = MessageBox.Show("命令リストのソースデータが失われます。\r\n本当によろしいですか?", "確認",
                                    MessageBoxButtons.YesNo, MessageBoxIcon.Question);
                if(dr == DialogResult.Yes)
                {
                    listView.Items.Clear();
                    Tgl.Clear();
                }
            }
        }

        //OKボタンがクリックされた時
        protected void OnClick_OK(object sender, EventArgs e)
        {
            //ListTypeが1、2の場合、CmdListまたはExeListを直接編集するので戻り値は無い
            if(ListType == 0)    //単純選択の場合のみ、Tgs(TGSet)を返す
            {
                //選択された行を確認する
                if(listView.SelectedItems.Count == 0)
                {
                    MessageBox.Show("データが選択されていません", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    return;
                }
                else
                {
                    Selection = listView.SelectedIndices[0];    //CmdListのSelection番目のTGSet
                }
            }
            //このフォームの戻り値
            DialogResult = DialogResult.OK;
            Close();
        }

        //Cancelボタンがクリックされた時
        protected void OnClick_Cancel(object sender, EventArgs e)
        {
            Selection = -1;        //未選択
            //このフォームの戻り値
            DialogResult = DialogResult.Cancel;
            Close();
        }

        //listViewがダブルクリックされた時
        protected void ListView_DoubleClick(object sender, MouseEventArgs e)
        {
            //選択された項目があるか確認
            if(listView.SelectedIndices.Count > 0)
            {
                Selection = listView.SelectedIndices[0];
                //解説:引数typeにより処理が変わる。

                if(ListType == 0)        //命令セット選択
                {
                    DialogResult = DialogResult.OK;
                    Close();            //Selection設定のみで終了
                }
                else if(ListType == 1)    //命令セットライブラリー(CmdList)
                {
                    TGSet copy = new TGSet(Tgl.GetSet(Selection));    //編集用データは直接変更されないようにコピーを渡す
                    CmdSetDlg csd = new CmdSetDlg(copy);            //データコピーを使って編集
                    if(csd.ShowDialog(this) == DialogResult.OK)        //編集結果がOKなら
                    {
                        Tgl.GetSet(Selection).Set(csd.Tgs);            //編集結果をオリジナル命令セットに代入する
                        listView.SelectedItems[0].Text = Selection.ToString();
                        listView.SelectedItems[0].SubItems[1].Text = csd.Tgs.Name;
                        listView.SelectedItems[0].SubItems[2].Text = csd.Tgs.Description;
                        MessageBox.Show(Selection.ToString() + "番目の命令セットを編集しました。", "確認", MessageBoxButtons.OK, MessageBoxIcon.Information);
                    }
                    csd.Dispose();
                }
            }
        }

        //listViewのコラムがクリックされた時
        private void ListView_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;
        }
    }

 

今日アップした本文を見て、すぐに朝令暮改しました。

 

オリジナルは「命令入力ダイアログ」でPenColorを選択すると「引数1」だけがカラー選択ダイアログで選べるようになり(またはキャンセルして手入力をするのですが)、

 

「繰り返し回数」による引数の変化を与えられませんでした。

 

これはいかん!

 

ということで、「命令入力ダイアログ」の引数2と引数3の".Enable3d = false;"の属性をカラー選択ダイアログ終了後、".Enable3d = true;"に戻し、色を選択するにせよ、キャンセルするにせよ、

(解説:↑はを選択した状態)

 

引数2、引数3による入力を解禁しました。これでループ中に色を変えることができます。具体的には、色情報はARGB(Alpha、Red、Green、Blue)が8bit0~255迄上位からARGBの形で並んでいるので、の場合は

 

 A-完全な不透明の場合、-1(0XFF)が入り、10進数では16,777,216(256X256X256)倍されます。

R-赤の場合-1(0XFF)が入り、10進数では65,536(256X256)倍されます。

Gー緑の場合-1(0XFF)が入り、10進数では256倍されます。

Bー青の場合-1(0XFF-10進数で255)が入ります。

= 0xFF0000FF16進数)

= -16,777,216 + 0 + 0 + 255=-16,776,961(上記ダイアログ値参照)

 

ということで、

 

今回の変更により、(例えば)引数2に与えるべき数は、

 

引数2=R部分の変更値x65,536倍G部分の変更値x256倍B部分の変更値

 

ということになります。面倒ですが、一応値を変更できるようになったので

 

お許しを!

 

ps. なお、先般アップした「ご参考」も本日修正しました。

 

                        case 11:            //PenColor(Color構造体←整数の変換-解説:Colorのメソッドです。)
                            Turtle.PenColor = Color.FromArgb(cmd[1] + cmd[2] * i + cmd[3] / (times - i));    //2026年04月22日変更
 

前回はTGEライブラリーのTurtleGraphics命令のデータ処理部分をやりましたので、今回はそのUI部分である「命令セットダイアログ(CmdSetDlg)」と「命令リストダイアログ(CmdListDlg)」をやりましょう。

 

復習になりますが、命令セットクラス(TGSet)のデータ部分は

    /////////////////////////////////////////////////
    //命令セットクラス(名称と説明付きの命令セット)
    /////////////////////////////////////////////////

    public class TGSet
    {    //クラスメンバー
        List<int[]> CmdSet;                        //命令配列
        public string Name {get; set;}            //命令セット名
        public string Description {get; set;}    //説明

(以下略)

 

命令リストクラス(TGList)のデータ部分は

    ///////////////////////////////////////////
    //命令リストクラス-命令セットの集合クラス
    ///////////////////////////////////////////

    public class TGList
    {
        //クラスメンバ
        List<TGSet> CmdList;
        public string FileName {get; set;}        //ファイル入出力名
(以下略)

 

のようになっていたことを念頭に以下解説します。

 

先ず「命令セットダイアログ」(表示は「命令セット入力ダイアログ」)で命令セットの作成を行う

には「命令追加」ボタンを押して命令を入力することが必要です。

 

命令追加」ボタンを押すと「入力ダイアログ(InputDlg)」(表示は「命令入力ダイアログ」)が現れるので、それで行います。

 

入力ダイアログ」の左にあるドロップダウンリストをプルダウンするとNopからDelayまでの命令が表示されるので、

 

例えばForwardを選択して引数を入力しようとすると引数の入力の際に引数の説明文が現れます。

 

引数が一つの殆どの命令はすべて同じ説明ですが、MoveTo

 

PenClolorだけ例外扱いとなります。特にPenColorは引数1のみが入力可能となり、入力しようとすると

 

カラー選択ダイアログが現れて数値入力を省略カラー選択ダイアログをキャンセルすると手入力が可能です)することができます。

 

命令の入力が終わる()と、

:この命令セットを繰り返す場合には、右の「繰り返し回数」のテキストボックスに回数を入力します。

 

命令・説明」のボタンを押して、専用入力画面()で入力します。「命令・説明」のボタンが変化した「命令セット」のボタンを押すと命令セット入力画面に戻ります。

:これは別のダイアログではなく、(Tabコントロールのように)同じダイアログのコントロールのVisible属性を弄ってこのように見せているだけです。

 

入力が終わるとその命令セットはTGEditクラスCmdList)に追加され(最下段参照)、それは(命令セットの追加、削除、編集が可能な)命令セットライブラリー」という表示のCmdListDlgで確認することができます。又、このダイアログは最上部のコラムをクリックすることでソートされたり、選択した行の命令セットをダブルクリックすると、

TGEditorが終了する際にこのライブラリー情報はCmdList.tgeというファイルに自動出力され、TGEditorを起動するたびに自動入力されます。

 

その命令セットが「命令セット編集ダイアログ」で編集可能となります。これは夫々の命令についても同じで、各命令をダブルクリックすると、

 

命令編集ダイアログ」が現れ、その命令を編集することができます。

 

(ご参考)両ダイアログとも「入力」モードで使用する場合、TGSetクラスインスタンスをnewにより新しく生成しますが(値渡し)、「編集」モードではTGEditクラスメンバーのTGListクラスインスタンスCTGSetクラスインスタンスを直接扱います。(参照渡し)

 

命令セットライブラリー」という表示のCmdListDlgは「描画部品」である命令セットのライブラリーの追加()、削除、編集を行います。

:↑で既に見た命令セット入力ダイアログ」が表示されるので、それで入力します。

 

命令セットライブラリー」の命令セット単体の実行は主にテスト目的で行われますが、「命令セット選択」という表示の「命令選択ダイアログ

 

で命令セットを選択して「OK」ボタンを押すか、ダブルクリックすると、「命令選択ダイアログ」の終了確認が出るので、終了させると実行されます。

 

CmdListDlgの「描画部品」である命令セットから選択したものを組み合わせて実行する場合には、「実行命令セット一覧

 

という表示のTGEditクラスの「実行リスト(ExeList))専用ダイアログを使います。このダイアログでは「命令セット追加」ボタンを押しても「命令セット入力ダイアログ」は現れず、↑の「命令選択ダイアログ」が出ますので、それでライブラリーから選択します。又ダブルクリックしても編集ダイアログはあられません。純粋にライブラリーからの選択、削除だけとなります。

:TGEditorの「ファイル」メニューの操作はこのExeListに対して行われます。従って特定のExeListをファイル(「実行ファイル」と言います)に(名前を付けて)保存したり、読み込んだりすることができます。ライブラリーファイルは自動保存、自動読み込みですが、実行ファイルユーザー管理で保存、自動読み込みします。

 

例えば↑の実行リストを実行すると、

 

となります。なお、「命令セットライブラリー」、「命令セット選択」、「実行命令セット一覧」という同じようなダイアログは(「命令セット入力・編集ダイアログ」、「命令入力・編集ダイアログ」と同様)同一ダイアログのコントロールや処理を制御して使い分けるようにしています。

 

繰り返しになりますが、メニューは、

 

ファイル」(実行ファイル関連操作)、

編集」(命令セット作成、命令リストによるライブラリー命令セットの作成、編集、削除、および命令リストによる実行リスト命令セットの選択、削除)、

実行」(命令選択ダイアログによる命令セット単体の実行、実行リストの実行)、

ヘルプ」(手抜きのメッセージボックス情報とバージョンダイアログが出ます)

 

で構成され、主要な処理はツールバーボタンで即実行できます。最下段のステータスバーにはバージョン情報と実行ファイルのファイルパスが表示されます。

 

前回と同様、コードは「ご参考」として後日アップします。

 

前回予告しました「TGEditクラス」「TGListクラス」「TGSet クラス」のコードを「ご参考」として掲載します。

コードの大部分はコメントだけで分かると思いますが、TGSetクラスのDoThis()(命令の実行)だけは解説:を入れます。

尚、ダイアログ部分は本編の解説が終わってからとなります。

 

///////////////////////////////////////////////////////////////////////////////
//                      TGEライブラリー (TGE.dll)
//                     Copyright (c) 2026 by Ysama
//
//                               【概要】
//LOGOベースの描画命令を纏めた「命令セット(TGSet)」、その一覧である「命令リス
//ト(TGList)」、「命令リスト」から指定した命令セットで、繰り返しを含め実行順を
//定める「実行リスト(TGList)」及び編集ダイアログをまとめたNakov.TurtleGraphics
//の命令編集ライブラリー
///////////////////////////////////////////////////////////////////////////////

using System;
using System.Windows.Forms;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;        //Listを使う為
using System.IO;                        //FIle関係
using System.Text;                      //Encoding.GetEncoding を使用するのに必要
using Nakov.TurtleGraphics;                //TurtleGraphicsを使う為

namespace TGE
{
    /////////////////////////////////////////////
    //TurtleGraphics命令を編集するラッパークラス
    /////////////////////////////////////////////

    public class TGEdit
    {
        //クラスフィールド

        public TGSet CmdSet;            //命令セット({コマンド番号, 引数}の整数配列 )
        public TGList CmdList;            //命令リスト(命令セットの一覧)
        public TGList ExeList;            //実行リスト(指定する命令セットの実行順リスト)

        //コンストラクター
        public TGEdit(Form form = null)
        {
            if(form == null)
                Turtle.Init();
            else
                Turtle.Init(form);
            CmdSet = new TGSet();
            CmdList = new TGList();
            ExeList = new TGList();
        }

        //TurtleGraphicsのDispose/Reset
        public void Reset()
        {
            Turtle.Reset();
        }

        //亀の表示/非表示を確認する
        public bool IsTurtleShown()
        {
            return Turtle.ShowTurtle;
        }

        //亀を表示/非表示する
        public void ShowTurtle(bool flag)
        {
            Turtle.ShowTurtle = flag;
        }
    }

    ///////////////////////////////////////////
    //命令リストクラス-命令セットの集合クラス
    ///////////////////////////////////////////

    public class TGList
    {
        //クラスメンバー
        List<TGSet> CmdList;
        public string FileName {get; set;}        //ファイル入出力名

        //コンストラクター
        public TGList()
        {
            CmdList = new List<TGSet>();
            FileName = String.Empty;
        }

        //コピーコンストラクター
        public TGList(TGList tgl)
        {
            CmdList = new List<TGSet>(tgl.CmdList);
            FileName = tgl.FileName;
        }

        //TGListの初期化
        public void Clear()
        {
            CmdList.Clear();
            FileName = String.Empty;
        }

        //TGListの命令セット数
        public int CountSets()
        {
            return CmdList.Count;
        }

        //n番目の命令セットを取得
        public TGSet GetSet(int n)
        {
            if(n < 0 || n > CmdList.Count - 1)
                return new TGSet();                //空のTGSetを返す
            return CmdList[n];
        }

        //TGListへのTGSetの追加
        public void AddSet(TGSet tgs)
        {
            CmdList.Add(new TGSet(tgs));        //Deep copy
        }

        //n番目の命令セットの削除
        public bool RemoveAt(int n)
        {
            if(n < 0 || n > CmdList.Count - 1)
                return false;
            else
                CmdList.RemoveAt(n);
            return true;
        }

        //n番目に命令セットを挿入
        public bool InsertSet(int n, TGSet tgs)
        {
            if(n < 0 || n > CmdList.Count - 1)    //最後尾に追加するにはAddを使う
                return false;
            else
                CmdList.Insert(n, tgs);
            return true;
        }

        //CmdListへTGSetを介して命令セット入力
        public bool ReadList(string data)
        {
            //入力用TGSet
            TGSet tgs = new TGSet();
            //文字列の行分割
            string[] lines = data.Split(new[]{"\r\n","\n","\r"}, StringSplitOptions.None);
            //行の語分割と整数配列の作成
            bool Is1stTime = true;            //最初の命令セットの’-1'か否かのフラグ
            foreach(string line in lines)
            {
                if(line == String.Empty)    //(有る筈のない)空行に遭遇したら中止する
                    break;
                string[] dt = line.Split(',');
                if(dt[0] == "Name:")        //命令セットデータの最終行は"Name:,(Name),(Description)"とする
                {
                    tgs.Name = dt[1];
                    tgs.Description = dt[2];
                    continue;                //次行へスキップ
                }
                int cmd = 0, arg1 = 0, arg2 = 0, arg3 = 0;    //入力結果
                if(!int.TryParse(dt[0], out cmd))
                    return false;
                else
                {
                    if(cmd == -1)            //命令'-1'はセット開始(先行セット終了、セットの区切り)
                    {
                        if(!Is1stTime)        //最初の-1で先行セットがなければAddSet()は不要
                            AddSet(tgs);    //CmdListに今迄の命令セットを登録
                        else
                            Is1stTime = false;
                        tgs.Clear();        //次の命令セットの為に入力用tgsを初期化する
                    }
                }
                if(!int.TryParse(dt[1], out arg1))
                    return false;
                if(!int.TryParse(dt[2], out arg2))
                    return false;
                if(!int.TryParse(dt[3], out arg3))
                    return false;
                tgs.AddCmd(cmd, arg1, arg2, arg3);
            }
            AddSet(tgs);                    //最後の命令セットを登録
            return true;
        }

        //CmdListの命令セットの一覧出力
        public string WriteList()
        {
            string str = String.Empty;
            foreach(TGSet tgs in CmdList)
            {
                str += tgs.WriteSet();
            }
            return str;
        }

        //CmdListの命令セットの実行
        public void DoSet()
        {
            foreach(TGSet tgs in CmdList)
            {
                tgs.DoThis();
            }
        }

        ///////////////////////////////////////////////////////////////////////////////////////
        // >>> "*.tge"のファイルについて <<<
        //(1) LOGOベースのTurtleGraphicsの命令に対応した命令番号と引数一つのテキストcsvファイル
        //(2) 命令'-1'はセット開始(先行セット終了、セットの区切り)で引数は繰り返し回数
        //(3) 命令セットデータの最終行は"Name:,(Name),(Description)"とする
        ///////////////////////////////////////////////////////////////////////////////////////

        //tgdファイル書き込み(UTF-8:Encoding.UTF8)
        public bool WriteFile()
        {
            if(FileName == String.Empty)
                return false;
            using(StreamWriter file = new StreamWriter(FileName, false, Encoding.UTF8))
            {
                //ファイルを書く
                file.Write(WriteList());
                //ファイルを閉じる
                file.Close();
            }
            return true;
        }

        //tgdファイル読み込み(UTF-8:Encoding.UTF8)
        public bool ReadFile()
        {
            if(FileName == String.Empty)
                return false;
            string data = String.Empty;
            try
            {
                using(StreamReader file = new StreamReader(FileName, Encoding.UTF8))
                {
                    data = file.ReadToEnd();
                    // ファイルを閉じる
                    file.Close();
                }
            }
            catch(Exception e)    //FileNotFoundException e
            {
                //Debug用
                //MessageBox.Show("エラーコード:" + e.ToString(), "初期化ファイル読み込みエラー", MessageBoxButtons.OK, MessageBoxIcon.Error);

                MessageBox.Show("ファイルを読み込めませんでした。\r\n。エラーコード:" + e.ToString(), "確認", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return false;
            }
            return ReadList(data);
        }
    }

    /////////////////////////////////////////////////
    //命令セットクラス(名称と説明付きの命令セット)
    /////////////////////////////////////////////////

    public class TGSet
    {    //クラスメンバー
        List<int[]> CmdSet;                        //命令配列
        public string Name {get; set;}            //命令セット名
        public string Description {get; set;}    //説明

        //コンストラクター1
        public TGSet()
        {
            CmdSet = new List<int[]>();
            Name = String.Empty;
            Description = String.Empty;
        }

        //コンストラクター1
        public TGSet(string name, string description)
        {
            CmdSet = new List<int[]>();
            Name = name;                        //再設定自由
            Description = description;            //再設定自由
        }

        //コピーコンストラクター
        public TGSet(TGSet copy)
        {
            CmdSet = new List<int[]>(copy.CmdSet);
            Name = copy.Name;
            Description = copy.Description;
        }

        //初期化
        public void Clear()
        {
            CmdSet.Clear();
            Name = String.Empty;
            Description = String.Empty;
        }

        //命令セットの命令数
        public int Count()
        {
            return CmdSet.Count;
        }

        //命令セットの設定(1)
        public void Set(TGSet tgs)
        {
            CmdSet.Clear();
            CmdSet = new List<int[]>(tgs.CmdSet);
            Name = tgs.Name;
            Description = tgs.Description;
        }

        //命令セットの設定(2)
        public void Set(List<int[]> list, string name, string description)
        {
            CmdSet.Clear();
            CmdSet = new List<int[]>(list);
            Name = name;
            Description = description;
        }

        //n番目の命令取得
        public int[] GetCmd(int n)
        {
            if(n < 0 || n > CmdSet.Count - 1)
                return new int[] {-1, -1, -1, -1};            //エラー
            else
                return CmdSet[n];
        }

        //n番目の命令設定(1)(命令番号cmdは必須、定数引数arg1は重要、arg2とarg3はオプション扱い)
        public bool SetCmd(int n, int cmd, int arg1, int arg2 = 0, int arg3 = 0)
        {
            if(n < 0 || n > CmdSet.Count - 1)
                return false;
            CmdSet[n] = new int[] {cmd, arg1, arg2, arg3};    //Deep copy
            return true;
        }

        //n番目の命令設定(2)
        public bool SetCmd(int n, int[] cmd)
        {
            if(n < 0 || n > CmdSet.Count - 1 || cmd.Length != 4)
                return false;
            CmdSet[n] = cmd;
            return true;
        }

        //命令の追加(命令番号cmdは必須、定数引数arg1は重要、arg2とarg3はオプション扱い)
        public void AddCmd(int cmd, int arg1, int arg2 = 0, int arg3 = 0)
        {
            CmdSet.Add(new int[] {cmd, arg1, arg2, arg3});    //Deep copy
        }

        //命令の追加(配列渡し)
        public bool AddCmd(int[] cmdarr)
        {
            if(cmdarr.Length != 4)
                return false;
            else
            {
                CmdSet.Add((int[])cmdarr.Clone());            //Deep copy
                return true;
            }
        }

        //命令の削除
        public bool RemoveAt(int n)
        {
            if(n < 0 || n > CmdSet.Count - 1)
                return false;
            else
                CmdSet.RemoveAt(n);
            return true;
        }

        //n番目に命令を挿入(命令番号cmdは必須、定数引数arg1は重要、arg2とarg3はオプション扱い)
        public bool Insert(int n, int cmd, int arg1, int arg2 = 0, int arg3 = 0)
        {
            if(n < 0 || n > CmdSet.Count - 1)                //最後尾に追加するにはAddを使う
                return false;
            else
                CmdSet.Insert(n, new int[] {cmd, arg1, arg2, arg3});
            return true;
        }

        //n番目に命令を挿入(配列渡し)
        public bool Insert(int n, int[] cmdarr)
        {
            if(n < 0 || n > CmdSet.Count - 1)                //最後尾に追加するにはAddを使う
                return false;
            else
                CmdSet.Insert(n, (int[])cmdarr.Clone());
            return true;
        }

        //命令の一覧出力
        public string WriteSet()
        {
            string str = String.Empty;
            foreach(int[] array in CmdSet)
            {
                //str += array[0].ToString() + "," + array[1].ToString() + ","
                //    + array[2].ToString() + "," + array[3].ToString() + Environment.NewLine;
                str += string.Join(",", array) + Environment.NewLine;
            }
            str += "Name:" + "," + Name + "," + Description + Environment.NewLine;
            return str;
        }

        //命令(int[4]-コマンド番号、引数1、引数2、引数3)の実行
        public void DoThis()    //解説:TurtleGraphicsの命令処理を行います。
        {
            int times = 1;    //解説:繰り返し回数です。既定値は「1回」です。
            for(int i = 0; i < times; i++)    //最低でも規定値1回は実行
            {    //cmdは命令(cmd[0])と引数(cmd[1~3])の整数配列
                foreach(int[] cmd in CmdSet)
                {    //引数cmd[1]は定数、cmd[2]は回数の倍数、cmd[3]は回数による分数(ともに規定値は0)
                    switch(cmd[0])            //引数cmd[0]は命令番号
                    {
                        case -1:            //命令セット開始(解説:これはTurtleGraphicsの命令ではなく、TGSet独自命令です。)
                            times = cmd[1];    //引数は繰り返し回数(解説:1回以上の場合、timesのループ回数が更新されます。)
                            cmd[0] = 0;        //繰り返しの為にNOPにする(解説:2回目からはNOPで「何もしません」。)
                            break;
                        case 0:                //NOP - No OPeration(解説:-1命令はループ中はNOPになります。)
                            break;
                        case 1:                //Forward(arg)(解説:基本的に引数は以下の式になります。)
                            Turtle.Forward(cmd[1] + cmd[2] * i + cmd[3] / (times - i));
                            break;
                        case 2:                //Backward(arg)
                            Turtle.Backward(cmd[1] + cmd[2] * i + cmd[3] / (times - i));
                            break;
                        case 3:                //MoveTo(arg1, arg2)(解説:引数の使い方が例外となります。)
                            int x = cmd[1] + cmd[3] * i;    //arg1(cmd[3]のループ倍数が加算)
                            int y = cmd[2] + cmd[3] * i;    //arg2(cmd[3]のループ倍数が加算)
                            Turtle.MoveTo(x, y);
                            break;
                        case 4:                //Rotate(arg)
                            Turtle.Rotate(cmd[1] + cmd[2] * i + cmd[3] / (times - i));
                            break;
                        case 5:                //RotateTo(arg)
                            Turtle.RotateTo(cmd[1] + cmd[2] * i + cmd[3] / (times - i));
                            break;
                        case 6:                //PenUp()
                            Turtle.PenUp();
                            break;
                        case 7:                //PenDown()
                            Turtle.PenDown();
                            break;
                        case 8:                //X
                            Turtle.X = cmd[1] + cmd[2] * i + cmd[3] / (times - i);
                            break;
                        case 9:                //Y
                            Turtle.Y = cmd[1] + cmd[2] * i + cmd[3] / (times - i);
                            break;
                        case 10:            //Angle
                            Turtle.Angle = cmd[1] + cmd[2] * i + cmd[3] / (times - i);
                            break;
                        case 11:            //PenColor(Color構造体←整数の変換-解説:Colorのメソッドです。)
                            Turtle.PenColor = Color.FromArgb(cmd[1] + cmd[2] * i + cmd[3] / (times - i));    //2026年04月22日変更
                            break;
                        case 12:            //PenSize
                            Turtle.PenSize = cmd[1];
                            break;
                        case 13:            //PenVisible (1 - true, 0 - false)
                            Turtle.PenVisible = (cmd[1] == 1);
                            break;
                        case 14:            //Delay
                            Turtle.Delay = cmd[1];
                            break;
                        default:
                            break;
                    }
                }
            }
            CmdSet[0][0] = -1;                //配列は参照引き渡しの為、値-1に復旧する
        }
    }

 

前回の「進め方」で書いた通り、今回はTGEditorの基盤となる旧TGEditクラスから現在のTGE.dllに至る迄、何度か繰り返した仕様変更について(プログラミングをされる方のご参考になればと)書いてみます。

 

仕様変更のポイントは、

 

(1)命令文の引数の数

(2)命令の繰り返し実行をどうするか

(3)UI(User Interface)との関係

(4)クラス化する際の構成

 

に纏められます。これらの観点で前期~中期~後期に分けて振り返ります。

 

1.前期

Visual Studio版を完成させ、C#5へ移植していた時期です。最終的にはTGEditorのようなものを作ることは意図していました。

 

(1)命令文の引数の数

元々は「ネタ探し」と「Visual Studio専用機」を活用する為に、WebとNuGetで面白いものを探していて「あっ、そうだ!TurtleGraphicsなんてあったね?」ということで始まった訳ですが、LOGO言語ベースのコマンドは↓のように比較的単純

 

『亀メソッド』(基本引数は一つで、MoveToのみ引数が二つ)
Init(arg1)-タートルグラフィックスシステムを初期化します。arg1は描画対象のFormsコントロールで、指定しない場合、アクティブなフォームが描画領域となります。
Dispose() / Reset()-タートルグラフィックスシステムを破棄し、関連するすべてのリソースを解放します。
Forward(arg1)-現在の方向に亀をarg1ピクセル前進させ、ペンが下がっている場合線を描きます。
Backward(arg1)-現在の方向に亀をarg1ピクセル後退させ、ペンが下がっている場合線を描きます。
MoveTo(arg1, arg2)-亀を指定した位置へ移動させます。ペンが下がっている場合線を描きます。
Rotate(arg1)-現在の向きからarg1(度数)亀を回転させます。
RotateTo(arg1)-亀を指定角度(度数)へ向けます。初期状態の亀の方向(角度)は0度(上向き)です。
PenUp()-ペンを描画面から離し、線は描かれません。
PenDown()-ペンを描画面に降ろし、線が描かれます。

『亀プロパティ』(引数は一つ)
X-亀の現在X座標を取得/設定します。初期状態の亀は {0, 0}(画面中央)です。
Y-亀の現在Y座標を取得/設定します。初期状態の亀は {0, 0}(画面中央)です。
Angle-亀の現在方向(度数)を取得/設定します。(上-0、右-90、下-180、左-270)
PenColor-描画色を取得/設定します。既定色はBlue(青)です。
PenSize-描画太さ(ピクセル単位)を取得/設定します。規定値は7です。
PenVisible-ペンが下がり描画可能であればtrue、ペンが上がればfalseとなります。
ShowTurtle-亀の表示可否を取得/設定します。規定値は表示可です。
Delay-移動/回転後の待機時間(ミリ秒)を取得/設定します。規定値は0です。

 

で、「毎回サンプルを作りコンパイルを繰り返すのってどーよ?」と感じたこともあり、先ず個々の命令文命令番号(オペコード)引数(オペランド)を与えて実行する形にしました。従って当初は引数2つの"MoveTo"命令があることから、

 

命令文 = new int[3]  {ijnt[0[(オペコード), int[1](オペランド1), int[2](オペランド2)};

 

という整数3つのint[](整数配列)とし、命令文Listで管理することにし、命令文の配列CmdSet(Command Set)、CmdSetの配列をCmdList(Command List)、としていました

 

        //クラスフィールド
        List<int[]> CmdSet = new List<int[]>();                    //描画設定の整数配列int[] {コマンド番号, 引数1、引数2}
        List<List<int[]>> CmdList = new List<List<int[]>>();    //構成設定の描画設定Listの配列
 

しかし"MoveTo"命令しか引数2を使わないことから、命令文をコンパクト化して整数2つ(命令番号, 引数}」)()にしました。(TurtleGraphics02

"MoveTo"命令の引数はHIWORDとLOWORDで整数一つに纏めました。(上位16bit値 = 16bit値 << 16; 下位16bit値 = 16bit値 & 0xFFFF;としてから(上位16bit値 | 下位16bit値)して作ります。分割する際はこの逆にビット処理します。)

 

(2)命令の繰り返し実行をどうするか

まだこの段階では気が付いていませんでした。

 

(3)UI(User Interface)との関係

まだこの段階ではダイアログ等のUIの在り方は具体化していませんでした。

 

(4)クラス化する際の構成

この段階では、上記の通り命令文の配列CmdSet)、CmdSetの配列であるCmdListを単純にTGEditクラスに入れればよいと考えていました。

 

2.中期

(1)命令文の引数の数(2)命令の繰り返し実行をどうするか

 

しかし、

 

一件落着と考えていた命令文について、ある日朝方に寝床で大失敗に気づきます。

 

複数の命令を繰り返し処理する場合、与える引数が定数だけじゃん!

 

例えばサンプルの赤渦巻き

を描くには、

 

            for (int i = 0; i < 25; i++)
            {
                CmdSet.Add(new int[] {1,
i * 5});    //Turtle.Forward(i * 5);
                CmdSet.Add(new int[] {4,
30 + i});    //Turtle.Rotate(30 + i);
            }

 

のように、ループカウンターを使った加算(減算)、乗算(除算)が必要だったのです。

 

では、どのように対応させたらよいのでしょうか?

 

この問題は更に「ループカウンターを使う対象は加減乗除のみでよいのか?」「ではいくつにすればよいのか?」「フラクタル関数等より複雑な式は使えないがそれでよいのか?」「不特定な計算処理まで考えると本格的なインタープリター機能が必要となり、際限なく複雑化してゆくぞ」と悩み、悩んだ末に(このアプリの性格から考え)私の回答は、引数を複数使い、「オペコード(命令番号-int[0])」に与える「オペランド(引数)」に、

 

引数=定数(int[1])+倍数(int[2] × 繰り返し数)+除数(int[3]÷(最大繰り返し数 - 繰り返し数))

 

という式を与えることでした。これを具体的に言うと、for(int i = 0; i < n; i++)というループの場合に引数120引数210引数30を与えると、最終的に「20 + 10 * i + 0 / (n - i)」という値が引数として与えられるので、「定数だけ、倍数、除数またはそれらの組み合わせ」が可能になる、ということです。(「フラクタル関数等より複雑な式」「不特定な計算処理」はGive upした、ということです。)

例:for(int i = 0; i < 12; i++)

        {

             deg1 = 0 + 30 * i + 0 / (12 - i);         //0, 30, 60, 90, 120, 150 ...

             deg2 = 360 - 30 * i + 0 / (12 - i);     //360, 330, 300, 270 ...

             deg3 = 0 - 0 * i + 360 / (12 - i);       //30, 32, 36, 40, 45 ... 

       }

 

(3)UI(User Interface)との関係

この段階ではUIダイアログの試作を始めており、整数4つの命令文に合わせ、オペコード(命令番号-int[0])をドロップダウンリストにし、3つのオペランド(引数)をテキストボックスで入力することを考えていました。

 

が、

 

ここでも「あ"っ!」が発生します。UIダイアログを試作していた段階で「人間が選択して使うには『名前』と『(それが何かと言う)説明』が必要」という人間の認知の為のUI問題から、「命令文の配列CmdSetに文字列の「名前」と「説明」を付加することを余儀なくされました。(↓の次項参照)

 

(4)クラス化する際の構成

最初はTGEditクラス

 

        //クラスフィールド
        List<int[]> CmdSet = new List<int[]>();                    //描画設定の整数配列int[] {コマンド番号, 引数1、引数2}
        List<List<int[]>> CmdList = new List<List<int[]>>();    //構成設定の描画設定Listの配列
 

をいれればいーや、と思っていた所、更にCmdSet t毎に

 

        public string Name {get; set;}            //命令セット名
        
public string Description {get; set;}    //説明

 

を入れなければならなくなった為に、"TGList"という「クラス内クラス」を急遽作ることにしました。

 

    public class TGEdit
    {
        
//命令リスト(クラス内)クラス-名称と説明付きの命令セット
        public class TGList
        {    
//クラスメンバー
            public List<int[]> intList;
            
public string Name {get; set;}
            
public string Description {get; set;}
          
 //コンストラクター
            public TGList(string name = "", string description = "")
            {
                intList =
new List<int[]>();
                Name = name;
                Description = description;
            }
            
//コピーコンストラクター
            public TGList(TGList copy)
            {
                intList = new List<int[]>(copy.intList);
                Name = copy.Name;
                Description = copy.Description;
            }
        }

 

        //RGEditのクラスフィールド
        TGList CmdSet = new TGList();                //命令セット({コマンド番号, 引数}の整数配列 )
        List<TGList> CmdList = new List<TGList>();    //命令リスト(命令セットの一覧)
        List<TGList> ExeList = new List<TGList>();    //実行リスト(指定する命令セットの実行順リスト)
(略)

}

 

ご覧の通り、この段階でもまだCmdSet は単なる整数配列のListにすぎず、「全体描画の部品」という性格や機能をもっていません。しかし、「CmdSet = 命令セット作成用」「CmdList = 作成した命令セットのライブラリー用命令リスト」「ExeList = ライブラリーから選択した命令セットで作る実行リスト」という概念が具体化してきました。

 

3.後期

 

(1)命令文の引数の数

(2)命令の繰り返し実行をどうするか

繰り返し対応の為に命令文を4つの整数配列(命令番号-int[0]と引数int[1]~int[3])にすることは決定しました。しかし、複数の命令文をどのように命令セットとするのか?また、繰り返し回数をどのようにデータ化するのか、という問題が残っていました。

これに対する私の回答は、命令番号-1で「(新しいの)当該命令セットの開始(同時に先行する命令セットがあればその終了」という定義を行い、その第1引数int[1]として「繰り返し回数を与える仕様にしました。

 

(3)UI(User Interface)との関係

名前」と「説明」を付加することから、命令セット命令リスト(命令番号-1で区切られる複数の命令セットを文字列化して(最終的に入出力用csvファイルに落とすためにも)読み書きするメソッドが必要となり、

 

命令セットの「文字列フォーマット」

-1,(繰り返し回数),0,0

   ・

   ・

   ・

int[0],int[1],int[2],int[3]

int[0],int[1],int[2],int[3]

int[0],int[1],int[2],int[3]

   ・

   ・

   ・

"Name:",(名前),(説明),0

 

命令セットの末尾に入れるようにしました。

 

(4)クラス化する際の構成

この最終段階で、

 

命令文int[4]の配列

 

命令番号(int[0])、引数1(int[1]-定数)、引数2(int[2]-倍数)、引数3(int[3]-除数)

 

繰り返しの対象となる命令セット

 

命令セット(TGSetクラス)

    /////////////////////////////////////////////////
    //命令セットクラス(名称と説明付きの命令セット)
    /////////////////////////////////////////////////
    
public class TGSet
    {    //クラスメンバー
        List<int[]> CmdSet;                        //命令配列
        
public string Name {get; set;}            //命令セット名
        
public string Description {get; set;}    //説明

(以下略)

 

そして元々は旧TGEditクラスの「クラス内クラス」であった命令リストも、命令セット(TGSetクラス)のクラス化に伴い、独立した「命令リスト(TGList)」クラスにしました。

 

命令リスト(TGListクラス)

    ///////////////////////////////////////////
    //命令リストクラス-命令セットの集合クラス
    ///////////////////////////////////////////
    
public class TGList
    {
        //クラスメンバー
        List<TGSet> CmdList;
        
public string FileName {get; set;}        //ファイル入出力名
(以下略)
 

最後に

  • 部品である「命令セット」を作成する為のCmdSet
  • また複数の「命令セット」をライブラリーとして管理する「命令リスト」であるCmdListと、
  • 特定の描画の為に「命令セット」を組み合わせる「命令リスト」であるExeList

を持つTGEditクラスを最上位クラスとしました。この再構成を行った時が「【TurtleGraphics】ちょ待ってね?です。

 

TGEditクラス

    /////////////////////////////////////////////

    //TurtleGraphics命令を編集するラッパークラス
    /////////////////////////////////////////////
    
public class TGEdit
    {
        //クラスフィールド
        
public TGSet CmdSet;            //命令セット({コマンド番号, 引数}の整数配列 )
        
public TGList CmdList;            //命令リスト(命令セットの一覧)
        
public TGList ExeList;            //実行リスト(指定する命令セットの実行順リスト)
(以下略)

 

このように、以下(↓)のように階層化されたクラスに合わせ、並行的に開発してきたUIダイアログ

 

TGEditクラス

      |

TGListクラス ・・・CmdListDlg(命令リスト作成、編集用の「命令リストダイアログ」)

      |

TGSet クラス・・・CmdSetDlg(命令セット作成、編集用の「命令セットダイアログ」)

                                                  |
                                         inputDlg(命令作成、編集用の「入力ダイアログ」)

 

最終的に追加して現在の「TGE.dll」というライブラリーになりました。それをサンプルに適用したのもがTurtleGraphics03です。(TGE.dllの最終テスト、という性格だった訳です。)

 

まぁ、今振り返ればいろんな紆余曲折を経ましたが、クラスの構造や構成は一番整合性がとれた良い形になったと思います。

 

次回は実際に「TGEditクラス」「TGListクラス」「TGSet クラス」のコードを「ご参考」として掲載し、その後に今回触れられなかったUIを担うCmdSetDlgCmdListDlgについて「【TurtleGraphics】TGEditor(4)-UIダイアログ」で説明します。

 

【TurtleGraphics】シリーズも中盤過ぎに差し掛かり、最終目的であるNovak.TurtleGraphics.dllを使って「様々なタートルグラフィックスを試すことができるアプリ」となりました。

 

 

前回の宿題

 

(1)(C#5のコードの紹介の前に)TGEditorの使い方を解説してからコード説明に入るか、

(2)先ずコード説明から入って、誰もがバッチファイルでコンパイルできるようにしてから、実際の使い方に入るか

 

については、

 

(1)コード量が2,000行を超えること(TGE.csが1440行、TGEditor.csが622行)

(2)TGEditorにリソース(ツールバービットマップ)を使っていて、簡単にバッチファイルでコンパイルできるプログラムではないこと

 

から、次のように進めさせていただきます。

 

(1)TGEライブラリー(クラス、ダイアログ)とTGEditor(UI)に分けて、

(2)(この間の「VisualStudio版」の時のように)ブログではプログラミングハイライトを書き、「ご参考」で(必要に 応じ)分割したコードを載せるだけにし、(大変なのでもさぼらせていただきます)

(3)ご自身でコンパイルして使ってみたい、という方にはBCCForm and BCCSkeltonパッケージの中の(MSCompAssでもバッチファイルでも直ぐ様にコンパイルですことができるように)↓を「SampleBCCSkelton」-「MSCompAss」-「Debug」-「Samples」-「TGEditor」フォールダーに入れておきます。

解説:ツールバービットマップの入ったリソースファイルと「亀」アイコン付きです。バッチファイルは(TGE.dllがなければ)TGE.csとTGEditor.csの二つともコンパイルします。

 

では、次回は「【TurtleGraphics】TGEditor(3)-TGEの仕様遷移」から始めさせていただくつもりです。

 

ps. ということで終了させていただこうと思いましたが、標題「進め方」に関連するので、予告しておくと、TGEditorで手仕舞にしようと思っていた所、TGEditorの限界も正しくご理解いただいたうえで、最後っ屁として"TG_Ala_Carte外伝"

 

 

で締めようかと考えております。どうぞ最後までお楽しみください。