トップ回答者
DataGridViewのDropDownListに矢印キーで移動

質問
-
おはようございます。
VB2005+SQLServerExpressを使用しております。
DataGridViewのComboboxにおいて、直接入力がしたいと思い、EditingControlShowingイベントにてDropDownStyleを変更して実現しております。
こちらでデータを入力した場合、前方一致の形で該当するデータまで移動をしてくれ、リストが表示されるのですが、矢印キーでそのリストに移動することができません。
ヘルプなどを何度も呼んでいるのですが、どうしてもそのような項目が見当たりません。
何か実現する方法があるのでしょうか。どうか、アドバイスお願いします。
回答
-
ようやく時間が取れたの検証してみました。
TABキーおよびENTERキーはまずComboBoxが受け取ります。つまり、DataGridViewComboBoxEditingControlが受け取ることになります。この時、TABキーとENTERキーではDataGridViewComboBoxEditingControlにおける動作が異なります。TABキーの場合ですと項目の選択(SelectedIndexなどのセット)をするところまで行ってくれるようですが、ENTERキーの場合はそれを行わず、そのままDropDownListを閉じてしまうようです。ですから、DataGridViewComboBoxEditingControlにおいて、ENTERキーでもSelectedIndexをセットするようにすれば解決しそうです。
>ところで、前回の返信の中にあった「鉛筆マーク」の確認はどのような意味があるのでしょうか。
編集中という意味です。DataRowのBeginEditメソッドが実行された状態です。バインド系のコントロールにはこのメソッドが必要です。バインドを行うコントロールではデータ項目をいくつか変更し、<del>その変更結果をまとめてデータソースに返す必要があるからです。例えば3つの列がある場合、1つの列を変更する度にデータソースに書き戻していたのでは効率が悪いですし</del>、エラーチェックでも不具合が生じます。3つの列を入力し終わった時点でエラーチェックをして欲しいからです。そうしなければ、例えば3列目はnullを禁止にしていても、1列目を入力し終わった時点でDataRowのエラーチェックが走ってしまい、3列目はnullじゃダメとしかられても、「おいおいちょっと待ってよ、まだ全部入力してないんだからね」っていうことです。
編集中を終わるにはEndEditメソッドを実行し、<del>この時点でデータソースのDataRowへまとめて書き戻されます。</del>
BeginEditメソッド、EndEditメソッドはバインド系のコントロールが自動的に呼び出します。
つまり、鉛筆マークになっていない状態ではいくらComboBoxをいじっても、データソースへその値が書き戻されないということを言いたかったのです。さて、話を元に戻して、TI-cb400さんのコードに少し手を加えてみました。標準のDataGridViewComboBoxEditingControlを使わずに、それを継承したExDataGridViewComboBoxEditingControlでENTERキーを受け取ったらSelectedIndexをセットするようにしてます。このクラスを使うために、DataGridViewComboBoxColumnを継承したExDataGridViewComboBoxColumnクラスも定義しています。一度コンパイルするとこのExDataGridViewComboBoxColumnがデザイナで選択できるようになりますので、デザイナで選択して下さい。
また、以下のコードにおいて、
cb.EditingControlDataGridView.NotifyCurrentCellDirty(True)
という記述がありますが、これは強制的に鉛筆モードにするための記述です。理由は上で説明した通りです。Imports System.Data.SqlClient Public Class DataGridViewComboBox3 Private DT As DataTable Private Sub SetCombobox() DT = New DataTable Using Cn As New SqlConnection(My.MySettings.Default.TESTConnectionString) Using Cmd As New SqlCommand With Cmd .Connection = Cn .CommandType = CommandType.StoredProcedure .CommandText = "SelectTEST" '.Parameters.AddWithValue("@firstkana", "") '.Parameters.AddWithValue("@lastkana", "") End With Using DA As New SqlDataAdapter DA.SelectCommand = Cmd DA.Fill(DT) End Using Dim clm As DataGridViewComboBoxColumn = Me.CDataGridView1.Columns("clmEmployee") With clm .DataSource = DT '.DisplayMember = "ptname" '.ValueMember = "pt_ptcode" .DisplayMember = "TEST1" .ValueMember = "ID" End With End Using End Using End Sub Private Sub SetDateFormat() '日付の書式設定 'Dim Wareki As New clsDGVWarekiTextBoxCell 'Dim clm As DataGridViewColumn = Me.CDataGridView1.Columns("clmDate") 'clm.CellTemplate = Wareki 'Headerの文字位置設定 Me.CDataGridView1.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter For ColumnIndex As Integer = 0 To Me.CDataGridView1.ColumnCount - 1 Me.CDataGridView1.Columns(ColumnIndex).SortMode = DataGridViewColumnSortMode.NotSortable Next End Sub Private Sub frmTest_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load SetCombobox() SetDateFormat() End Sub End Class Public Class CDataGridView Inherits System.Windows.Forms.DataGridView Private Sub CDataGridView_CellEnter(ByVal sender As Object, _ ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles Me.CellEnter Dim dgv As CDataGridView = CType(sender, CDataGridView) '編集コントロールがコンボボックスの場合 If TypeOf dgv.Columns(e.ColumnIndex) Is DataGridViewComboBoxColumn Then SendKeys.Send("{F4}") '編集コントロールがテキストボックスの場合 ElseIf TypeOf dgv.Columns(e.ColumnIndex) Is DataGridViewTextBoxColumn Then SendKeys.Send("{F2}") End If End Sub '********************************************************************************************** 'Enterキーで移動 '********************************************************************************************** <System.Security.Permissions.UIPermission( _ System.Security.Permissions.SecurityAction.LinkDemand, _ Window:=System.Security.Permissions.UIPermissionWindow.AllWindows)> _ Protected Overrides Function ProcessDialogKey( _ ByVal keyData As Keys) As Boolean System.Diagnostics.Debug.WriteLine("ProcessDialogKey") If (keyData And Keys.KeyCode) = Keys.Enter Then Return Me.ProcessTabKey(keyData) ElseIf (keyData And Keys.KeyCode) = Keys.Delete Then If MessageBox.Show("選択している行を削除します。" & vbCr & "よろしいですか" _ , "削除確認", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) = DialogResult.OK Then DeleteRows() End If End If Return MyBase.ProcessDialogKey(keyData) End Function <System.Security.Permissions.SecurityPermission( _ System.Security.Permissions.SecurityAction.LinkDemand, Flags:= _ System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)> _ Protected Overrides Function ProcessDataGridViewKey(ByVal e As System.Windows.Forms.KeyEventArgs) As Boolean System.Diagnostics.Debug.WriteLine("ProcessDataGridViewKey") If e.KeyCode = Keys.Enter Then Return Me.ProcessRightKey(e.KeyData) End If Return MyBase.ProcessDataGridViewKey(e) End Function '********************************************************************************************** '選択されている行を削除 '********************************************************************************************** Private Sub DeleteRows() Dim r As DataGridViewRow For Each r In Me.SelectedRows Me.Rows.Remove(r) Next End Sub '********************************************************************************************** 'Comboboxに直接入力できるようにDropDownStyleをDropDownに変更 '********************************************************************************************** Private Sub CDataGridView_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles Me.EditingControlShowing If TypeOf e.Control Is DataGridViewComboBoxEditingControl Then Dim cb As DataGridViewComboBoxEditingControl = CType(e.Control, DataGridViewComboBoxEditingControl) 'cb.DropDownStyle = ComboBoxStyle.DropDown //既定のスタイルなのでセットしなくても良い '必ず編集モードにする。 cb.EditingControlDataGridView.NotifyCurrentCellDirty(True) End If End Sub End Class Public Class ExDataGridViewComboBoxColumn Inherits System.Windows.Forms.DataGridViewComboBoxColumn Public Sub New() MyBase.New() Me.CellTemplate = New ExDataGridViewComboBoxCell End Sub End Class Public Class ExDataGridViewComboBoxCell Inherits System.Windows.Forms.DataGridViewComboBoxCell Public Overrides ReadOnly Property EditType() As System.Type Get Return GetType(ExDataGridViewComboBoxEditingControl) End Get End Property End Class Public Class ExDataGridViewComboBoxEditingControl Inherits System.Windows.Forms.DataGridViewComboBoxEditingControl Protected Overrides Function IsInputKey(ByVal keyData As Keys) As Boolean Select Case keyData Case Keys.Enter Me.SelectedIndex = Me.FindStringExact(Me.Text) Return False Case Else Return MyBase.IsInputKey(keyData) End Select End Function End Class
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2011年4月30日 12:37 編集するとコードが崩れるので再掲載
- 回答としてマーク TI-cb400 2011年5月2日 9:18
すべての返信
-
おはようございます。
追加情報です。
ComboboxのDropDownStyleは「DropDown」にしてあります。
現状の動作として
・Comboboxにフォーカスが移動すると、DropDownListが表示される
・Listが表示された最初の状態では、矢印キーでListを移動でき、Enterキーでデータが確定される
となっています。しかし、ここでComboboxに文字を入力すると、その文字を含む先頭行までListが移動してくれます。
この状態でも、矢印キーでデータは移動できているのですが、選択されているであろう行が青く反転されません。また、該当の行が見つかりEnterキーを押してもその段階では選択したデータが表示されているのですが、
フォーカスが移動すると別のデータになってしまいます。ただ、上記の操作をしてマウスで該当のデータをクリックすると、フォーカスを移動しても選択したデータのままと
なっています。SelectedValueが変更されていないためと考えられるのですが、DisplayMemberを変更して、SelectedValueが
一緒に変更にならない理由がわかりません。どうか、アドバイスお願いいたします。
-
おはようございます。
情報があまりにも不足していることに気がつきましたので、追加の記載です。
DataGridViewのComboboxはDataTableをDataSourceとしてセットしてあります。
いろいろと考えていたのですが、流れとしては以下の形になるかと考えます。
・Comboboxに文字入力
・候補の先頭にComboboxのデータが移動
・データを選択データの選択のところで、ComboboxのDisplayMemberであるデータはCombobox上に
表示されているのですが、ValueMemberがセットされていないようでした。そこで、
・Comboboxに現在表示されているデータを取得
・上記の値を元にDataTableから該当の行を検索
・検索により取得した行からComboboxのValueMemberにセットしている値を取得
・ComboboxのSelectedValueに上記の値をセットで実現可能と思われるのですが、流れとしてはいかがでしょうか。
それとも、もっと簡単に実現可能な方法がありますでしょうか。
-
こんにちは。
DataGridViewのComboboxを更新したときの表示されているテキストを取得し、DataTable内を検索するキーとするため、
以下のように記述をして見ました。しかし、この状態ではテキストが取得できません。
ヘルプやインターネットでいろいろと調べてみているのですが、
Private Sub DataGridView_CellValidating(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellValidatingEventArgs) Handles dgvAzukarikin.CellValidating
Dim dgv As DataGridView = CType(sender, DataGridView)'
If TypeOf dgv.Columns(e.ColumnIndex) Is DataGridViewComboBoxColumn Then
Dim str As String = dgv.CurrentCell.FormattedValue
Dim foundRow() As DataRowfoundRow = retDT.Select("name = '" & str & "'")
Me.dgvAzukarikin.Item(e.ColumnIndex, e.RowIndex).Value = foundRow("name")
End If
End SubDataGridViewのComboboxの表示されているデータを取得するにはどのようにすればよいのでしょうか。
どうか、アドバイスお願いします。
-
ComboBoxを取得するには、DataGridViewのEditingControlShowingイベントを利用します。その引数であるDataGridViewEditingControlShowingEventArgsのControlプロパティをキャストすることにより、当該のComboBoxを取得することができます。ComboBoxが取得できれば、そのTextプロパティで表示されているデータが取得できるはずです。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/ -
ご回答ありがとうございます。
以下のように記述をして見ました。
Private EditingControlCombobox As DataGridViewComboBoxEditingControl
Private Sub dgv_CellValidating(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellValidatingEventArgs) Handles dgv.CellValidating
Dim dgv As DataGridView = CType(sender, DataGridView)'
If TypeOf dgv.Columns(e.ColumnIndex) Is DataGridViewComboBoxColumn Then
Dim SearchName As String = ""
Dim foundRow() As DataRowSearchName = EditingControlCombobox.Text
If SearchName <> "" Then
foundRow = DT.Select("name = '" & SearchName & "'") → ここEditingControlCombobox.SelectedValue = foundRow(0)
End If
End If
End Sub
Private Sub dgv_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles dgv.EditingControlShowing
If TypeOf e.Control Is DataGridViewComboBoxEditingControl Then
Dim dgv As DataGridView = CType(sender, DataGridView)EditingControlCombobox = CType(e.Control, DataGridViewComboBoxEditingControl)
End If
End SubDataGridViewのComboboxにはDataTableがバインドされており、常時データが300件程度あるため、comboboxに直接データを入力し
頭だしをしたうえで選択をしたいと考えております。また、以下のコードでセルに何も入力しない場合は、1行前に同じ行のデータを転記するようにしております。
Private Sub dgvAzukarikin_CellLeave(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles dgvAzukarikin.CellLeave
If e.RowIndex >= 1 ThenMe.dgv.CurrentCell.Value = Me.dgv(e.ColumnIndex, e.RowIndex - 1).Value
End If
End Sub上記の内容が原因ではないと思うのですが、新しい行に移動して、Comboboxから選択をすると、1行前のComboboxのデータが表示されてしまいます。
EditingControlShowingで取得したComboboxがそのままになっていることが原因かと思い、DataGridViewのCellLeaveイベントをComboboxを取得するために
用意した変数をNothingにしたところ、今度はデータ自体が取得できなくなってしまいました。原因を調べているところですが、まだわかっていません。
よろしければアドバイスをお願いします。
-
DataGridView全体でDataGridViewComboBoxEditingControlのインスタンスは一つしかありません。ComboBox列をクリックしたタイミングでそのComboBoxのインスタンスが場所を変えて表示されます。EditingControlShowingは実際にComboBoxが表示される前に発生しますので、一つ前のComboBoxの状態を取得することになります。
DataGridViewComboBoxEditingControlにKeyPressイベントハンドラを設定し、そこで頭出しをされてみてはいかがでしょうか?(参考)
DataGridViewでセルが編集中の時にキーイベントを捕捉する
http://dobon.net/vb/dotnet/datagridview/textboxevent.html(追記)
TextChangedイベント辺りがいいかもしれませんね。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/- 編集済み trapemiyaModerator 2011年4月17日 13:07 追記
-
理解が悪くて大変申し訳ないのですが、DataGridViewにKeyPressイベントは見つかったのですが、TextChangedイベントがみつからないのですが、
こちらはどこにあるのでしょうか。DataGridViewではなくComboBoxのTextChangedイベントのことを言っています。繰り返しになりますが、DataGridViewでComboBoxを操作している時は、DataGridViewの上にComboBoxが現れて、それを操作しているイメージになります。
また、EditingControlShowingは1つ前のComboboxの状態を取得するとのことですが、なぜ、上記の参考URLでは現在編集中の内容を取得する
ことになるのでしょうか。こちらも繰り返しになりますが、EditingControlShowingイベントで得られるComboBoxは常に1つのインスタンスです。上記の参考URLではそのComboBoxに対してKeyPressイベントハンドラを設定しているだけで、ComboBoxの値を取得しているわけではありません。この後、ComboBoxに値を入力することによりKeyPressイベントが発生し、そのイベントハンドラで入力された値を取得しています。つまり、ComboBoxから値を取得するのはComboBoxが表示され、ユーザーが入力した後になります。一方、EditingControlShowingイベントはComboBoxが表示される前に発生します。ComboBoxのインスタンスは1つで使いまわしていますから、そのインスタンスから値を取得すると、1つ前のユーザーが入力した値になるわけです。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/ -
ご回答が遅くなりました。
DataGridViewのComboboxのTextを取得し、ComboboxのValueMemberの値を取得するところまでできました。
しかし、そのあたいをComboboxに指定したいのですが、セルを移動するとComboboxが空白になってしまいます。
(取得したEditingControlのSelectedValueにセットすればできそうなのですが)以下、コードです。
Private Sub dgv_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles dgvAzukarikin.EditingControlShowing
If TypeOf e.Control Is DataGridViewComboBoxEditingControl Then
' Dim dgv As DataGridView = CType(sender, DataGridView)Me.dataGridViewCombobox = CType(e.Control, DataGridViewComboBoxEditingControl)
'TextChangedイベントハンドラを追加
AddHandler Me.dataGridViewCombobox.TextChanged, AddressOf dataGridViewCombobox_TextChanged
End If
End SubPrivate Sub dataGridViewCombobox_TextChanged(ByVal sender As Object, ByVal e As EventArgs)
'Dim cb As DataGridViewComboBoxEditingControl = CType(sender, DataGridViewComboBoxEditingControl)
dataGridViewCombobox = CType(sender, DataGridViewComboBoxEditingControl)_Name = dataGridViewCombobox.Text
If InStr(_Name, " ") <> 0 AndAlso _Name <> "" Then
Dim foundRow() As DataRowfoundRow = DT.Select("name = '" & _Name & "'")
dataGridViewCombobox.SelectedValue = foundRow(0) → ここ
End If
End Sub上記のfoundRowに表示中のデータを含むDataTableの行を取得していることまではデバッグで確認しております。
矢印の部分で値をセットできると思ったのですが、デバッグで確認するとエラーにはならないのですが、矢印の部分のdataGridViewComboboxが
Nothingになっています。値のセットの仕方が悪いのでしょうか。
Comboboxのヘルプなどを読み直しているところですが、どうにも理由がわかりません。
-
dataGridViewCombobox.SelectedValue = foundRow(0) → ここ
SelectedValueにDataRowをセットすることは通常ありえませんので、foundRow(0)(0)のような形になるはずです。
また、「dataGridViewComboboxがNothingになっています。」と書かれていますが、上記の理由により、dataGridComboboxのSelectedValueがNothingということではないでしょうか?
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/ -
ご回答ありがとうございます。
以前作成したコードを何の考えもなしにコピーしていたので、まったく気づいていませんでした。
本当に基本的なことを失念しておりました。ご教授いただいたおかげでようやく、形になってきました。
しかし、最後に1点疑問があります。
現在、DataGridViewはカスタムコントロール(この説明でよいかわからないのですが)として使用しています。
具体的にはEnterキーを押すことでTabキー同様次のセルへ移動できるようにあらかじめしております。現在のDataGridViewにおいて、Tabキーで該当のComboboxから次のセルへ移動すると、データがComboboxに
表示されるのですが、Enterキーを押した場合では、最初のときと同様、Comboboxが空白になってしまいます。はじめは、Enterキーを押したときにTabキーを押したときと同じ動作をさせればよいと思ったのですが、もともと使用している
DataGridViewにはそのコードを記述してあり、なぜ、このような動作になってしまうか、皆目見当がつきません。改めて、DataGridViewを配置しているフォームにも、同じコードを書いて試してみようと思うのですが、同じことを2度やるのは
どう考えてもおかしいので、悩んでおります。何か、原因があるものなのでしょうか。
もう少し、調べてみたいと思います。 -
おはようございます。
経過報告です。(まだ、思い通りには動いていません。)
Enterキーを押した際に、TextChangedイベントよりも先に、DataGridViewのEnterキーを押すことによりTabキーと同じ動作をするコードが
先に走っているかとおもい、デバッグにて確認をしたところ、先にTextChangedイベントが動作し、DataGridViewComboboxに値をセットしてから
DataGridViewのEnterキーを押した際のイベントに移動しておりました。以下、DataGridViewのカスタムコントロールの該当部分のコードです。
'**********************************************************************************************
'Enterキーで移動
'**********************************************************************************************
<System.Security.Permissions.UIPermission( _
System.Security.Permissions.SecurityAction.LinkDemand, _
Window:=System.Security.Permissions.UIPermissionWindow.AllWindows)> _
Protected Overrides Function ProcessDialogKey( _
ByVal keyData As Keys) As BooleanIf (keyData And Keys.KeyCode) = Keys.Enter Then
Return Me.ProcessTabKey(keyData)
ElseIf (keyData And Keys.KeyCode) = Keys.Delete Then
If MessageBox.Show("選択している行を削除します。" & vbCr & "よろしいですか" _
, "削除確認", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) = DialogResult.OK Then
DeleteRows()
End If
End IfReturn MyBase.ProcessDialogKey(keyData)
End Functionまた、余談ではありますが、Enterキーを押した際にTabキーと同じ動作をするはずなので、右隣のセルに移動するはずなのですが、なぜか
下のセルに移動してしまうことがあります。
こちらも、原因がよくわかっていません。Tabキーを用いて、移動をすれば目的の動作は実現できてはいるのですが、記述したコードどおりにしか、プログラムは動かないといわれるように
どうにも、現状はすっきりとしないので、何とか解決をしたいと思っております。又、経過を報告しますので、何かお気づきの方、ぜひともアドバイスお願いします。
-
なかなかわかりずらい問題のようですが、Enterキーでの移動に関する処理に問題がありそうということですね?
その処理にProcessDialogKeyメソッドを使用されていますが、これはDataGridViewがEditモードの場合に使用できます。Editモードでない時はProcessDataGridViewKeyメソッドを使用することになりますが、この辺りの使い分けは大丈夫でしょうか? 例えばReadOnlyのカラムの場合はEditできませんので、ProcessDataGridViewKeyメソッドを使う必要があるのではないかと思います。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/ -
ご回答ありがとうございます。
使い分けの件に関してはまったく知りませんでした。
また、DataGridViewのEnterきーの挙動を知るためにヘルプを見たところ、どうもいろいろな挙動が混ざってしまっているような印象です。
まず、編集モードの件ですが、常にEditモードになっているはずです。
というのは、使用しているカスタムコントロールのDataGridViewに以下のコードを記述しております。Private Sub CDataGridView_CellEnter(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles Me.CellEnter
Dim dgv As CDataGridView = CType(sender, CDataGridView)'編集コントロールがコンボボックスの場合
If TypeOf dgv.Columns(e.ColumnIndex) Is DataGridViewComboBoxColumn Then
SendKeys.Send("{F4}")'編集コントロールがテキストボックスの場合
ElseIf TypeOf dgv.Columns(e.ColumnIndex) Is DataGridViewTextBoxColumn Then
SendKeys.Send("{F2}")
End If
End Subしかし、いろいろとテストをしてみると、時々編集モードにならない場合(セル全体が青く反転し、テキストボックスなどの
先頭にカーソルが来ていない状態)があり、そのような場合にEnterキーを押すと下のセルに移動するようです。
(ヘルプを見ましたが、これがDataGridViewにおける、Enterキーの標準的な動作なのですね)ProcessDataGridViewKeyメソッドをためして、また報告をしたいと思います。
-
Enterキーで移動した際に表示が消えてしまう場合、DataGridViewの行ヘッダはえんぴつマークが出ているのでしょうか? また、Enterキーによる横移動を止めた時には表示が消えずに正しく入力されるのでしょうか? 私の方でも簡単なテストコードを書いて試していますが、問題を把握しきれていないようです。可能であれば、再現する最低限のコードを目指してテスト的なものを作られてみてはいかがでしょうか? そして、再現できればそのコードを示していただくと、いろいろな方の具体的なアドバイスがいただけるのではないかと思います。もちろん私も力の及ぶ限り拝見させていただきます。また、再現する最低限のコードを構築する過程で、何かに気付かれる可能性もあると思います。
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/ -
ご回答ありがとうございました。
鉛筆マークの件ですが、Comboboxに直接入力をしている間は黒い三角マークになり、直接入力をせず、矢印キーでリストから選択する場合は
鉛筆マークのままになっています。また、テストで同じようなものを作成してみたのですが、結果は同じでした。
以下に、テストで作成したコードです。
デザイナ
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()> _
Partial Class frmTest
Inherits System.Windows.Forms.Form'フォームがコンポーネントの一覧をクリーンアップするために dispose をオーバーライドします。
<System.Diagnostics.DebuggerNonUserCode()> _
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
MyBase.Dispose(disposing)
End Sub'Windows フォーム デザイナで必要です。
Private components As System.ComponentModel.IContainer'メモ: 以下のプロシージャは Windows フォーム デザイナで必要です。
'Windows フォーム デザイナを使用して変更できます。
'コード エディタを使って変更しないでください。
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
Me.CDataGridView1 = New HpSystem.CDataGridView
Me.clmDate = New System.Windows.Forms.DataGridViewTextBoxColumn
Me.clmEmployee = New System.Windows.Forms.DataGridViewComboBoxColumn
Me.Column1 = New System.Windows.Forms.DataGridViewTextBoxColumn
CType(Me.CDataGridView1, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SuspendLayout()
'
'CDataGridView1
'
Me.CDataGridView1.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize
Me.CDataGridView1.Columns.AddRange(New System.Windows.Forms.DataGridViewColumn() {Me.clmDate, Me.clmEmployee, Me.Column1})
Me.CDataGridView1.Location = New System.Drawing.Point(12, 46)
Me.CDataGridView1.Name = "CDataGridView1"
Me.CDataGridView1.RowTemplate.Height = 21
Me.CDataGridView1.Size = New System.Drawing.Size(562, 339)
Me.CDataGridView1.TabIndex = 0
'
'clmDate
'
Me.clmDate.HeaderText = "日付"
Me.clmDate.Name = "clmDate"
'
'clmEmployee
'
Me.clmEmployee.HeaderText = "社員"
Me.clmEmployee.Name = "clmEmployee"
'
'Column1
'
Me.Column1.HeaderText = "Column1"
Me.Column1.Name = "Column1"
'
'frmTest
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 12.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(586, 445)
Me.Controls.Add(Me.CDataGridView1)
Me.Name = "frmTest"
Me.Text = "frmTest"
CType(Me.CDataGridView1, System.ComponentModel.ISupportInitialize).EndInit()
Me.ResumeLayout(False)End Sub
Friend WithEvents CDataGridView1 As HpSystem.CDataGridView
Friend WithEvents clmDate As System.Windows.Forms.DataGridViewTextBoxColumn
Friend WithEvents clmEmployee As System.Windows.Forms.DataGridViewComboBoxColumn
Friend WithEvents Column1 As System.Windows.Forms.DataGridViewTextBoxColumn
End Classフォームのコード
Imports System.Data.SqlClient
Public Class frmTest
Private DT As DataTablePrivate Sub SetCombobox()
DT = New DataTableUsing Cn As New SqlConnection(My.MySettings.Default.Con)
Using Cmd As New SqlCommandWith Cmd
.Connection = Cn
.CommandType = CommandType.StoredProcedure
.CommandText = "s_s_ptsearch"
.Parameters.AddWithValue("@firstkana", "")
.Parameters.AddWithValue("@lastkana", "")
End WithUsing DA As New SqlDataAdapter
DA.SelectCommand = CmdDA.Fill(DT)
End UsingDim clm As DataGridViewComboBoxColumn = Me.CDataGridView1.Columns("clmEmployee")
With clm
.DataSource = DT
.DisplayMember = "ptname"
.ValueMember = "pt_ptcode"
End WithEnd Using
End Using
End SubPrivate Sub SetDateFormat()
'日付の書式設定
Dim Wareki As New clsDGVWarekiTextBoxCell
Dim clm As DataGridViewColumn = Me.CDataGridView1.Columns("clmDate")clm.CellTemplate = Wareki
'Headerの文字位置設定
Me.CDataGridView1.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenterFor ColumnIndex As Integer = 0 To Me.CDataGridView1.ColumnCount - 1
Me.CDataGridView1.Columns(ColumnIndex).SortMode = DataGridViewColumnSortMode.NotSortable
Next
End SubPrivate Sub frmTest_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
SetCombobox()
SetDateFormat()
End Sub
End Class -
ご回答ありがとうございます。
以下がカスタムコントロールのコードです。
Public Class CDataGridView
Inherits System.Windows.Forms.DataGridView
Private Sub CDataGridView_CellEnter(ByVal sender As Object, _
ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles Me.CellEnter
Dim dgv As CDataGridView = CType(sender, CDataGridView)'編集コントロールがコンボボックスの場合
If TypeOf dgv.Columns(e.ColumnIndex) Is DataGridViewComboBoxColumn Then
SendKeys.Send("{F4}")'編集コントロールがテキストボックスの場合
ElseIf TypeOf dgv.Columns(e.ColumnIndex) Is DataGridViewTextBoxColumn Then
SendKeys.Send("{F2}")
End If
End Sub'**********************************************************************************************
'Enterキーで移動
'**********************************************************************************************
<System.Security.Permissions.UIPermission( _
System.Security.Permissions.SecurityAction.LinkDemand, _
Window:=System.Security.Permissions.UIPermissionWindow.AllWindows)> _
Protected Overrides Function ProcessDialogKey( _
ByVal keyData As Keys) As BooleanIf (keyData And Keys.KeyCode) = Keys.Enter Then
Return Me.ProcessTabKey(keyData)
ElseIf (keyData And Keys.KeyCode) = Keys.Delete Then
If MessageBox.Show("選択している行を削除します。" & vbCr & "よろしいですか" _
, "削除確認", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) = DialogResult.OK Then
DeleteRows()
End If
End IfReturn MyBase.ProcessDialogKey(keyData)
End Function<System.Security.Permissions.SecurityPermission( _
System.Security.Permissions.SecurityAction.LinkDemand, Flags:= _
System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)> _
Protected Overrides Function ProcessDataGridViewKey(ByVal e As System.Windows.Forms.KeyEventArgs) As Boolean
If e.KeyCode = Keys.Enter Then
Return Me.ProcessRightKey(e.KeyData)
End IfReturn MyBase.ProcessDataGridViewKey(e)
End Function'**********************************************************************************************
'選択されている行を削除
'**********************************************************************************************
Private Sub DeleteRows()
Dim r As DataGridViewRowFor Each r In Me.SelectedRows
Me.Rows.Remove(r)
Next
End SubPrivate Sub CDataGridView_CellValidating(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellValidatingEventArgs) Handles Me.CellValidating
End Sub
'**********************************************************************************************
'Comboboxに直接入力できるようにDropDownStyleをDropDownに変更
'**********************************************************************************************
Private Sub CDataGridView_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles Me.EditingControlShowing
If TypeOf e.Control Is DataGridViewComboBoxEditingControl Then
Dim cb As DataGridViewComboBoxEditingControl = CType(e.Control, DataGridViewComboBoxEditingControl)cb.DropDownStyle = ComboBoxStyle.DropDown
End If
End SubEnd Class
ところで、前回の返信の中にあった「鉛筆マーク」の確認はどのような意味があるのでしょうか。
Googleでも検索をしてみましたが、すぐにはよく理解ができませんでした。 -
ようやく時間が取れたの検証してみました。
TABキーおよびENTERキーはまずComboBoxが受け取ります。つまり、DataGridViewComboBoxEditingControlが受け取ることになります。この時、TABキーとENTERキーではDataGridViewComboBoxEditingControlにおける動作が異なります。TABキーの場合ですと項目の選択(SelectedIndexなどのセット)をするところまで行ってくれるようですが、ENTERキーの場合はそれを行わず、そのままDropDownListを閉じてしまうようです。ですから、DataGridViewComboBoxEditingControlにおいて、ENTERキーでもSelectedIndexをセットするようにすれば解決しそうです。
>ところで、前回の返信の中にあった「鉛筆マーク」の確認はどのような意味があるのでしょうか。
編集中という意味です。DataRowのBeginEditメソッドが実行された状態です。バインド系のコントロールにはこのメソッドが必要です。バインドを行うコントロールではデータ項目をいくつか変更し、<del>その変更結果をまとめてデータソースに返す必要があるからです。例えば3つの列がある場合、1つの列を変更する度にデータソースに書き戻していたのでは効率が悪いですし</del>、エラーチェックでも不具合が生じます。3つの列を入力し終わった時点でエラーチェックをして欲しいからです。そうしなければ、例えば3列目はnullを禁止にしていても、1列目を入力し終わった時点でDataRowのエラーチェックが走ってしまい、3列目はnullじゃダメとしかられても、「おいおいちょっと待ってよ、まだ全部入力してないんだからね」っていうことです。
編集中を終わるにはEndEditメソッドを実行し、<del>この時点でデータソースのDataRowへまとめて書き戻されます。</del>
BeginEditメソッド、EndEditメソッドはバインド系のコントロールが自動的に呼び出します。
つまり、鉛筆マークになっていない状態ではいくらComboBoxをいじっても、データソースへその値が書き戻されないということを言いたかったのです。さて、話を元に戻して、TI-cb400さんのコードに少し手を加えてみました。標準のDataGridViewComboBoxEditingControlを使わずに、それを継承したExDataGridViewComboBoxEditingControlでENTERキーを受け取ったらSelectedIndexをセットするようにしてます。このクラスを使うために、DataGridViewComboBoxColumnを継承したExDataGridViewComboBoxColumnクラスも定義しています。一度コンパイルするとこのExDataGridViewComboBoxColumnがデザイナで選択できるようになりますので、デザイナで選択して下さい。
また、以下のコードにおいて、
cb.EditingControlDataGridView.NotifyCurrentCellDirty(True)
という記述がありますが、これは強制的に鉛筆モードにするための記述です。理由は上で説明した通りです。Imports System.Data.SqlClient Public Class DataGridViewComboBox3 Private DT As DataTable Private Sub SetCombobox() DT = New DataTable Using Cn As New SqlConnection(My.MySettings.Default.TESTConnectionString) Using Cmd As New SqlCommand With Cmd .Connection = Cn .CommandType = CommandType.StoredProcedure .CommandText = "SelectTEST" '.Parameters.AddWithValue("@firstkana", "") '.Parameters.AddWithValue("@lastkana", "") End With Using DA As New SqlDataAdapter DA.SelectCommand = Cmd DA.Fill(DT) End Using Dim clm As DataGridViewComboBoxColumn = Me.CDataGridView1.Columns("clmEmployee") With clm .DataSource = DT '.DisplayMember = "ptname" '.ValueMember = "pt_ptcode" .DisplayMember = "TEST1" .ValueMember = "ID" End With End Using End Using End Sub Private Sub SetDateFormat() '日付の書式設定 'Dim Wareki As New clsDGVWarekiTextBoxCell 'Dim clm As DataGridViewColumn = Me.CDataGridView1.Columns("clmDate") 'clm.CellTemplate = Wareki 'Headerの文字位置設定 Me.CDataGridView1.ColumnHeadersDefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter For ColumnIndex As Integer = 0 To Me.CDataGridView1.ColumnCount - 1 Me.CDataGridView1.Columns(ColumnIndex).SortMode = DataGridViewColumnSortMode.NotSortable Next End Sub Private Sub frmTest_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load SetCombobox() SetDateFormat() End Sub End Class Public Class CDataGridView Inherits System.Windows.Forms.DataGridView Private Sub CDataGridView_CellEnter(ByVal sender As Object, _ ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles Me.CellEnter Dim dgv As CDataGridView = CType(sender, CDataGridView) '編集コントロールがコンボボックスの場合 If TypeOf dgv.Columns(e.ColumnIndex) Is DataGridViewComboBoxColumn Then SendKeys.Send("{F4}") '編集コントロールがテキストボックスの場合 ElseIf TypeOf dgv.Columns(e.ColumnIndex) Is DataGridViewTextBoxColumn Then SendKeys.Send("{F2}") End If End Sub '********************************************************************************************** 'Enterキーで移動 '********************************************************************************************** <System.Security.Permissions.UIPermission( _ System.Security.Permissions.SecurityAction.LinkDemand, _ Window:=System.Security.Permissions.UIPermissionWindow.AllWindows)> _ Protected Overrides Function ProcessDialogKey( _ ByVal keyData As Keys) As Boolean System.Diagnostics.Debug.WriteLine("ProcessDialogKey") If (keyData And Keys.KeyCode) = Keys.Enter Then Return Me.ProcessTabKey(keyData) ElseIf (keyData And Keys.KeyCode) = Keys.Delete Then If MessageBox.Show("選択している行を削除します。" & vbCr & "よろしいですか" _ , "削除確認", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) = DialogResult.OK Then DeleteRows() End If End If Return MyBase.ProcessDialogKey(keyData) End Function <System.Security.Permissions.SecurityPermission( _ System.Security.Permissions.SecurityAction.LinkDemand, Flags:= _ System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)> _ Protected Overrides Function ProcessDataGridViewKey(ByVal e As System.Windows.Forms.KeyEventArgs) As Boolean System.Diagnostics.Debug.WriteLine("ProcessDataGridViewKey") If e.KeyCode = Keys.Enter Then Return Me.ProcessRightKey(e.KeyData) End If Return MyBase.ProcessDataGridViewKey(e) End Function '********************************************************************************************** '選択されている行を削除 '********************************************************************************************** Private Sub DeleteRows() Dim r As DataGridViewRow For Each r In Me.SelectedRows Me.Rows.Remove(r) Next End Sub '********************************************************************************************** 'Comboboxに直接入力できるようにDropDownStyleをDropDownに変更 '********************************************************************************************** Private Sub CDataGridView_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles Me.EditingControlShowing If TypeOf e.Control Is DataGridViewComboBoxEditingControl Then Dim cb As DataGridViewComboBoxEditingControl = CType(e.Control, DataGridViewComboBoxEditingControl) 'cb.DropDownStyle = ComboBoxStyle.DropDown //既定のスタイルなのでセットしなくても良い '必ず編集モードにする。 cb.EditingControlDataGridView.NotifyCurrentCellDirty(True) End If End Sub End Class Public Class ExDataGridViewComboBoxColumn Inherits System.Windows.Forms.DataGridViewComboBoxColumn Public Sub New() MyBase.New() Me.CellTemplate = New ExDataGridViewComboBoxCell End Sub End Class Public Class ExDataGridViewComboBoxCell Inherits System.Windows.Forms.DataGridViewComboBoxCell Public Overrides ReadOnly Property EditType() As System.Type Get Return GetType(ExDataGridViewComboBoxEditingControl) End Get End Property End Class Public Class ExDataGridViewComboBoxEditingControl Inherits System.Windows.Forms.DataGridViewComboBoxEditingControl Protected Overrides Function IsInputKey(ByVal keyData As Keys) As Boolean Select Case keyData Case Keys.Enter Me.SelectedIndex = Me.FindStringExact(Me.Text) Return False Case Else Return MyBase.IsInputKey(keyData) End Select End Function End Class
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
- 編集済み trapemiyaModerator 2011年4月30日 12:37 編集するとコードが崩れるので再掲載
- 回答としてマーク TI-cb400 2011年5月2日 9:18
-
編集中という意味です。DataRowのBeginEditメソッドが実行された状態です。バインド系のコントロールにはこのメソッドが必要です。バインドを行うコントロールではデータ項目をいくつか変更し、その変更結果をまとめてデータソースに返す必要があるからです。例えば3つの列がある場合、1つの列を変更する度にデータソースに書き戻していたのでは効率が悪いですし、エラーチェックでも不具合が生じます。3つの列を入力し終わった時点でエラーチェックをして欲しいからです。そうしなければ、例えば3列目はnullを禁止にしていても、1列目を入力し終わった時点でDataRowのエラーチェックが走ってしまい、3列目はnullじゃダメとしかられても、「おいおいちょっと待ってよ、まだ全部入力してないんだからね」っていうことです。
編集中を終わるにはEndEditメソッドを実行し、この時点でデータソースのDataRowへまとめて書き戻されます。
すみません。上記は誤りです。まとめてデータソースへ書き出されるわけではありません。つまり、DataGridViewの列に値を入力し終わったタイミングでDataTableへ書き出されます。編集中は制約などが一時的に無効になります。詳しくは以下を参考にして下さい。
DataRow.BeginEdit メソッド
http://msdn.microsoft.com/ja-jp/library/system.data.datarow.beginedit.aspx
★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/