そういえば、PPCM の法則に優先順位を決めたら優先順位が変わらない間はそのままの実行順序で行く。その間に優先順位が変わるような状況があったら、再び優先順位を見直す、てのがある。PDCA の基本でもあるし、アジャイルのアジャイルたるところでもあるし、手順を作って突っ走るほうのが一番早いウォーターフォール開発ってのもその意味では正しい。が、これらの「優先順位づけ」が「コストが掛かる」≒「時間がかかる」てのが前提条件になっていて、実際いろいろな状況を鑑みて計画を練り直す(Excel シートに書き付けた計画シートを書き直す、という作業的なものも含めて)のはコストがかかるわけで、ある程度の期間は計画はそのままというのがベター。となると、逆に計画変更のコストが安くなれば?ってのが plan-language の発端だったなぁ、と思い返してみたり。いやいや、ひとりでやっているときは、タイムボックス的なところの計画と、詳細のタスク分割とは切り離して考えるのがベターかなと。
■取り札のコントロールを作る(その1)
昨日の続きで、取った札をユーザーコントロールに
最初は、動的に PictureBox を増減させていたのですが、簡単のため≒バグを少なくするために、あらかじめ花札の枚数分だけ用意しておきます。デザイナでペタペタと張り付けて…というのは大変なので、コントロールのコンストラクタで作ります。このあたり、最終的には XAML にするパターンになるのでかなり違ってしまうのが難点ですが、思想は同じようにできる感じで。
なぜか Controls に逆順で挿入しないとダメなのと(デザイナ的には、順方向でいいはずなんですが)、一度 PictureBox を表示しておかないと、上下がおかしくなるというバグ的な動きが。まあ、これはプロトタイプなので、そのままにしておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | public partial class GainControl : UserControl { public GainControl() { InitializeComponent(); // あらかじめ 48 枚分用意しておく this .pictureBox1.Visible = false ; _pics = new List<PictureBox>(); int x = this .pictureBox1.Location.X; int y = this .pictureBox1.Location.Y; for ( int i = 0; i < 48; i++) { var pic = new PictureBox(); pic.Size = this .pictureBox1.Size; pic.Location = new Point(x, y); pic.SizeMode = this .pictureBox1.SizeMode; pic.Visible = true ; pic.Image = CardUI.GetUra(); x += 20; _pics.Add(pic); } // 逆順で Controls に追加 _pics.Reverse(); foreach ( var p in _pics) { this .Controls.Add(p); } // 元に戻す _pics.Reverse(); } protected List<PictureBox> _pics; protected List<Card> _data; public List<Card> DataSource { get { return _data; } set { _data = value; UIUpdate(); } } protected void UIUpdate() { if (_data == null ) return ; foreach ( var p in _pics) { p.Visible = false ; } int i = 0; foreach ( var c in _data) { _pics[i].Image = CardUI.GetResName(c.ID); _pics[i].Visible = true ; i++; } } } |
画面のほうからは DataSource プロパティでバインド。
1 | gainControl2.DataSource = _board.Player2.TakenCards; |
まあ、これで動くといえば動くのですが、花札の場合、タネとか短冊とかの枚数がわからないと次の手を打ちにくいので、これを「普通の花札ゲーム」のように4つに分けます。
■取り札のコントロールを作る(その2)
まずは、上記のように4つの置き場所を作ります。これにデータバンドをしたいわけですが、さて、それぞれのリストに対して DataSource プロパティを作るべきか?と悩んだのですが…やめました。ユーザーコントロールから設定する場合は、DataSource プロパティひとつに設定しておいて、内部的にリストに設定すれば OK なのですね。なんか、ListBox などのバインドイメージがあったので、List のコレクションをそのままバインドしないといけない気になっていた訳です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | public partial class TakenControl : UserControl { public TakenControl() { InitializeComponent(); // あらかじめ20,10,5,1札の場所を用意しておく this .pictureBox1.Visible = false ; this .pictureBox2.Visible = false ; this .pictureBox3.Visible = false ; this .pictureBox4.Visible = false ; _pics20 = InitPics( this .pictureBox1, 5); _pics10 = InitPics( this .pictureBox2, 10); _pics5 = InitPics( this .pictureBox3, 10); _pics1 = InitPics( this .pictureBox4, 20); } private List<PictureBox> InitPics(PictureBox ptemp, int num) { var pics = new List<PictureBox>(); ptemp.Visible = false ; int x = ptemp.Location.X; int y = ptemp.Location.Y; for ( int i = 0; i < num; i++) { var pic = new PictureBox(); pic.Size = ptemp.Size; pic.Location = new Point(x, y); pic.SizeMode = ptemp.SizeMode; pic.Visible = true ; pic.Image = CardUI.GetUra(); x += 20; pics.Add(pic); // this.Controls.Add(pic); } // 逆順で Controls に追加 pics.Reverse(); foreach ( var p in pics) { this .Controls.Add(p); } // 元に戻す pics.Reverse(); return pics; } protected List<PictureBox> _pics20, _pics10, _pics5, _pics1; protected List<Card> _data; public List<Card> DataSource { get { return _data; } set { _data = value; UIUpdate(); } } protected void UIUpdate() { if (_data == null ) return ; foreach ( var p in _pics20) { p.Visible = false ; } foreach ( var p in _pics10) { p.Visible = false ; } foreach ( var p in _pics5) { p.Visible = false ; } foreach ( var p in _pics1) { p.Visible = false ; } // 点数ごとに分けて表示する int i20 = 0; int i10 = 0; int i5 = 0; int i1 = 0; foreach ( var c in _data) { switch (c.Ten) { case 20: _pics20[i20].Image = CardUI.GetResName(c.ID); _pics20[i20].Visible = true ; i20++; break ; case 10: _pics10[i10].Image = CardUI.GetResName(c.ID); _pics10[i10].Visible = true ; i10++; break ; case 5: _pics5 [i5 ].Image = CardUI.GetResName(c.ID); _pics5 [i5 ].Visible = true ; i5++; break ; case 1: _pics1 [i1 ].Image = CardUI.GetResName(c.ID); _pics1 [i1 ].Visible = true ; i1++; break ; } } } } |
置き場所にあらかじめ PictureBox を作っておくのは同じで、画面を更新するための UIUpdate メソッドで DataSource プロパティの値(内部的には _data)のカードを振り分けます。こうすると、インターフェース的には、DataSource プロパティひとつでいける訳で、コードが簡単になる、ってことで。
これでずいぶん花札ゲームらしくなったということで。
お次は、「山のコントロール」と「手札や山から場に1枚だしたときのコントロール」。場に出したときのコントロールは、別のコントロールにしようと思ったのですが、場コントロールに含めてみた、という話を。