Click here to Skip to main content
15,885,985 members
Home / Discussions / Windows Forms
   

Windows Forms

 
GeneralRe: What is best way to convert desktop app to web app? Pin
bobishkindaguy27-Jul-10 9:18
bobishkindaguy27-Jul-10 9:18 
GeneralRe: What is best way to convert desktop app to web app? Pin
kofflerd27-Jul-10 20:00
kofflerd27-Jul-10 20:00 
AnswerRe: What is best way to convert desktop app to web app? Pin
Phil N Bennett25-Jul-10 11:29
Phil N Bennett25-Jul-10 11:29 
GeneralRe: What is best way to convert desktop app to web app? Pin
bobishkindaguy27-Jul-10 9:20
bobishkindaguy27-Jul-10 9:20 
AnswerRe: What is best way to convert desktop app to web app? Pin
Andy_L_J27-Jul-10 22:48
Andy_L_J27-Jul-10 22:48 
AnswerRe: What is best way to convert desktop app to web app? Pin
BillWoodruff28-Jul-10 18:12
professionalBillWoodruff28-Jul-10 18:12 
AnswerRe: What is best way to convert desktop app to web app? Pin
Ravi Bhavnani2-Aug-10 14:45
professionalRavi Bhavnani2-Aug-10 14:45 
QuestionDataGridView Custom Cells/Columns problems Pin
kmh7275620-Jul-10 6:43
kmh7275620-Jul-10 6:43 
I believe that my questions are all going to revolve around the internal workings of DataGridView.
I've been trying to find a forum where I can get some interaction and help to move forward.
Microsoft's websites seem to be all questions and no answers.

I'm using VB.Net under Visual Studio 2005 SP2.

I need to have a DataColumn that is dynamically visible based on other data in the same DataRow.

After a lot of unsuccessful testing with various methods of accomplishing this, I came across the concept of Custom Cells/Columns.

I started with several slightly varying examples and everything seemed to be falling into place. I tried to research each property and attribute as I incrementally learned a lot about the internal workings of Windows controls in general and DataGridView in particular.

The confusing symptom is that as I scroll down through the grid, the painting of the custom cells is at best unreliable.

I created a minimum test case to eliminate as many variables as possible and I combined all of the source into one Form1.vb as follows:

Option Explicit On
Option Strict On
Public Class Form1
  Public m_OrderDetails As Generic.List(Of OrderDetail)
  Public m_dgv As New System.Windows.Forms.DataGridView

  Public Enum GridViewColumnType
    TextBox
    InvisibleTextBox
  End Enum

  Public Sub New()
    MyBase.New()
    InitializeComponent()
    ' fake the usual load procedures with minimum so this screen can stand alone
    m_OrderDetails = New Generic.List(Of OrderDetail)
    Dim LineCounter As Int32 = 0
    Dim ItemCounter As Int32 = 0
    GenerateSetOfLines(LineCounter, ItemCounter, OrderDetail.ProductItemDealerSplitType.Percent)
    GenerateSetOfLines(LineCounter, ItemCounter, OrderDetail.ProductItemDealerSplitType.ItemCodes)
    GenerateSetOfLines(LineCounter, ItemCounter, OrderDetail.ProductItemDealerSplitType.Percent)
    GenerateSetOfLines(LineCounter, ItemCounter, OrderDetail.ProductItemDealerSplitType.Dollars)
    GenerateSetOfLines(LineCounter, ItemCounter, OrderDetail.ProductItemDealerSplitType.Percent)
    GenerateSetOfLines(LineCounter, ItemCounter, OrderDetail.ProductItemDealerSplitType.ItemCodes)
    GenerateSetOfLines(LineCounter, ItemCounter, OrderDetail.ProductItemDealerSplitType.Percent)
  End Sub

  Private Sub GenerateSetOfLines(ByRef linecount As Int32, ByRef itemcount As Int32, ByVal splittype As OrderDetail.ProductItemDealerSplitType)
    itemcount += 1
    linecount += 100
    Dim od As New OrderDetail(linecount, "ITEM" & itemcount.ToString, splittype)
    m_OrderDetails.Add(od)
    linecount += 100
    od = New OrderDetail(linecount, String.Empty, splittype)
    m_OrderDetails.Add(od)
    linecount += 100
    od = New OrderDetail(linecount, String.Empty, splittype)
    m_OrderDetails.Add(od)
  End Sub

  Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Me.Size = New Size(350, 175)
    With m_dgv
      .Name = "DataGridView1"
      .Location = New Point(0, 0)
      .Dock = DockStyle.Fill
      .AutoGenerateColumns = False
      .AllowUserToAddRows = False
      .AllowUserToDeleteRows = False
      AddCol(m_dgv, "Line#", GridViewColumnType.TextBox, "DisplayLineNumberColumn", "DisplayLineNumber", True, DataGridViewContentAlignment.MiddleLeft, "00000")
      AddCol(m_dgv, "Item", GridViewColumnType.TextBox, "ItemCodeColumn", "ItemCode", True, DataGridViewContentAlignment.MiddleLeft, "")
      AddCol(m_dgv, "Split Type", GridViewColumnType.InvisibleTextBox, "SplitTypeColumn", "DealerSplitType", True, DataGridViewContentAlignment.MiddleLeft, "")
      .DataSource = m_OrderDetails
    End With
    Me.Controls.Add(m_dgv)
    m_dgv.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells)
    AddHandler m_dgv.CellFormatting, AddressOf DataGridView1_CellFormatting
  End Sub

  Private Sub AddCol(ByVal grid As DataGridView, ByVal headertext As String, ByVal columntype As GridViewColumnType, ByVal name As String, ByVal propertyname As String, ByVal protect As Boolean, ByVal align As Windows.Forms.DataGridViewContentAlignment, ByVal format As String)
    Dim newcol As DataGridViewColumn
    Select Case columntype
      Case GridViewColumnType.InvisibleTextBox
        newcol = New InvisibleTextBoxColumn
      Case Else
        newcol = New DataGridViewTextBoxColumn
    End Select
    newcol.Name = name
    newcol.DataPropertyName = propertyname
    newcol.HeaderText = headertext
    newcol.ReadOnly = protect
    newcol.DefaultCellStyle.Alignment = align
    newcol.DefaultCellStyle.Format = format
    grid.Columns.Add(newcol)
  End Sub

  Private Sub DataGridView1_CellFormatting(ByVal sender As Object, ByVal e As System.Windows.Forms.DataGridViewCellFormattingEventArgs)
    If e.ColumnIndex <> m_dgv.Columns.Item("SplitTypeColumn").Index Then
      Exit Sub
    End If
    Dim invtextbox As InvisibleTextBoxCell = CType(m_dgv.Item(e.ColumnIndex, e.RowIndex), InvisibleTextBoxCell)
    invtextbox.Invisible = (m_dgv.Item("ItemCodeColumn", e.RowIndex).Value.ToString = String.Empty)
    Dim sb As New System.Text.StringBuilder
    sb.Append("CellFormat: ")
    sb.Append(m_dgv.Item("DisplayLineNumberColumn", e.RowIndex).Value.ToString)
    sb.Append(" ")
    sb.Append(m_dgv.Item("ItemCodeColumn", e.RowIndex).Value.ToString)
    sb.Append(" ")
    sb.Append(invtextbox.Instance.ToString)
    sb.Append(" ")
    sb.Append(invtextbox.Invisible.ToString)
    Debug.Print(sb.ToString)
  End Sub
End Class

Public Class OrderDetail
  Private m_DisplayLineNumber As Int32
  Private m_ItemCode As String
  Private m_DealerSplitType As ProductItemDealerSplitType

  Public Enum ProductItemDealerSplitType
    Percent = 0
    Dollars = 1
    ItemCodes = 2
  End Enum

  Public Sub New(ByVal displaylinenumber As Int32, ByVal itemcode As String, ByVal dealersplittype As ProductItemDealerSplitType)
    m_DisplayLineNumber = displaylinenumber
    m_ItemCode = itemcode
    m_DealerSplitType = dealersplittype
  End Sub

  Public ReadOnly Property DealerSplitType() As ProductItemDealerSplitType
    Get
      Return m_DealerSplitType
    End Get
  End Property

  Public ReadOnly Property DisplayLineNumber() As Int32
    Get
      Return m_DisplayLineNumber
    End Get
  End Property

  Public ReadOnly Property ItemCode() As String
    Get
      Return m_ItemCode
    End Get
  End Property
End Class

Public Class InvisibleTextBoxCell
  Inherits Windows.Forms.DataGridViewTextBoxCell

  Private InvisibleValue As Boolean
  Private InstanceValue As Int32

  Public Property Instance() As Int32
    Get
      Return Me.InstanceValue
    End Get
    Set(ByVal value As Int32)
      Me.InstanceValue = value
    End Set
  End Property

  Public Property Invisible() As Boolean
    Get
      Return Me.InvisibleValue
    End Get
    Set(ByVal value As Boolean)
      Me.InvisibleValue = value
      If Me.DataGridView IsNot Nothing Then
        If Me.DataGridView.ReadOnly OrElse _
          Me.DataGridView.Rows.Item(Me.RowIndex).ReadOnly OrElse _
          Me.DataGridView.Columns.Item(Me.ColumnIndex).ReadOnly Then
        Else
          Me.ReadOnly = value
        End If
      End If
    End Set
  End Property

  Public Overrides Function Clone() As Object
    Dim Cell As InvisibleTextBoxCell = CType(MyBase.Clone(), InvisibleTextBoxCell)
    Cell.Invisible = Me.Invisible
    LastInstanceId += 1
    Cell.Instance = LastInstanceId
    Return Cell
  End Function

  Public Sub New()
    MyBase.New()
    Me.InvisibleValue = False
    LastInstanceId += 1
    Me.InstanceValue = LastInstanceId
  End Sub

  Protected Overrides Sub Paint(ByVal graphics As Drawing.Graphics, ByVal clipBounds As Drawing.Rectangle, ByVal cellBounds As Drawing.Rectangle, ByVal rowIndex As Integer, ByVal elementState As Windows.Forms.DataGridViewElementStates, ByVal value As Object, ByVal formattedValue As Object, ByVal errorText As String, ByVal cellStyle As Windows.Forms.DataGridViewCellStyle, ByVal advancedBorderStyle As Windows.Forms.DataGridViewAdvancedBorderStyle, ByVal paintParts As Windows.Forms.DataGridViewPaintParts)
    Dim sb As New System.Text.StringBuilder
    sb.Append("InvTxtPaint: ")
    sb.Append(MyBase.DataGridView.Rows.Item(rowIndex).Cells.Item("DisplayLineNumberColumn").Value.ToString())
    sb.Append(" ")
    sb.Append(MyBase.DataGridView.Rows.Item(rowIndex).Cells.Item("ItemCodeColumn").Value.ToString())
    sb.Append(" ")
    sb.Append(Me.InstanceValue.ToString)
    sb.Append(" ")
    sb.Append(Me.InvisibleValue.ToString.ToUpper)
    Debug.Print(sb.ToString)
    If Me.InvisibleValue Then
      ' The textbox cell is invisible, so paint the border and background
      ' Draw the background of the cell, if specified.
      If (paintParts And Windows.Forms.DataGridViewPaintParts.Background) = Windows.Forms.DataGridViewPaintParts.Background Then
        Dim cellBackground As New Drawing.SolidBrush(cellStyle.BackColor)
        graphics.FillRectangle(cellBackground, cellBounds)
        cellBackground.Dispose()
      End If
      ' Draw the cell borders, if specified.
      If (paintParts And Windows.Forms.DataGridViewPaintParts.Border) = Windows.Forms.DataGridViewPaintParts.Border Then
        PaintBorder(graphics, clipBounds, cellBounds, cellStyle, advancedBorderStyle)
      End If
    Else
      ' The textbox cell is not invisible, so let the base class handle the painting.
      MyBase.Paint(graphics, clipBounds, cellBounds, rowIndex, elementState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts)
    End If
  End Sub
End Class

Public Class InvisibleTextBoxColumn
  Inherits Windows.Forms.DataGridViewTextBoxColumn

  Public Sub New()
    Me.CellTemplate = New InvisibleTextBoxCell()
  End Sub
End Class


The only other code is a Public variable called LastInstanceId in a module file so that I could generate an identifier for each created Custom Cell.

Structurally I am binding the grid to a Generic.List of Custom Business Objects. I believe there is enough evidence here that something very unusual is going on but I can't imagine what some of the answers might lead to.

As the form is first loaded, the grid appears entirely correct and the following Output messages are produced:


CellFormat: 100 ITEM1 3 False
InvTxtPaint: 100 ITEM1 3 FALSE
CellFormat: 200 7 True
InvTxtPaint: 200 5 FALSE
CellFormat: 300 9 True
InvTxtPaint: 300 5 FALSE
CellFormat: 400 ITEM2 11 False
InvTxtPaint: 400 ITEM2 5 FALSE
CellFormat: 500 13 True
InvTxtPaint: 500 5 FALSE
CellFormat: 600 15 True
InvTxtPaint: 600 5 FALSE
CellFormat: 100 ITEM1 3 False
InvTxtPaint: 100 ITEM1 3 FALSE
CellFormat: 200 7 True
InvTxtPaint: 200 7 TRUE
CellFormat: 300 9 True
InvTxtPaint: 300 9 TRUE
CellFormat: 400 ITEM2 11 False
InvTxtPaint: 400 ITEM2 11 FALSE
CellFormat: 500 13 True
InvTxtPaint: 500 13 TRUE
CellFormat: 600 15 True
InvTxtPaint: 600 15 TRUE


Scrolling down one line at a time works correctly but clicking below the scrollbar handle to cause an entire page at a time produces the following Output:


CellFormat: 600 15 True
InvTxtPaint: 600 15 TRUE
CellFormat: 700 ITEM3 17 False
InvTxtPaint: 700 ITEM3 5 FALSE
CellFormat: 800 19 True
InvTxtPaint: 800 5 FALSE
CellFormat: 900 21 True
InvTxtPaint: 900 5 FALSE
CellFormat: 1000 ITEM4 23 False
InvTxtPaint: 1000 ITEM4 5 FALSE
CellFormat: 1100 25 True
InvTxtPaint: 1100 5 FALSE


The InstanceId's and Invisible properties are totally out of control starting with line 700.

So, my questions go something like this:

Why does a Custom Cell have to have a Clone method at all and how is functionally used?
Why would there ever be 2 instances of the same Custom Cell at the same point in time?
Where are all of the 'missing' InstanceId's?
Why isn't the Paint method inside the Custom Cell class getting the same instance that was just immediately processed by the CellFormatting event?
When I intentionally avoid interacting directly with the form and I move some other window in front of it and away again, the resulting repaint is again entirely correct without any of the InstanceId 'problems'. Why?

When I decided to do this as a learning experience, I didn't know how much I was going to need to understand.

Then again, there could just be a bug somewhere in this code that I can't see because I have been looking at it for too long.

Thank you in advance for any insights and your time.
AnswerRe: DataGridView Custom Cells/Columns problems Pin
Peace ON21-Jul-10 21:43
Peace ON21-Jul-10 21:43 
GeneralRe: DataGridView Custom Cells/Columns problems Pin
kmh7275622-Jul-10 23:36
kmh7275622-Jul-10 23:36 
QuestionHelp with Reports: Within one of my .rdlc there 2 tables from 2 datasets.... Pin
roman_s15-Jul-10 4:56
roman_s15-Jul-10 4:56 
AnswerRe: Help with Reports: Within one of my .rdlc there 2 tables from 2 datasets.... Pin
Richard Blythe15-Jul-10 6:46
Richard Blythe15-Jul-10 6:46 
QuestionC# Windows Form Application with WCF on Windows XP Pin
xavier999914-Jul-10 16:06
xavier999914-Jul-10 16:06 
AnswerRe: C# Windows Form Application with WCF on Windows XP Pin
Eddy Vluggen16-Jul-10 0:36
professionalEddy Vluggen16-Jul-10 0:36 
AnswerRe: C# Windows Form Application with WCF on Windows XP Pin
Abhinav S20-Jul-10 18:33
Abhinav S20-Jul-10 18:33 
AnswerRe: C# Windows Form Application with WCF on Windows XP Pin
spn21-Jul-10 18:59
spn21-Jul-10 18:59 
QuestionSave DataGridView To Database Pin
.NetDeveloper0910-Jul-10 21:31
.NetDeveloper0910-Jul-10 21:31 
AnswerRe: Save DataGridView To Database Pin
TimmL11-Jul-10 8:58
TimmL11-Jul-10 8:58 
AnswerRe: Save DataGridView To Database Pin
Peace ON11-Jul-10 19:53
Peace ON11-Jul-10 19:53 
QuestionControl Binding of a bool isn't updating Pin
TimmL9-Jul-10 2:56
TimmL9-Jul-10 2:56 
AnswerRe: Control Binding of a bool isn't updating Pin
Richard Blythe9-Jul-10 6:00
Richard Blythe9-Jul-10 6:00 
AnswerRe: Control Binding of a bool isn't updating Pin
darkelv9-Jul-10 7:43
darkelv9-Jul-10 7:43 
QuestionDataset and Crystal Reports Pin
.NetDeveloper093-Jul-10 23:05
.NetDeveloper093-Jul-10 23:05 
AnswerRe: Dataset and Crystal Reports Pin
darkelv10-Jul-10 18:50
darkelv10-Jul-10 18:50 
GeneralRe: Dataset and Crystal Reports Pin
.NetDeveloper0910-Jul-10 21:27
.NetDeveloper0910-Jul-10 21:27 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.