tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルDataGridViewのComboBoxColumnのコレクションを単一選択にしたい
記事No5474
投稿日: 2007/05/13(Sun) 23:28
投稿者area88
こんにちは、area88です。
またまた、ご質問です。

DataGridViewのComboBoxにコレクションがあるとします。
例:馬、牛、犬、猫、鹿
そこで、Row1に馬を選択。
つぎに、Row2に犬を選択。
そこで、Row3に馬を選択すると、Row1の馬を消去し、Row1を空欄にして、Row3に馬を表示。
つまり、あるColumnには、コレクションの内容が、一つしか選択できないようにしたいのです。

方法としては、コレクションのインデックスを取得して、同じインデックスのデータを検索し、
見つかると、消去するみたいな考え方かな、、、。
上記の考え方では、結構ややこしくなりますよね。もっとシンプルにいけそうですが、、、。

[ツリー表示へ]
タイトルRe: DataGridViewのComboBoxColumnのコレクションを単一選択にしたい
記事No5476
投稿日: 2007/05/14(Mon) 02:17
投稿者魔界の仮面弁士
> そこで、Row1に馬を選択。
> つぎに、Row2に犬を選択。
> そこで、Row3に馬を選択すると、Row1の馬を消去し、Row1を空欄にして、Row3に馬を表示。
> つまり、あるColumnには、コレクションの内容が、一つしか選択できないようにしたいのです。

もしそれが、
 馬を選択した時点で、既に馬が選択されていた行は、コンボを非選択状態(空欄)にする。
という仕様を意味しているなら、実装は比較的簡単かと思いますが、
 馬を選択した時点で、その行以外のコンボ項目から、馬を消去する。
 また、そこから馬以外を選択し直すと、他の行の項目にも馬が復活する。
という仕様を意味しているなら、やや面倒そうです。

どちらの仕様をお望みですか?

[ツリー表示へ]
タイトルRe^2: DataGridViewのComboBoxColumnのコレクションを単一選択にしたい
記事No5482
投稿日: 2007/05/14(Mon) 21:22
投稿者area88
こんにちは、area88です。

>馬を選択した時点で、既に馬が選択されていた行は、コンボを非選択状態(空欄)にする。
>という仕様を意味しているなら、実装は比較的簡単かと思いますが、
>馬を選択した時点で、その行以外のコンボ項目から、馬を消去する。

まさに、この仕様です。

もうちょっと詳しい例での説明です。
部署A、部署B、部署C、部署D、部署Eが各Cellにあり(たてに並んでいる)、
コンボボックスコレクションに、人材Xさん、Yさん、Zさんがいます。
で、部署AにXさんをコンボボックスから選択。
部署BにZさん、部署CにYさんを選択。
しかし、部署DをXさんに変更したくなった時、コンボボックスからXさんを選択する。
すると、部署AはXさんが部署Dに移ったので、空欄にする。
こんな感じで、コンボボックスで選択した内容が、重複しない状態を作りたいのです。

通常の実装なら、コンボボックスの内容は重複して選択できますね。
(例:部署AからEまで、全部Yさんみたいに。)
しかし、Yさんは一人しかいませんから、部署を異動すると、前の部署にはいなくなる。
このような考え方で、コンボボックスを使いたいのです。

よろしくお願いいたします。

[ツリー表示へ]
タイトルRe^3: DataGridViewのComboBoxColumnのコレクションを単一選択にしたい
記事No5483
投稿日: 2007/05/15(Tue) 02:56
投稿者魔界の仮面弁士
> >馬を選択した時点で、既に馬が選択されていた行は、コンボを非選択状態(空欄)にする。
> >という仕様を意味しているなら、実装は比較的簡単かと思いますが、
> >馬を選択した時点で、その行以外のコンボ項目から、馬を消去する。
> まさに、この仕様です。

どちらの仕様ですか?
コンボボックスの項目を取り除くのと(後者)、
すでに選択済みの項目を非選択に戻す(前者)のは別仕様です。

前者は、選択状態が変化するだけで、項目数は変化しませんが、
後者は、選択状態だけでなく、セルごとに項目数の増減を行わねばなりませんよね。


> しかし、Yさんは一人しかいませんから、部署を異動すると、前の部署にはいなくなる。
兼任は?

[ツリー表示へ]
タイトルRe^4: DataGridViewのComboBoxColumnのコレクションを単一選択にしたい
記事No5484
投稿日: 2007/05/15(Tue) 08:50
投稿者area88
こんにちは、area88です。

>すでに選択済みの項目を非選択に戻す(前者)
>前者は、選択状態が変化するだけで、項目数は変化しません

私の説明不足で申し訳ありません。<(_ _)>ペコリ
(前者)の仕様です。
項目数の変化はありません。
あと、兼任などもありません。
前スレの例を用いれば、部署一つに、一人の配属です。
よろしくお願いいたします。

[ツリー表示へ]
タイトルRe^5: DataGridViewのComboBoxColumnのコレクションを単一選択にしたい
記事No5485
投稿日: 2007/05/15(Tue) 13:08
投稿者魔界の仮面弁士
となると、CellValueChanged イベントが妥当でしょう。
e.ColumnIndex と e.RowIndex から、選択された値を取得して、
その値が他の行で既に使われていたら、その行の値をクリアする、と。

たとえば、こんな感じ。
--------------
Public Class Form1

  '実験用のダミーデータ生成。このあたりは適当に。
  Private Function CreateComboItem() As String()
    Dim list As New List(Of String)
    list.Add("")
    list.Add("Visual Basic")
    list.Add("C#")
    list.Add("C++/CLI")
    list.Add("JScript")
    list.Add("Delphi")
    Return list.ToArray()
  End Function

  '説明の都合上、今回はコントロール生成も Form1_Load で行っていますが、
  '普通は、デザイン時に設定しておくだけで十分です。
  Private WithEvents dataGridView1 As New DataGridView()
  Private comboColumn1 As DataGridViewComboBoxColumn
  Private items() As String

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs _
  ) Handles MyBase.Load
    'コンボボックス列の設定
    comboColumn1 = New DataGridViewComboBoxColumn()
    comboColumn1.DataSource = CreateComboItem()

    'グリッドの設定
    dataGridView1.EditMode = DataGridViewEditMode.EditOnEnter
    dataGridView1.Dock = DockStyle.Fill
    dataGridView1.ColumnHeadersVisible = False
    dataGridView1.RowHeadersVisible = False
    dataGridView1.AllowUserToResizeRows = False
    dataGridView1.AllowUserToResizeColumns = False
    dataGridView1.AllowUserToAddRows = False
    dataGridView1.DefaultCellStyle.SelectionBackColor = _
             dataGridView1.DefaultCellStyle.BackColor
    dataGridView1.DefaultCellStyle.SelectionForeColor = _
             dataGridView1.DefaultCellStyle.ForeColor
    dataGridView1.RowCount = 5
    dataGridView1.ColumnCount = 1
    dataGridView1.Columns.Add(comboColumn1)
    Controls.Add(dataGridView1)

    'おまけ
    Dim combo As New DataGridViewComboBoxCell()
    combo.Items.AddRange("犬 猿 雉".Split())
    dataGridView1(0, 0) = combo
    dataGridView1(0, 0).Value = "猿"
    dataGridView1(0, 1).Style.BackColor = Color.Gold
    dataGridView1(0, 1).Style.ForeColor = Color.Red
    dataGridView1(0, 1).Value = "テキスト"
    dataGridView1(0, 2) = New DataGridViewButtonCell()
    dataGridView1(0, 2).Value = "ボタン"
    dataGridView1(0, 3) = New DataGridViewImageCell(False)
    dataGridView1(0, 3).Value = Me.Icon
    dataGridView1(0, 4) = New DataGridViewCheckBoxCell()
    dataGridView1(0, 4).Style.Alignment = DataGridViewContentAlignment.MiddleCenter
    dataGridView1(0, 4).Value = True
  End Sub

  'ここから本題。
  Private Sub dataGridView1_CellValueChanged(ByVal sender As Object, _
  ByVal e As DataGridViewCellEventArgs) Handles dataGridView1.CellValueChanged
    If e.ColumnIndex <> comboColumn1.Index Then
      Return
    End If

    '選択されたコンボ項目を取得
    Dim dgv As DataGridView = DirectCast(sender, DataGridView)
    Dim value As String = dgv(e.ColumnIndex, e.RowIndex).Value.ToString()

    If value = "" Then
      '未選択状態の時は、特に何もしない
      Return
    End If

    '他の行の状態を確認
    For rowIndex As Integer = 0 To dgv.RowCount - 1
      If rowIndex = e.RowIndex Then
        '自分は除外
        Continue For
      End If
      Dim cell As DataGridViewCell = dgv(e.ColumnIndex, rowIndex)
      If Object.Equals(cell.Value, value) Then
        '同じ値を持つ行があれば、未選択に戻す
        cell.Value = ""
        Exit For
      End If
    Next
  End Sub
End Class
----------

この場合、重複した他のセルの値がクリアされるのは、コンボボックスから
選択したときではなく、選択後にそのセルからフォーカスが抜けたときです。

もしセルを移動させることなく、項目が選択されるたびに、即座に他の行の値を
クリアしたいのであれば、CurrentCellDirtyStateChanged イベントでコミットすれば OK です。

Private Sub dataGridView1_CurrentCellDirtyStateChanged(……
  If dataGridView1.CurrentCell.ColumnIndex = comboColumn1.Index Then
    dataGridView1.CommitEdit(DataGridViewDataErrorContexts.Commit)
  End If
End Sub

ただ、即座にコミットすると見た目はわかりやすいのですが、それだと困ることもあります。
ドロップダウンからのクリックで選択した場合であれば良いのですが、マウスホイールや
矢印キーで選択した場合には、そのたびに他の行の値が次々とクリアされる結果になるので。

[ツリー表示へ]
タイトルRe^6: DataGridViewのComboBoxColumnのコレクションを単一選択にしたい
記事No5487
投稿日: 2007/05/15(Tue) 22:37
投稿者area88
こんにちは、area88です。

キター!
動きました!!!(^_^)v

しかし、2時間ばかりかかりました。

>Dim value As String = dgv(e.ColumnIndex, e.RowIndex).Value.ToString()

ここが、コンパイルエラーでどうしても前に進まなかったのですが、

Dim value As Object = dgv(e.ColumnIndex, e.RowIndex).Value

で通りました。

そうそう、CombpBoxの内容が、ListBoxから引っ張っていたので、Stringでは通してくれなかったんです。

別スレで書いたのですが、
>Dim z As Object = Column16
>z.Items.clear()
>For Each obj As Object In ListBox1.Items
>     z.Items.add(obj)
>Next
というコードで、ComboBoxの内容を登録していたので、Objectでないとダメ!とコンパイラー
から文句が出たわけです。

魔界の仮面弁士さん、まことにありがとうございました。
今後とも、よろしくお願いいたします。<(_ _)>ペコリ

P.S 悩んだ末のプログラミングが動くと、アハ体験に近いものがありますね。

[ツリー表示へ]
タイトルRe^7: DataGridViewのComboBox...
記事No5488
投稿日: 2007/05/15(Tue) 22:52
投稿者魔界の仮面弁士
> >Dim value As String = dgv(e.ColumnIndex, e.RowIndex).Value.ToString()
> ここが、コンパイルエラーでどうしても前に進まなかったのですが、

あれ? 実行時のエラーではなく、コンパイル時のエラーですか。

手元の環境で再現できなかったので、念のために確認しておきたいのですが、
 Dim value As String = dgv(e.ColumnIndex, e.RowIndex).Value
ではなく、
 Dim value As String = dgv(e.ColumnIndex, e.RowIndex).Value.ToString()
のコードで発生した、ということなのですね?
できれば、正確なエラーの内容を教えていただけないでしょうか。


> Dim value As Object = dgv(e.ColumnIndex, e.RowIndex).Value
> で通りました。
なるほど。確かに無理に元の型に戻さずとも、Object型のままで十分ですね。

ところで…その行の実行後に、
  If value IsNot Nothing Then
     MessageBox.Show( value.GetType().FullName )
     MessageBox.Show( value.ToString() )
  Else
     MessageBox.Show( "Nothing" )
  End If
を試すと、どのような型名が表示されるのでしょうか?

[ツリー表示へ]
タイトルRe^8: DataGridViewのComboBox...
記事No5506
投稿日: 2007/05/16(Wed) 22:01
投稿者area88
こんにちは、area88です。

>あれ? 実行時のエラーではなく、コンパイル時のエラーですか。
>Dim value As String = dgv(e.ColumnIndex, e.RowIndex).Value.ToString()

私もあれ?今回試してみると、実行エラーで、
「オブジェクト参照がオブジェクト インスタンスに設定されていません。」
とでました。
ただ、魔界の仮面弁士さんコード以外に、いろいろと他の部分(私のコードなど)変更を加えたので、それで、コンパイルが通ったのかな?
昨日、仮面弁士さんのコードをコピーし、私の仕様(例:DataGridview3など)に
変更しただけの時は、確か、、、Form1がうんぬんかんうんだったような、、、。
とにかく、コンパイルのときに停止して、実行ともう一つ何か選択ボタンが出て、
どちらをクリックしても、終了してしまいました。

>Dim value As String = dgv(e.ColumnIndex, e.RowIndex).Value
これは、実行時エラーでないですね。問題なしです。

>If value IsNot Nothing Then
>     MessageBox.Show( value.GetType().FullName )
>     MessageBox.Show( value.ToString() )
>  Else
>     MessageBox.Show( "Nothing" )
>  End If
>を試すと、どのような型名が表示されるのでしょうか

「System,String」というメッセージボックスが出て、実行時エラーで止まります。
この実行時エラーは、何か文句を言われるのではなく、コンボボックスの内容が表示されません。

どうやら、今回は、Dim value As String = dgv(e.ColumnIndex, e.RowIndex).Value
でもOKなようで、、、。
私、VB2005を始めて、たった3週間で、リファレンスやら入門書をあっちこっちひっくり返して
アプリを作成しております。
実際、ある程度動きますが、もっとすっきりしたコードになるんでないかと思いながら
悪戦苦闘しております。
今回、Dim dgv As DataGridView = DirectCast(sender, DataGridView)
      Dim value As String = dgv(e.ColumnIndex, e.RowIndex).Value.ToString()を

   Dim value As Object = DataGridView3(e.ColumnIndex, e.RowIndex).Value
に書き換えたら、うまく動いたので、こんなものなのかなぁ、、、と思っているしだいです。
私の他のコードがなけれが、仮面弁士さんのコード、そのままつかえるのかなぁと思います。

[ツリー表示へ]