none
DataGridViewのCellPaintingで文字が重なる RRS feed

  • 質問

  • DataGridViewで”...”を表示したくなかったので、
    CellPaintingを自分で処理するようにしたのですが、
    横スクロールし、さらに縦スクロールを行なうと、
    横スクロールで見えなくなった部分が、前のセルに重なって表示されてしまいました。

    この現象を回避する方法があれば教えてください。

    CellPaintingのコード:

        Private Sub DataGridView1_CellPainting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
            'セルの選択状態
            Dim Selected As Boolean = e.State And DataGridViewElementStates.Selected
            'セルの前景色
            Dim ForeColor As Color = If(Selected, e.CellStyle.SelectionForeColor, e.CellStyle.ForeColor)
            '背景の描画
            e.PaintBackground(e.ClipBounds, Selected)
            '前景の描画
            TextRenderer.DrawText(e.Graphics, e.Value, e.CellStyle.Font, e.CellBounds, ForeColor)
            'イベント終了
            e.Handled = True
        End Sub

    画像
    上.最初
    中.横スクロール
    下.縦スクロール

    2014年12月1日 11:51

すべての返信

  • 縦スクロールしたときだけではありません。
    たとえば、画面外に一度フォームを追い出して画面内に戻すような移動操作をすると、重なる現象が再現することを確認できると思います。
    TextRenderer.DrawText をそのまま使うと Graphics のクリッピングを無視するのでこのような振る舞いになります。
    (普段の再描画の範囲・順番でたまたまうまくいっていただけということになります)

    さて、TextRenderer でクリッピングが効かないとわかったことから、検索で「TextRenderer.DrawText clipping」を探すと、こちらの記事 にあたります。
    この記事をお読みいただければ、クリッピングを維持するための引数の指定が見えてくるのではないでしょうか。

    2014年12月1日 13:53
    モデレータ
  • こんな

        Private Sub DataGridView1_CellPainting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
    
            'セルの選択状態
            Dim Selected As Boolean = e.State And DataGridViewElementStates.Selected
            'セルの前景色
            Dim ForeColor As Color = If(Selected, e.CellStyle.SelectionForeColor, e.CellStyle.ForeColor)
            '背景の描画
            e.PaintBackground(e.ClipBounds, Selected)
            '前景の描画
    
            'TextRenderer.DrawTextはGraphics.Clipを無視するらしい
            'そのため、Clipの範囲外に文字がはみ出てもDataGridViewに描画されてしまう
            'TextRenderer.DrawText(e.Graphics, e.Value, e.CellStyle.Font, e.CellBounds, ForeColor)
    
            Dim isUseTextRender As Boolean = False
            If (isUseTextRender) Then
                'TextRenderを使う場合
                'セルと同じ大きさの別のビットマップに書いて、そのビットマップをDataGridViewのセルに転写する
                Dim bmp As Bitmap = New Bitmap(e.CellBounds.Width, e.CellBounds.Height, e.Graphics)
                Using g As Graphics = Graphics.FromImage(bmp)
                    TextRenderer.DrawText(g, e.FormattedValue, e.CellStyle.Font, New Rectangle(Point.Empty, bmp.Size), e.CellStyle.ForeColor)
                End Using
                e.Graphics.DrawImage(bmp, e.CellBounds)
            Else
                'TextRenderを使わない場合
                '自分で文字位置を決めてGraphics.DrawStringで文字列を描く
                Dim sf As New StringFormat()
                sf.Alignment = StringAlignment.Center
                sf.LineAlignment = StringAlignment.Center
                sf.Trimming = StringTrimming.Character
    
                e.Graphics.DrawString(e.FormattedValue, e.CellStyle.Font, New SolidBrush(e.CellStyle.ForeColor), e.CellBounds, sf)
            End If
    
            'イベント終了
            e.Handled = True
        End Sub



    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2014年12月1日 14:52
  • TextRenderer はデフォルトでクリッピングを無視してしまうだけであって、フラグで指定すれば対応可能です。
    詳しくは前述の投稿にリンクしている MSDN マガジンの記事をお読みください。
    (TextRenderer 関連のところのクリッピングという言葉周りを)
    2014年12月1日 15:42
    モデレータ
  • たしかに、Azuleanさんの指摘されているように適切なTextFormatFlagsの指定かSetCompatibleTextRenderingDefault(true)でクリップを有効にできるので、私の書いたコードは必要ないですね。


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2014年12月1日 16:26
  • Azuleanさん、gekkaさん、返信いただき、ありがとうございます。

    Azuleanさんに教わったMSDNマガジンの記事は、
    勉強のため一通り読んでおきました。

    質問する前にVisual Studioのドキュメントの
    TextFormatFlags列挙体については読んだのですが、
    「クリッピング」と言う言葉を知らなかったので、
    まさかこの事とは思わず、試しもしませんでした。
    「切り抜き」と言う意味でしょうかね。

    そのせいでおふた方には手間をかけていただく事になりましたが、
    感謝しています。

    gekkaさんにはソースコードをいただいたので、
    手法の勉強として実行してみました。

    isUseTextRenderをTure/Falseで確認してみました。
    TextRenderにこだわっていたわけではないのですが、
    省略記号と呼ぶのですね”...”を消す方法を
    他のサイトで見つけたもので、そのまま流用していました。

    それと、以前gekkaさんにも返信いただいた
    私の質問「DataGridViewの行ヘッダーの左余白」で、
    文字の表示部分にはTextRenderを採用したのもあって、
    頭の中はTextRenderだけになっていました。

    実のところ、先のMSDNマガジンの記事にもありますが、
    GraphicsクラスとTextRendererクラスの相違や関係も理解しておらず、
    業務優先で勉強不足なままやっている不始末でした。

    セルスタイルのプロパティにでも、
    省略記号を表示しない設定があればいいのにと思うのですが、
    他にも私と同じ羽目になった人もあろうに、
    検索してもなかなかヒットせず、質問する事になってしまいました。

    引数にTextFormatFlags.PreserveGraphicsClippingを設定する事で解決しました。

    Azuleanさん、gekkaさん、ありがとうございました。
    2014年12月2日 14:09