C# チェックボックスの表示順番について

タグの編集
投稿者 ケンケン  (社会人) 投稿日時 2022/8/10 11:17:40
C# 
  チェックボックス
  大阪
  東京
  名古屋
  福岡
  北海道

 すべてチェックをして表示すると
  (複数でも同じ)
  北海道
  福岡
  名古屋
  東京
  大阪
  の順で表示されます。

  ソースコードです。   

            foreach (Control item in groupBox1.Controls)
            {

                if (item.GetType().Equals(typeof(CheckBox)))
                {
                    // チェックボックスの場合
                    CheckBox obj = item as CheckBox;
                    if (obj.Checked)
                    {

                        // 選択されているなら表示させる。
                        chkitem.Add(obj.Tag.ToString() + " ");
                        Console.WriteLine("tag: " + obj.Tag.ToString() + " ");
                        chkitem.Sort();
                        foreach (var i in chkitem)
                        {
                            txttorikomi.Text += i.ToString();
                        }

                        chkitem.Clear();


                    }
                }
            }

チェックボックスの表示順番は、上からチェックされた順番になりませんか?

 何方か、わかる方ご教授お願い致します。





  
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/8/10 11:35:21
> foreach (Control item in groupBox1.Controls)
登録順に列挙されていませんか?

デザイン時に用意したコントロールならば、Form1.Designer.cs 内で、
 ~.Controls.Add(…);
あるいは
 ~.Controls.AddRange(…);
と記述されている箇所がありますよね。


> if (item.GetType().Equals(typeof(CheckBox)))
『foreach (var chk in groupBox1.Controls.OfType<CheckBox>())』にすれば、
if 文での絞り込みが不要になりますよ。


> CheckBox obj = item as CheckBox;
> if (obj.Checked)
これは誤り。item が CheckBox であることが確実である場合には
 CheckBox obj = (CheckBox)item;
 if (obj.Checked)
と書くべきです。

もしも as を使うのであれば、それが CheckBox とは限らない場面のことなので、
 CheckBox obj = item as CheckBox;
 if (obj != null && obj.Checked)
あるいは
 CheckBox obj = item as CheckBox;
 if (obj?.Checked ?? false)
と書かれるべきかと。


そして、もしも Checked 状態であるものだけを列挙したいのであれば、
『foreach (var chk in groupBox1.Controls.OfType<CheckBox>().Where(c => c.Checked))』
にすれば、そもそも if 文が不要になります。


> // 選択されているなら表示させる。
> chkitem.Add(obj.Tag.ToString() + " ");
> Console.WriteLine("tag: " + obj.Tag.ToString() + " ");
> chkitem.Sort();
> foreach (var i in chkitem) { … }
> chkitem.Clear();

ここの chkitem.Sort(); って意味ありますか?

「1 件 Add しては Clear する処理」を毎回繰り返しているので、
chkitem 内に複数件のデータが存在することはありえないように見えます。
投稿者 ケンケン  (社会人) 投稿日時 2022/8/10 12:38:40
素早い回答ありがとうございます。

            foreach (var chk in groupBox1.Controls.OfType<CheckBox>().Where(c => c.Checked))
            {
                Console.WriteLine("tag: " + chk.Tag.ToString() + " ");

                txttorikomi.Text += chk.Tag.ToString() + " ";
            }

 ご指摘の様に修正したしましたが、やはりだめでした。
 何か、ヒントがあればよろしくお願いいたします。


tag: 北海道 
tag: 福岡 
tag: 名古屋 
tag: 東京 
tag: 大阪 


投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/8/10 14:25:09
まず、「やりたいこと」は何でしょうか?

「~の順で表示されます。」とは書かれているのですが、当初の質問には
「どうなって欲しい」のかという点について記されていないように見受けられました。


そもそもあのコードは、Console への出力結果なら "tag:" と出力されるはずですし、
txttorikomi には空白区切りで出力されるべきコードであったはずです。
しかしながら実際の例示ではそのいずれでも無いため、状況を説明しきれていません。

また、chkitem が何であるのか、どういう意図であったのかという説明も放棄されたままです。


> チェックボックスの表示順番は、上からチェックされた順番になりませんか?
…ということで、そもそもの質問の意味を当方が読み取り切れていないのですが、
この発言は、Controls から列挙される順を問われているということでしょうか?

だとしたら先に述べた通り「登録順」に列挙される仕様です。
これは、画面に貼り付けた順ではありません。
また、「画面上のコントロールの位置(座標)」や
「ユーザーがチェックボックスを操作した順」とも無関係です。

たとえば、デザイン時の割り当てが下記のようになっていた場合、5,4,3,2,1 の順で列挙されます。

this.groupBox1.Controls.Add(this.checkBox5);
this.groupBox1.Controls.Add(this.checkBox4);
this.groupBox1.Controls.Add(this.checkBox3);
this.groupBox1.Controls.Add(this.checkBox2);
this.groupBox1.Controls.Add(this.checkBox1);


この時、デザイン画面で checkBox3 を右クリックして [最前面へ移動] を選択すると、
checkBox3 が最初に Add されるようになり、列挙時も最初に現れます。

後から追加された物(重ね合わせた時に、手前側に配置されるもの)が最初に列挙され、
先に追加された物(重ね合わせた時に、奥面側に配置されるもの)が後に列挙されますが、
最前面/最背面への移動を繰り返すことで、この順序を調整できます。


数が多くて、最前面/最背面だけでは面倒な場合には、
[表示]-[その他のウィンドウ]-[ドキュメント アウトライン]から
選択して、上部の[↑][↓]アイコンで並び替えることで、
Controls の列挙順を揃えられます。


もしも、Controls からの列挙順に関わらず【並び替えて表示したい】という意図であるのなら、
どの順で揃えたいのかを明確にしてみてください。
たとえば、
 🔹画面上のコントロールの位置(座標)
 🔹{Tab} キー押下時の並び順(TabIndex)
 🔹Name プロパティの昇順
 🔹Text プロパティの昇順
 🔹Tag プロパティの昇順
 🔹都道府県コード順
などなど、色々なパターンが考えられますよね。
https://nlftp.mlit.go.jp/ksj/gml/codelist/PrefCd.html


あるいは、【実際にチェックした順序】に並べたいのであれば、Controls から列挙することは
意味がありません。その場合は、列挙順を保持するためのコレクションを設けるべきです。

private List<CheckBox> prefectures = new List<CheckBox>();
private List<CheckBox> checkBoxes;
public Form1()
{
    InitializeComponent();
    checkBoxes = groupBox1.Controls.OfType<CheckBox>().ToList();
    checkBoxes.ForEach(c => c.CheckedChanged += (sende, e) =>
    {
        prefectures.Remove(c);
        if (c.Checked) { prefectures.Add(c); }
    });
}
private void button1_Click(object sender, EventArgs e)
{
    txttorikomi.Text = string.Join(" ", prefectures.Select(c => c.Text));
}
投稿者 魔界の仮面弁士  (社会人) 投稿日時 2022/8/10 14:44:24
> 列挙順を保持するためのコレクションを設けるべきです。
> private List<CheckBox> prefectures = new List<CheckBox>();

このサンプルは、チェックボックスが On/Off されるたびに、
prefectures の内容を書き換えるためのものでしたが……少し訂正しておきます。

先のサンプルだと、ロード直後の prefectures 変数は 0 件で始まりますので、
デザイン時にはすべての CheckBox を Checked = false にしておく必要がありました。
(最初からチェックされた状態であった場合、チェックしなおさないと txttorikomi に表示されなかった)


もし、デザイン段階で Checked = true 状態のチェックボックスを含める必要が
ある場合には、たとえばこのように変更してみてください。
※初期状態でチェック済みだった項目の並び順は、「Controls からの列挙順」に依存します。

private List<CheckBox> prefectures = new List<CheckBox>();
public Form1()
{
    InitializeComponent();
    groupBox1.Controls.OfType<CheckBox>().ToList().ForEach(c =>
    {
        if (c.Checked) { prefectures.Add(c); }
        c.CheckedChanged += delegate
        {
            prefectures.Remove(c);
            if (c.Checked) { prefectures.Add(c); }
        };
    });
}
private void button1_Click(object sender, EventArgs e)
{
    txttorikomi.Text = string.Join(" ", prefectures.Select(c => c.Text));
}
投稿者 ケンケン  (社会人) 投稿日時 2022/8/10 14:53:34

あるいは、【実際にチェックした順序】に並べたいのであれば、Controls から列挙することは
意味がありません。
   ↑グループ化していれば、チェックした順番に並ぶと思っていました。
  私の考え違いでした。 


その場合は、列挙順を保持するためのコレクションを設けるべきです。
 ↑
 もちょっと考えを巡らしていればよかったのですがその迄の指向に至りませんでした。
 (^^;)

結論から言いますと、
 上からチェックした順番に表示しれました。

 本当にありがとうございました。

因みにこのロジックです。
private List<CheckBox> prefectures = new List<CheckBox>();
private List<CheckBox> checkBoxes;
public Form1()
{
    InitializeComponent();
    checkBoxes = groupBox1.Controls.OfType<CheckBox>().ToList();
    checkBoxes.ForEach(c => c.CheckedChanged += (sende, e) =>
    {
        prefectures.Remove(c);
        if (c.Checked) { prefectures.Add(c); }
    });
}
private void button1_Click(object sender, EventArgs e)
{
    txttorikomi.Text = string.Join(" ", prefectures.Select(c => c.Text));
}