Announcements

Announcement: We’re aware of an issue affecting starting a new topic from category pages and creating new blog posts. New topics can still be started directly from the relevant board. Learn more here.

Crash (Endless loop) on any iteract to non-First row of DataGridView

Crash (Endless loop) on any iteract to non-First row of DataGridView

Maxim-CADman77
Advisor Advisor
950 Views
4 Replies
Message 1 of 5

Crash (Endless loop) on any iteract to non-First row of DataGridView

Maxim-CADman77
Advisor
Advisor

(this thread seems related to general programming but as soon as I suffer the issue in context of Inventor-relatad project I hope it is not too-much off-topic  here)

 

In one of my Inventor-related projects I'd like to provide user with Windows Form containing controls of some options (CheckBoxes) organized with DataGridView.

 

Here is a very simplified (Paint-created) version of the desired form:

Tabled-Options-Form.pngTabled-Options-Form.png

Pay attention there are some <empty> (not an option) cells.

 

The hard thing of this task - irregularity of the CheckBoxCell pattern (should be generated dynamically according to some business logic that is not an object of this thread).

 

Initially I thought I need to use Boolean type for Columns where CheckBoxes are to be placed but that seems to be the wrong way (AFAIK CheckBoxCell can't be converted to an <empty cell>, at least all my attempts to do so were unsuccessful, see commented).
Then I search Internet a bit and found a mention that opposite conversation (from TextBoxCell to CheckBoxCell is possible).

Using this knowledge I get some progress towards the desired.


Now I can convert <empty cell> to checkbox with following iLogic code (see the DataBindingComplete sub):

Option Explicit On

AddReference "System.Data"
AddReference "System.Xml"
Imports System.Data
Imports System.Windows.Forms


Class AAA

Friend WithEvents oDGV As New DataGridView

Dim sHeader1 As String = "Name"
Dim sHeader2 As String = "Option1"
Dim sHeader3 As String = "Option2"


Sub Main

Dim oDT As DataTable = New DataTable

oDT.Columns.Add(sHeader1, GetType(String))
oDT.Columns.Add(sHeader2, GetType(String)) ' GetType(Boolean))
oDT.Columns.Add(sHeader3, GetType(String)) ' GetType(Boolean))

Dim names = New List(Of String)
names.Add("A1")
names.Add("A2")

' Dim opt1vals = New List(Of String)
' opt1vals.Add(False)
' opt1vals.Add(True)

' Dim opt2vals = New List(Of String)
' opt2vals.Add(True)
' opt2vals.Add(False)

Dim i As Integer = 0
For Each name in Names
	oDT.Rows.Add(oDT.Columns.Count)
	oDT.Rows(i)(sHeader1) = name
	' oDT.Rows(i)(sHeader2) = opt1vals(i)
	' oDT.Rows(i)(sHeader3) = opt2vals(i)
	i +=1
Next


Dim oDV As DataView = oDT.DefaultView

With oDGV
	.AllowUserToAddRows = False
	.AutoSize = True
	.DataSource = oDV
End With


Dim oForm As New Form
With oForm
	.AutoSize = True
	.Controls.Add(oDGV)
End With

' logger.info("oDGV.Rows.Count #1 = " & oDGV.Rows.Count)

oForm.ShowDialog()

End Sub


Sub oDGV_DataBindingComplete(ByVal sender As Object, ByVal E As DataGridViewBindingCompleteEventArgs) Handles oDGV.DataBindingComplete

	logger.info("oDGV.Rows.Count #2 = " & oDGV.Rows.Count)

	oDGV.Rows(0).Cells(sHeader3).Value = True
	oDGV.Rows(0).Cells(sHeader3) = New DataGridViewCheckBoxCell()

	' oDGV.Rows(1).Cells(sHeader2).Value = True ' !!! ENDLESS-LOOP
	' oDGV.Rows(1).Cells(sHeader2) = New DataGridViewCheckBoxCell()
	
End Sub

' Sub oDGV_DataBindingComplete(ByVal sender As Object, ByVal E As DataGridViewBindingCompleteEventArgs) Handles oDGV.DataBindingComplete
	' logger.info("oDGV.Rows.Count-2 = " & oDGV.Rows.Count)

	' oDGV.Rows(0).Cells(sHeader2).Value = False
	' oDGV.Rows(0).Cells(sHeader2) = New DataGridViewTextBoxCell()
	' oDGV.Rows(0).Cells(sHeader2).Value = ""
' End Sub

End Class

 

BUT for some reason this behaves as expected ONLY for cells of the first row (row index = 0).
As soon as I try to do this for a cell in the second row (row index = 1) the code crashes (after some time of endless cycling ... which I detect by means of iLogic Log panel).

 

But the problem seems not the conversation itself ... the code goes to endless cycle even if I comment-out those two lines related to the second row but try to interact with any cell of second row!

 

What I'm missing?

Please vote for Inventor-Idea Text Search within Option Names

0 Likes
951 Views
4 Replies
Replies (4)
Message 2 of 5

Maxim-CADman77
Advisor
Advisor

I've managed to get a bit closer to what I want with approach of disabling existing checkbox with the code based on CellPainting Event handle:

 

Option Explicit On

AddReference "System.Data"
AddReference "System.Drawing"
AddReference "System.Xml"
Imports System.Data
Imports System.Drawing
Imports System.Windows.Forms


Class AAA

Friend WithEvents oDGV As New DataGridView

Dim sHeader1 As String = "Name"
Dim sHeader2 As String = "Option1"
Dim sHeader3 As String = "Option2"


Sub Main

Dim oDT As DataTable = New DataTable

oDT.Columns.Add(sHeader1, GetType(String))
oDT.Columns.Add(sHeader2, GetType(Boolean))
oDT.Columns.Add(sHeader3, GetType(Boolean))

Dim names = New List(Of String)
names.Add("A1")
names.Add("A2")

Dim opt1vals = New List(Of String)
opt1vals.Add(False)
opt1vals.Add(True)

Dim opt2vals = New List(Of String)
opt2vals.Add(True)
opt2vals.Add(False)

Dim i As Integer = 0
For Each name in Names
	oDT.Rows.Add(oDT.Columns.Count)
	oDT.Rows(i)(sHeader1) = name
	oDT.Rows(i)(sHeader2) = opt1vals(i)
	oDT.Rows(i)(sHeader3) = opt2vals(i)
	i +=1
Next


Dim oDV As DataView = oDT.DefaultView

With oDGV
	.AllowUserToAddRows = False
	.AutoSize = True
	.DataSource = oDV
End With


Dim oForm As New Form
With oForm
	.AutoSize = True
	.Controls.Add(oDGV)
End With

oForm.ShowDialog()

End Sub


Sub oDGV_CellPainting(ByVal sender As Object, ByVal e As DataGridViewCellPaintingEventArgs) Handles oDGV.CellPainting
	logger.info(1)

	' If e.RowIndex = 0 And e.ColumnIndex = 1 ' sHeader2
		' Dim cell1 As DataGridViewCell = oDGV.Rows(0).Cells(sHeader2)
		' Dim chkCell1 As DataGridViewCheckBoxCell = TryCast(cell1, DataGridViewCheckBoxCell)
		' chkCell1.Value = False
		' chkCell1.FlatStyle = FlatStyle.Flat
		' chkCell1.Style.ForeColor = Color.DarkGray
		' cell1.ReadOnly = True
	' End If

	If e.RowIndex = 1 And e.ColumnIndex = 2 
		Dim cell2 As DataGridViewCell = oDGV.Rows(1).Cells(sHeader3)
		Dim chkCell2 As DataGridViewCheckBoxCell = TryCast(cell2, DataGridViewCheckBoxCell)
		chkCell2.Value = False
		chkCell2.FlatStyle = FlatStyle.Flat
		chkCell2.Style.ForeColor = Color.DarkGray
		cell2.ReadOnly = True
	End If

End Sub

End Class

 

 

Now I can clear disable checkboxes not only in the first row.

CheckBox-Chart-with-Disabled-Instances.pngCheckBox-Chart-with-Disabled-Instances.png

But I still worry about the fact that the sub is called 24 times for single cell and get to endless cycle (at least without crashing) if un-comment the code lines related to the checkbox in a first row ...

 

... I believe it will crash on real check box chart with dozens checkboxes.

 

Any ideas?

Please vote for Inventor-Idea Text Search within Option Names

0 Likes
Message 3 of 5

Maxim-CADman77
Advisor
Advisor

Ok endless loop seems to be triggered with cell value line(s):

 

 

chkCell1.Value = True

 

 

 

Event Handler Sub shouldn't contain lines changing cells' values.

 

I still don't understand why the sub is triggered so many times yet now I got more-or-less what I need without endless cycle.

 

PS:

I'll keep the thread open for some time in case somebody want to share a better solution.

Please vote for Inventor-Idea Text Search within Option Names

0 Likes
Message 4 of 5

Maxim-CADman77
Advisor
Advisor

The form I'm working on will have much more options than names thus I decided to optimize it a bit - swap rows with columns (AFAIK such table transformation is called "Pivot") but got to some re-paint issue that I don't understand how to overcome.

My current code is:

Option Explicit On

AddReference "System.Data"
AddReference "System.Drawing"
AddReference "System.Xml"
Imports System.Data
Imports System.Drawing
Imports System.Windows.Forms


Class UserSetsForm

Friend WithEvents oDGV As New DataGridView

Dim sHeader1 As String = "   "
Dim sHeaderIPT As String = "*.ipt"
Dim sHeaderIAM As String = "*.iam"
Dim sHeaderIDW As String = "*.idw"

Sub Main

Dim optNames = New List(Of String)

optNames.AddRange({"00-AAA", "2D-AAA", "3D-AAA", "00-ABC", "2D-ABC", "3D-ABC"})

Dim oDT As DataTable = dataTableVERT(optNames)
' Dim oDT As DataTable = dataTableHOR(optNames)

Dim oDV As DataView = oDT.DefaultView

With oDGV
	.AllowUserToAddRows = False
	.AutoSize = True
	.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells
	.BackgroundColor = Drawing.Color.White
	.DataSource = oDV
End With

Dim oForm As New Form
With oForm
	.AutoSize = True
	.Controls.Add(oDGV)
End With

oForm.ShowDialog()

End Sub


Function dataTableVERT(optNames As List(Of String)) As dataTable
	Dim oDT_V As DataTable = New DataTable

	oDT_V.Columns.Add(sHeader1, GetType(String))
	oDT_V.Columns.Add(sHeaderIPT, GetType(Boolean))
	oDT_V.Columns.Add(sHeaderIAM, GetType(Boolean))
	oDT_V.Columns.Add(sHeaderIDW, GetType(Boolean))

	Dim i As Integer = 0
	For Each cn in optNames
		oDT_V.Rows.Add(oDT_V.Columns.Count)
		oDT_V.Rows(i)(sHeader1) = cn
		i +=1
	Next

	Return oDT_V
End Function


Function dataTableHOR(optNames As List(Of String)) As dataTable
	Dim oDT_H As DataTable = New DataTable

	oDT_H.Columns.Add(sHeader1, GetType(String))

	For Each cn in optNames
		oDT_H.Columns.Add(cn, GetType(Boolean))
	Next

	oDT_H.Rows.Add(oDT_H.Columns.Count)
	oDT_H.Rows(0)(sHeader1) = sHeaderIPT
	oDT_H.Rows.Add(oDT_H.Columns.Count)
	oDT_H.Rows(1)(sHeader1) = sHeaderIAM
	oDT_H.Rows.Add(oDT_H.Columns.Count)
	oDT_H.Rows(2)(sHeader1) = sHeaderIDW

	Return oDT_H
End Function


Sub ChkBoxDisable(cell As DataGridViewCell)
	Dim chkCell As DataGridViewCheckBoxCell = TryCast(cell, DataGridViewCheckBoxCell)
	chkCell.FlatStyle = FlatStyle.Flat
	chkCell.Style.ForeColor = Color.DarkGray
	cell.ReadOnly = True
End Sub


Sub oDGV_CellPainting(ByVal sender As Object, ByVal e As DataGridViewCellPaintingEventArgs) Handles oDGV.CellPainting

	''' Disable choosing non-applicable check-boxes
	If e.RowIndex >= 0 And e.ColumnIndex >= 1

		Dim row As DataGridViewRow = oDGV.Rows(e.RowIndex)
		Dim column As DataGridViewColumn = oDGV.Columns(e.ColumnIndex)

		Select Case True

			''' CASES-for-VERTICAL-table

			Case row.Cells(sHeader1).Value.StartsWith("2D-") And (column.HeaderText = sHeaderIPT OR column.HeaderText = sHeaderIAM)
				ChkBoxDisable(oDGV.Rows(e.RowIndex).Cells(sHeaderIAM))
				ChkBoxDisable(oDGV.Rows(e.RowIndex).Cells(sHeaderIPT))

			Case row.Cells(sHeader1).Value.StartsWith("3D-") And (column.HeaderText = sHeaderIDW)
				ChkBoxDisable(oDGV.Rows(e.RowIndex).Cells(sHeaderIDW))


			''' CASES-for-HORIOZONTAL-table (??? poor-painting ???)

			Case column.HeaderText.StartsWith("2D-") AndAlso (row.Cells(sHeader1).Value = sHeaderIPT OR row.Cells(sHeader1).Value = sHeaderIAM)
				ChkBoxDisable(oDGV.Rows(sHeaderIPT).Cells(e.ColumnIndex))
				ChkBoxDisable(oDGV.Rows(sHeaderIAM).Cells(e.ColumnIndex))

			Case column.HeaderText.StartsWith("3D-") AndAlso row.Cells(sHeader1).Value = sHeaderIDW
				ChkBoxDisable(oDGV.Rows(sHeaderIDW).Cells(e.ColumnIndex))

		End Select
	End If

End Sub

End Class

 

If DataTable is created as Vertical (line 26) then I get 100% OK result:

Vert-Table-OK.pngVert-Table-OK.png

 

But if DataTable is created Horizontal/"Pivoted" (line 27) then most cells got stuck until hovered with mouse pointer (but even re-drawed with pointer the disabled checkboxes are not shown):

Hor-Table-Cell-Pint-FAILURE.pngHor-Table-Cell-Pint-FAILURE.png

 

PS:
Dear @MjDeck I'm very sorry for disturbing you but, I understand that this thread is not Inventor-related but kindly hope you can look into and comment on this.

Please vote for Inventor-Idea Text Search within Option Names

0 Likes
Message 5 of 5

MjDeck
Autodesk
Autodesk

Hi Maxim - the CellPainting event is not a good place to do this. That's more suited for customizing the appearance of the cell. 
Here's a new version of your rule. This sets the read-only state with the same logic that you were using in oDgv_CellPainting. But it does it in the Form.Shown event. The system will call that only once, and it will call it after the DataGridView has been populated. So that's a good time to set some cells to read-only.
I also made a small change to the Rows.Add() lines. You were calling them with the columns count as an argument. I think that value is ignored, so I took it out just to make the code more clear. Let me know if I missed something here. 


Mike Deck
Software Developer
Autodesk, Inc.