DataGridViewのRaadOnly背景色を変えない

解決


ターチ  2009-05-08 00:48:06  No: 145898

お世話になります。
VB2008でプログラムを開発しています、
戸惑ってしまい教えて頂きたく投稿しました。

DataGridViewを使いプログラムを作成しています。
セルに、個人コード(入力項目)と氏名(表示項目)が2つ有り
個人コードを入力するとテーブルから読み取った氏名を表示し、
次の行に移動、2人目の個人コードを入力するといった感じで、
入力画面を作成しています。

--問題--
氏名を、ReadOnly=Trueにしたのにクリックで背景色が青く
なってしまいます、別のセルをクリックすれば戻るのですが...

見やすい様、一行置きに色を変えて表示しています、、
マウスクリックしても青くならないような方法はないでしょうか?

いろいろ試したのですが解決できません、何か良い方法があれば教えて
頂ければと思います、宜しくお願いします。


魔界の仮面弁士  2009-05-08 01:40:49  No: 145899

Style 系プロパティを用いて、選択時の前景色/背景色を、
通常時の前景色/背景色と同じ色にしておくのが常套手段かと。


ターチ  2009-05-08 02:23:38  No: 145900

早速の回答ありがとうございます。
回答の通り考えましたが、入力担当者が目移りしてしまうので
一行おきに色を変えてほしいとの事です、なんとかならないでしょうか?
    
Protected Overrides Sub OnMouseDown(ByVal e As MouseEventArgs)
    If リードオンリー = True Then
        Return
    End If
    MyBase.OnMouseDown(e)
End Sub

DataGridViewを継承したコントロールにオオバーライドし実行しました
Returnの以外にコメントし実行したら氏名の背景色は青くならずに思い
通り動きました、そこで(If リードオンリー = True )判定を入れたら
どうかなと思います、カッコの中をコードにしたいのですがどの様に記述
すればいいか分かりません、よろしくお願いします。


魔界の仮面弁士  2009-05-08 02:53:11  No: 145901

> 一行おきに色を変えてほしいとの事です、なんとかならないでしょうか?

AlternatingRowsDefaultCellStyle プロパティを使ってみてください。

Private dgv As DataGridView
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
    dgv = New DataGridView()
    dgv.Dock = DockStyle.Fill
    Controls.Add(dgv)

    dgv.AllowUserToAddRows = False
    dgv.ColumnCount = 5
    dgv.RowCount = 100

    '全体の色
    With dgv.DefaultCellStyle
        .ForeColor = Color.DarkGreen
        .BackColor = Color.Lavender
        .SelectionForeColor = .ForeColor
        .SelectionBackColor = .BackColor
    End With

    '奇数行の色
    With dgv.AlternatingRowsDefaultCellStyle
        .BackColor = Color.LightSteelBlue
        .SelectionBackColor = .BackColor
    End With

End Sub


ターチ  2009-05-08 18:03:53  No: 145902

回答ありがとうございます、早速試し思い通りの結果が確認できました。
下記にコードを貼り付けました。

--追加質問--
氏名セルをクリックするとカーソルがなくなります、個人番号
にカーソルを残す事はできないでしょうか?

質問にきりがなくて申し訳ありません。

Public Class Form1
    Private dgv As DataGridView

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
        'dgv = New DataGridView()
        'dgv.Dock = DockStyle.Fill
        'Controls.Add(dgv)

        DataGridView1.AllowUserToAddRows = False
        DataGridView1.ColumnCount = 2
        DataGridView1.RowCount = 100

        '全体の色
        With DataGridView1.DefaultCellStyle
            .ForeColor = Color.DarkGreen
            .BackColor = Color.Lavender
            .SelectionForeColor = .ForeColor
            .SelectionBackColor = .BackColor
        End With

        '奇数行の色
        With DataGridView1.AlternatingRowsDefaultCellStyle
            .BackColor = Color.White
            .SelectionBackColor = .BackColor
        End With

        DataGridView1.Columns(1).ReadOnly = True

        DataGridView1.Focus()
        DataGridView1.CurrentCell = DataGridView1(1, 0)
        DataGridView1.BeginEdit(True)

    End Sub

    ''' <summary>セルがフォーカスされた時の処理です。</summary>
    Private Sub DataGridView1_CellEnter(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellEnter
        ' セルの背景色を変更します。
        If e.ColumnIndex >= 0 And e.RowIndex >= 0 Then
            DataGridView1.Item(e.ColumnIndex, e.RowIndex).Style.BackColor = Color.Yellow
            DataGridView1.BeginEdit(True)
        End If
    End Sub

    ''' <summary>セルがフォーカスを失う時の処理です。</summary>
    Private Sub DataGridView1_CellLeve(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellLeave
        ' セルの背景色を変更します。
        If e.ColumnIndex >= 0 And e.RowIndex >= 0 Then
            DataGridView1.Item(e.ColumnIndex, e.RowIndex).Style.BackColor = Color.Empty
            DataGridView1.EndEdit()
        End If
    End Sub

    Private Sub DataGridView1_CellValidated(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellValidated
        Dim currentCell As DataGridViewCell = DataGridView1.Item(e.ColumnIndex, e.RowIndex)
        If currentCell Is Nothing Then
            Return
        End If
        If Not e.ColumnIndex = 0 Then Return

        Dim no As Integer = DataGridView1.Item(e.ColumnIndex, e.RowIndex).Value
        Select Case no
            Case "1"
                DataGridView1.Item(e.ColumnIndex + 1, e.RowIndex).Value = "鈴木"
            Case "2"
                DataGridView1.Item(e.ColumnIndex + 1, e.RowIndex).Value = "佐藤"
        End Select
    End Sub
End Class


魔界の仮面弁士  2009-05-08 21:52:15  No: 145903

> 氏名セルをクリックするとカーソルがなくなります、個人番号
> にカーソルを残す事はできないでしょうか?
個人番号 = Columns(0)、
氏名セル = Columns(1)、という意味でしょうか。

提示されたコードでは、キャレットは表示されないと思います。
Load (≠Shown)イベントでは、コントロールが表示されていない状態ですから、
Focus メソッドは意味がありません(常に False を返すはず)。
それに、そもそも DataGridView と DataGridView.EditingControl は別物です。

とりあえず今回の件についていえば、EditingControlShowing イベント内にて
「Me.ActiveControl = e.Control」を記述しておけば、事は足りるかと思います。

ところで…。

> DataGridView1.Columns(1).ReadOnly = True
> DataGridView1.Focus()
> DataGridView1.CurrentCell = DataGridView1(1, 0)
> DataGridView1.BeginEdit(True)
1 列目 は ReadOnly なのですよね。にも関わらず、
1 列目のセルを BeginEdit させているのは、何故でしょうか?

> If e.ColumnIndex >= 0 And e.RowIndex >= 0 Then
And 演算子のかわりに、AndAlso 演算子を使われることをお奨めします。

> DataGridView1.CurrentCell = DataGridView1(1, 0)
> DataGridView1.Item(e.ColumnIndex, e.RowIndex).Style.BackColor = Color.Yellow
「DataGridView1(列, 行)」表記と
「DataGridView1.Item(列, 行)」表記とが混在しています。
どちらも同じ意味ではありますが、表記は統一しておきましょう。

> Dim no As Integer = DataGridView1.Item(e.ColumnIndex, e.RowIndex).Value
数字以外の値を入力された場合、ここの処理が失敗してしまいます。

Integer.TryParse メソッド等を使って変換するようにするか、もしくは、
DataTable 等をデータバインドして、0 列目に Integer 型の列を
割り当てるようにした方が良いでしょう。

> Select Case no
>   Case "1"
no は Integer 型なのですから、
Case "1" ではなく、Case 1 ですよね。

> DataGridView1.Item(e.ColumnIndex, e.RowIndex).Style.BackColor = Color.Yellow
Style をセル毎に割り当てると、DataGridViewCellStyle クラスのインスタンスが
それぞれに作成されるため、オーバーヘッドが大きくなります。

下記の[セル スタイルの効率的な使用]を参照しておいてください。
http://msdn.microsoft.com/ja-jp/library/ha5xt0d9.aspx

現在編集中のセルを黄色にしたいのであれば、事前に共通のスタイルを用意しておき、
それを割り当てるようにしてみてください。たとえば、DefaultCellStyle 系のプロパティのみを用い、
セル単位での個別スタイルを設定していない場合には、このように記述できます。

Private activeCellStyle As DataGridViewCellStyle
Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
  activeCellStyle = New DataGridViewCellStyle()
  activeCellStyle.BackColor = Color.Yellow
  DataGridView1.EditMode = DataGridViewEditMode.EditOnEnter

  DataGridView1.AllowUserToAddRows = False
  DataGridView1.ColumnCount = 2
  DataGridView1.RowCount = 100
    :
  DataGridView1.Columns(1).ReadOnly = True
End Sub

Private Sub DataGridView1_CellBeginEdit(ByVal sender As DataGridView, ByVal e As DataGridViewCellCancelEventArgs) Handles DataGridView1.CellBeginEdit
  If e.ColumnIndex >= 0 And e.RowIndex >= 0 Then
    sender(e.ColumnIndex, e.RowIndex).Style = activeCellStyle
  End If
End Sub

Private Sub DataGridView1_CellEndEdit(ByVal sender As DataGridView, ByVal e As DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit
  If e.ColumnIndex >= 0 And e.RowIndex >= 0 Then
    sender(e.ColumnIndex, e.RowIndex).Style = Nothing
  End If
End Sub


ターチ  2009-05-09 03:24:38  No: 145904

魔界の仮面弁士さん親切かつ丁寧なご説明ありがとうございます。
初心者で、上記の回答を理解するのに半日以上もかかりやっと直し終えました、
分ったようでも、完全に理解できたかどうかは不安です、下記にコードを修正
しましたので、よろしくお願いします。

Public Class Form1
    Private activeCellStyle As DataGridViewCellStyle

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
        activeCellStyle = New DataGridViewCellStyle()
        activeCellStyle.BackColor = Color.Yellow
        DataGridView1.EditMode = DataGridViewEditMode.EditOnEnter

        DataGridView1.AllowUserToAddRows = False
        DataGridView1.ColumnCount = 2
        DataGridView1.RowCount = 100

        '全体の色
        With DataGridView1.DefaultCellStyle
            .ForeColor = Color.DarkGreen
            .BackColor = Color.Lavender
            .SelectionForeColor = .ForeColor
            .SelectionBackColor = .BackColor
        End With

        '奇数行の色
        With DataGridView1.AlternatingRowsDefaultCellStyle
            .BackColor = Color.White
            .SelectionBackColor = .BackColor
        End With

        DataGridView1.Columns(1).ReadOnly = True
    End Sub

    ''' <summary>セルがフォーカスされた時の処理です。</summary>
    Private Sub DataGridView1_CellEnter(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellEnter
        ' セルの背景色を変更します。
        If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then
            sender.Item(e.ColumnIndex, e.RowIndex).Style = activeCellStyle
        End If
    End Sub

    ''' <summary>セルがフォーカスを失う時の処理です。</summary>
    Private Sub DataGridView1_CellLeve(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellLeave
        ' セルの背景色を変更します。
        If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then
            sender.Item(e.ColumnIndex, e.RowIndex).Style = Nothing
        End If
    End Sub

    Private Sub DataGridView1_CellValidated(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellValidated
        Dim currentCell As DataGridViewCell = sender.Item(e.ColumnIndex, e.RowIndex)
        If currentCell Is Nothing Then
            Return
        End If
        If Not e.ColumnIndex = 0 Then Return
        Dim no As Integer
        Integer.TryParse(sender.Item(e.ColumnIndex, e.RowIndex).Value, no)
        Select Case no
            Case 0
                sender.Item(e.ColumnIndex + 1, e.RowIndex).Value = ""
            Case 1
                sender.Item(e.ColumnIndex + 1, e.RowIndex).Value = "鈴木"
            Case 2
                sender.Item(e.ColumnIndex + 1, e.RowIndex).Value = "佐藤"
            Case Is > 2
                sender.Item(e.ColumnIndex + 1, e.RowIndex).Value = ""
        End Select
    End Sub

    Private Sub DataGridView1_EditingControlShowing(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
        Me.ActiveControl = e.Control
    End Sub
End Class

前回の質問で
> 氏名セルをクリックするとカーソルがなくなります、個人番号
> にカーソルを残す事はできないでしょうか?

説明が分かりずらくてすいません、もう一度説明したいと思います。

個人番号セル = Columns(0)、
氏名セル = Columns(1)、という意味です。

1行目の値  個人番号セル = "1" と 氏名セル = "鈴木"  
2行目の値  個人番号セル = "2" と 氏名セル = "佐藤"

と入力された状態で、現在は個人番号セル("2")にキャレットがあります、
氏名セル("鈴木")をクリックすると、キャレットが表示されなくなります、
その時キャレットをその行(クリックした行)の氏名セル("1")に強制的に
置くことは可能でしょうか?

> そもそも DataGridView と DataGridView.EditingControl は別物です。
この回答の指す意味が理解できません、強制的に置くことは難しいと
言うことなのでしょうか。

またの質問、きりがなくて申し訳ありません。


魔界の仮面弁士  2009-05-09 07:28:54  No: 145905

> ''' <summary>セルがフォーカスされた時の処理です。</summary>
> Private Sub DataGridView1_CellEnter(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellEnter

先の私のコードでは、CellBeginEdit/CellEndEdit を利用していますが、
ここでは、CellEnter/CellLeve イベントを利用していますね。

CellEnter/CellLeve と CellBeginEdit/CellEndEdit、どちらを使うべきかは、
作成するアプリ仕様による物なので一概には言えないのですが、両者はそれぞれ、
異なる動作を行うものであるという点には注意しておいてください。

たとえば Button をひとつ用意して、その Click イベントに
  DataGridView1.BeginEdit(True)
と記述したとします。

ボタンを押すときにはフォーカスが Button 側に移りますので、黄色セルは解除されますよね。
そしてボタンを押すと、BeginEdit により、再度編集状態に移行する事になります。

しかし CellEnter を使っていた場合、現在のセル位置は変更されていないため、
CellEnter イベントが発生せず、黄色にならない(しかし編集可能)という結果になります。
しかし、CellBeginEdit であればイベントが発生するため、再度黄色になってくれます。

> ''' <summary>セルがフォーカスされた時の処理です。</summary>
> Private Sub DataGridView1_CellEnter(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewCellEventArgs) Handles DataGridView1.CellEnter
>    ' セルの背景色を変更します。
>    If e.ColumnIndex >= 0 AndAlso e.RowIndex >= 0 Then
>        sender.Item(e.ColumnIndex, e.RowIndex).Style = activeCellStyle
>    End If
> End Sub
イベント内で、DataGridView1 ではなく sender を使うのは、もしも今後、
DataGridView2 が登場するような場合を考えてのことです。しかしながら、
上記のように sender を Object 型のまま利用するのはやめた方が良いでしょう。

先の私のコードのように、As Object の部分を
> ByVal sender As DataGridView, 
のように DataGridView 型に変更しておくか(要VB2008以降)、もしくは、
  Dim dgv As DataGridView = DirectCast(sender, DataGridView)
のように、TryCast や DirectCast 等で型変換してから使うようにします。
あるいは、元の DataGridView1 をそのまま使うか、ですね。

> Integer.TryParse(sender.Item(e.ColumnIndex, e.RowIndex).Value, no)
TryParse メソッドの第一引数は Stringで、戻り値は Booleanです。なので、
  If Integer.TryParse(CStr(sender.Item(e.ColumnIndex, e.RowIndex).Value), no) Then
    Select Case no
      :
    End Select
  End If
のように記述した方が良いかと思います。

ただし、数値変換できない場合には no は 0 になりますので、個人番号 = 0 を使わない場合は、
  Integer.TryParse(CStr(sender.Item(e.ColumnIndex, e.RowIndex).Value), no)
  Select Case no
    :
  End Case
の記述でも良いかも知れませんね。

> Select Case no
>     Case 0
>         sender.Item(e.ColumnIndex + 1, e.RowIndex).Value = ""
>     Case 1
>         sender.Item(e.ColumnIndex + 1, e.RowIndex).Value = "鈴木"
>     Case 2
>         sender.Item(e.ColumnIndex + 1, e.RowIndex).Value = "佐藤"
>     Case Is > 2
>         sender.Item(e.ColumnIndex + 1, e.RowIndex).Value = ""
> End Select
これはマズイと思いますよ。たとえば、2 を入力すると "佐藤" と表示されますが、その後、
その 2 を -123 に変更したとしても、"" ではなく、"佐藤" のままになってしまいますよね。

> 1行目の値  個人番号セル = "1" と 氏名セル = "鈴木"  
> 2行目の値  個人番号セル = "2" と 氏名セル = "佐藤"
> と入力された状態で、現在は個人番号セル("2")にキャレットがあります、
Columns(1) は ReadOnly にしていますので、氏名セルがアクティブになっても、
キャレットは表示されないはずです。フォーカス枠の破線は表示されるでしょうけれども。
(キャレット/Caretとは、TextBox 上で点滅する I の字のカーソルのことです)

> その時キャレットをその行(クリックした行)の氏名セル("1")に強制的に
> 置くことは可能でしょうか?
今回の場合は、SelectionMode プロパティを FullRowSelect にし、さらに
MultiSelect プロパティを False にした状態で、このように記述してみてください。

Private Sub DataGridView1_SelectionChanged(ByVal sender As DataGridView, ByVal e As EventArgs) Handles DataGridView1.SelectionChanged
  Dim pos As Point = sender.CurrentCellAddress
  If pos.Y >= 0 AndAlso pos.X = 1 Then
    sender.CurrentCell = sender.Item(0, pos.Y)
  End If
End Sub

>> そもそも DataGridView と DataGridView.EditingControl は別物です。
> この回答の指す意味が理解できません、強制的に置くことは難しいと
> 言うことなのでしょうか。
最初のコードのように、
  DataGridView1.Focus()
と記述しても、DataGridView.EditingControl がアクティブになるとは限らないという事です。

そもそも、DataGridView の編集を開始した時には、DataGridView の上に
EditingControl(編集コントロール)が表示される仕様になっています。

現在どのコントロールがアクティブになっているかを確認するために、
フォームに Timer を貼っておき、Form の Load イベントに
  Timer1.Interval = 100
  Timer1.Start()
そして、Timer1 の Tick イベントに
  If Me.ActiveControl Is Nothing Then
    Me.Text = ""
  Else
    Me.Text = Me.ActiveControl.GetType().FullName
  End If
と記述してみてください。
編集作業中は、DataGridView ではなく、DataGridViewTextBoxEditingControl と
表示されることが、目で確認できるかと思います。

編集コントロールの実態は、セルのタイプに応じて異なっており、
テキストボックス(DataGridViewTextBoxEditingControl)だったり、
コンボボックス(DataGridViewComboBoxEditingControl)だったりしますが、
いずれにせよ、DataGridView とは別のコントロールなのです。

※ただし、編集コントロールが使われない場合(チェックボックスタイプのセルの時など)もあります。

フォームを表示した直後でなければ、DataGridView 自身が、編集コントロールを
自動的にアクティブにしてくれるのですが、ロード直後のタイミングだと、期待した通りに
フォーカスが遷移してくれません。そこで、ActiveControl プロパティを使って、
強制的に編集コントロールをアクティブにしたというわけです。


ターチ  2009-05-09 19:38:51  No: 145906

魔界の仮面弁士さんご回答ありがとうございます。

> 現在どのコントロールがアクティブになっているかを確認するために
> 記述してみてください。編集作業中は、DataGridView ではなく、
> DtaGridViewTextBoxEditingControl と
> 表示されることが、目で確認できるかと思います。

素晴らしいです〜こんな確認の仕方も有るんですね...
今後の開発に生かせればと思います。

お蔭様で無事に問題を解決することができました、
この度は細かい内容まで、ご親切に指導していただき
本当に参考になりました、ありがとうございます。

また投稿し、お聞きする事もあるかと思いますが、
その時は宜しくお願い致します。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加