メモリエディタを作る

  • ~中級編~

    ここは、前節「ScenarioModを.NETとの混在アセンブリ(C++/CLI)とする」の続きとなります。

    さて、前節では、.NET FrameWorkをSceanrioModの中で使用し、フォームを表示、フォームの中にボタンを1つ配置しました。

    • フォームクラスを用意
    • GUIコンポーネント(ボタンといったもの)用意
    • GUIコンポーネントを何かした時用のイベントハンドラを用意(クリックした時これを実行~など)
    • フォーム上にGUIコンポーネントを配置(Controls->Addの部分)
    • フォーム表示( ->Showの部分)

    といった一連の流れを作りました。

    これは.NET FrameWorkで最も多い「フォームタイプ」の基礎です。

    今節では、これを踏まえて、メモリビューワ→メモリエディタを作ります。

    信じられないほどあっという間に出来上がるので驚くことでしょう。

  • MemoryEditorFromの変更

    前節のMemoryEditorFormの中身の変更となります。

    まずはメモリビューワーを実現してみましょう。

    気を付けるべきはやはりString^型です。
    .NET FrameWorkの String^型と、C/C++のchar*やstd::string 型との相互変換のやり方は知っておく必要があります。

    C++/CLIがC#に比べて若干取り回しがしにくいのは、この文字列周りだと言えるでしょう。

    
    ref class MemoryEditorForm : public Form {
    
    private:
      DataGridView^ dgv;  // データグリッドビュー型。 エクセルのように格子状のデータを表示できるため、非常によく利用される。
    
    public:
      MemoryEditorForm() {
    
        this->Text = "MemoryEditor";
    
        // データグリッド全体のインスタンス
        dgv = gcnew DataGridView();
        dgv->Width = 1000;
        dgv->Height = 800;
    
        // .NETのString^型を要素とする、.NETの配列array^型。
        cli::array<String^>^ colTitle = {"indexID", "姓", "名", "政治", "戦闘", "智謀"};
    
        // -------------- ここからデータグリッドの「縦列に相当するカラムブジェクト」と、「各カラムのタイトル」の文字列をセッティングする。
        // 「index:0 姓:1 名:2 政治:3 戦闘:4 智謀:5」 という順番となる。
    
        for (int i=0; i < colTitle->Length; i++) {
          // 縦列のオブジェクトを作り
          DataGridViewTextBoxColumn^ dgvtbc = gcnew DataGridViewTextBoxColumn();
          // タイトル文字列を設定
          dgvtbc->HeaderText = colTitle[i];
          // グリッドビューに縦列として追加。
          dgv->Columns->Add(dgvtbc);
        }
    
        // 各カラムに天翔記のデータ(532人の武将の、名字、名前、政才、戦才、智才)を足してゆく
        DgvDataImport();
    
        // データグリッドビューをフォームに乗っける
        this->Controls->Add(dgv);
      }
    
    private:
      void DgvDataImport() {
    
        // 横列単位で足してゆく、index:0, 姓:1, 名:2, 政治:3 戦闘:4 智謀:5 の順番通り
        for ( int i=0; i<最大数::武将情報::配列数; i++) {
          dgv->Rows->Add(
            i,
            String←string( Get_名字(i) ),
            String←string( Get_名前(i) ),
            p武将情報[i].最大政才,
            p武将情報[i].最大戦才,
            p武将情報[i].最大智才
            );
        }
      }
    };
    


  • メモリエディタへ

    最後にメモリエディタとしましょう

    グリッドの値が変化した時に、ScenarioModでやってきた通り、
    p武将情報[i].*** などに値を代入すれば良いだけです。

    ref class MemoryEditorForm : public Form {
    
    private:
      DataGridView^ dgv;  // データグリッドビュー型。 エクセルのように格子状のデータを表示できるため、非常によく利用される。
    
    public:
      MemoryEditorForm() {
    
        this->Text = "MemoryEditor";
    
        // データグリッド全体のインスタンス
        dgv = gcnew DataGridView();
        dgv->Width = 1000;
        dgv->Height = 800;
    
        // .NETのString^型を要素とする、.NETの配列array^型。
        cli::array<String^>^ colTitle = {"indexID", "姓", "名", "政治", "戦闘", "智謀"};
    
        // -------------- ここからデータグリッドの「縦列に相当するカラムブジェクト」と、「各カラムのタイトル」の文字列をセッティングする。
        // 「index:0 姓:1 名:2 政治:3 戦闘:4 智謀:5」 という順番となる。
    
        for (int i=0; i < colTitle->Length; i++) {
          // 縦列のオブジェクトを作り
          DataGridViewTextBoxColumn^ dgvtbc = gcnew DataGridViewTextBoxColumn();
          // タイトル文字列を設定
          dgvtbc->HeaderText = colTitle[i];
          // グリッドビューに縦列として追加。
          dgv->Columns->Add(dgvtbc);
        }
    
        // 各カラムに天翔記のデータ(532人の武将の、名字、名前、政才、戦才、智才)を足してゆく
        DgvDataImport();
    
        // データグリッドのセルを編集した時のイベントハンドラを登録する。
        dgv->CellValueChanged += gcnew DataGridViewCellEventHandler(this, &MemoryEditorForm::dgv_CellValueChanged);
    
        // データグリッドビューをフォームに乗っける
        this->Controls->Add(dgv);
      }
    
    private:
      void DgvDataImport() {
    
        // 横列単位で足してゆく、index:0, 姓:1, 名:2, 政治:3 戦闘:4 智謀:5 の順番通り
        for ( int i=0; i<最大数::武将情報::配列数; i++) {
          dgv->Rows->Add(
            i,
            String←string( Get_名字(i) ),
            String←string( Get_名前(i) ),
            p武将情報[i].最大政才,
            p武将情報[i].最大戦才,
            p武将情報[i].最大智才
            );
        }
      }
    
      void dgv_CellValueChanged(Object^ sender, DataGridViewCellEventArgs^ e)	{
        // イベントが発生したオブジェクト(=sender) はデータグリッドビュー型なので、データグリッドビュー型として受け取って…
        DataGridView^ grid = (DataGridView^)sender;
    
        // 編集したセルの「行」の一番左のカラムである「indexID(=0列目)」に入っている値が、武将番号【配列用】である。
        // ユーザが手で入力したものは全て文字列になってしまう。
        // 一方プログラムからDgvDataImportでは最初は数値で入れていた。
        // よって、どのようなデータ型でも良いように、ToStringによってすべて文字列に直した後、Convert::***によって、整数型にする。
        int iBushouID = Convert::ToInt32(grid[0, e->RowIndex]->Value->ToString()); // indexIDが一番左だからcolumnは0
    
        // 発生したイベントの列が3番目なら
        if (e->ColumnIndex == 3) {
          // 該当のグリッドの値をbyte型(256までの型)にして、p武将情報に代入する。
          p武将情報[iBushouID].最大政才 = Convert::ToByte(grid[e->ColumnIndex, e->RowIndex]->Value->ToString());
        }
        // 発生したイベントの列が4番目なら
        if (e->ColumnIndex == 4) {
          // 該当のグリッドの値をbyte型(256までの型)にして、p武将情報に代入する。
          p武将情報[iBushouID].最大戦才 = Convert::ToByte(grid[e->ColumnIndex, e->RowIndex]->Value->ToString());
        }
        // 発生したイベントの列が5番目なら
        if (e->ColumnIndex == 5) {
          // 該当のグリッドの値をbyte型(256までの型)にして、p武将情報に代入する。
          p武将情報[iBushouID].最大智才 = Convert::ToByte(grid[e->ColumnIndex, e->RowIndex]->Value->ToString());
        }
        デバッグ出力 << "budhou:" << iBushouID << "row:" << e->RowIndex << "column:" << e->ColumnIndex << endl;
    
      }
    };