wxListCtrl and ClientData

BlitzMax Modules Forums/Brucey's Modules/wxListCtrl and ClientData

Here is a stripped down version of a program i am working on.
There is a GUI file and a Main file.

The main file creates a sqllite database and adds some tables and data.

The first table (MainTask) is read by TMainTask and a list is returned to the frame to be populated in the topmost ListCtrl (and stored as ClientData). I have a listbox example that works fine but the difference is the first item in the listbox is the primary key i need for the next lookup.

I am displaying the data in a different order to what is in the table. The table has a Primary Key of MainTaskID but this is not in the first column. This might be my problem and if so i guess i can use it in column 1 but give it a zero width.

Once i click one of these items in the ListCtrl it is supposed to pass the correct MainTaskID to the TSubTask.GetListForTypeId function so the relevant Sub Tasks can be returned and displayed in the second ListCtrl.

Unfortunately only the MainTaskID = 1 SubTask is returned and displayed.

I haven't got the hang of this client data for the listctrl yet.

The code looks quite messy but this is because i normally have 3 sets of files: GUI, DataBase Types and the Main File.
That is why i am passing values around so much.

Any help appreciated.

Cheers
Glenn

ListCtrlClientDataGUI.bmx
'
' BlitzMax code generated with wxCodeGen v1.02 : 10 Mar 2008 22:34:08
' 
' 
' PLEASE DO "NOT" EDIT THIS FILE!
' 
SuperStrict

Import wx.wxFrame
Import wx.wxListCtrl
Import wx.wxPanel
Import wx.wxWindow


Type MyFrame2Base Extends wxFrame

	Field m_panel5:wxPanel
	Field m_listCtrl1:wxListCtrl
	Field m_listCtrl2:wxListCtrl


	Method Create:MyFrame2Base(parent:wxWindow = Null,id:Int = wxID_ANY, title:String = "", x:Int = -1, y:Int = -1, w:Int = 787, h:Int = 328, style:Int = wxDEFAULT_FRAME_STYLE|wxTAB_TRAVERSAL)
		Return MyFrame2Base(Super.Create(parent, id, title, x, y, w, h, style))
	End Method

	Method OnInit()

		Local bSizer11:wxBoxSizer
		bSizer11 = New wxBoxSizer.Create(wxVERTICAL)
		m_panel5 = New wxPanel.Create(Self, wxID_ANY,,,,, wxTAB_TRAVERSAL)

		Local bSizer12:wxBoxSizer
		bSizer12 = New wxBoxSizer.Create(wxVERTICAL)
		m_listCtrl1 = New wxListCtrl.Create(m_panel5, wxID_ANY,,,,, wxLC_REPORT)
		m_listCtrl1.SetMinSize(700,-1)
		bSizer12.Add(m_listCtrl1, 1, wxALL, 5)

		m_listCtrl2 = New wxListCtrl.Create(m_panel5, wxID_ANY,,,,, wxLC_REPORT)
		m_listCtrl2.SetMinSize(700,-1)
		bSizer12.Add(m_listCtrl2, 1, wxALL, 5)

		m_panel5.SetSizer(bSizer12)
		m_panel5.Layout()
		bSizer11.Add(m_panel5, 1, wxEXPAND | wxALL, 5)

		SetSizer(bSizer11)
		Layout()

		m_listCtrl1.ConnectAny(wxEVT_COMMAND_LIST_ITEM_SELECTED, _ListCtrl1Clicked, Null, Self)
	End Method

	Function _ListCtrl1Clicked(event:wxEvent)
		MyFrame2Base(event.sink).ListCtrl1Clicked(wxListEvent(event))
	End Function

	Method ListCtrl1Clicked(event:wxListEvent)
		DebugLog "Please override MyFrame2.ListCtrl1Clicked()"
		event.Skip()
	End Method

End Type


MainFile
'
' Example application stub generated by wxCodeGen v1.02 : 10 Mar 2008 21:41:30
'
SuperStrict

Framework wx.wxApp

Import "ListCtrlClientDataGUI.bmx"
Import BaH.DBSQLite
Import brl.filesystem
Import brl.system

Const cListCtrlDateWidth:Int = 80

New MyApp.run()

Type MyApp Extends wxApp

	Field frame:MyFrame2
	
	Method OnInit:Int()
?macos
		' make the default Mac font for controls not so big
		wxSystemOptions.SetOption(wxWINDOW_DEFAULT_VARIANT, wxWINDOW_VARIANT_SMALL)
?
		' needed so my graphics show up
		wxInitAllImageHandlers()
		' for Mac/Win32 (it is default on Linux)
		wxImage.AddHandler( New wxXPMHandler )
		' create the frame
		Local frame:MyFrame2 = MyFrame2(New MyFrame2.Create(, , "ListCtrl Client Data Test"))
		' frames are created hidden. You need to manually show it.
		frame.show()
		' make sure it is brought to the front
		SetTopWindow(frame)

		Return True
	End Method
	
End Type



Type MyFrame2 Extends MyFrame2Base

	Method OnInit()
		Super.OnInit()
		' There are 2 list controls. Create the Column Headers here
		CreateListCtrlColumnHeaders()
		' populate the Due Date ListCtrl
		PopulateListCtrl(m_listCtrl1)		
		
	End Method

	Method CreateListCtrlColumnHeaders()
		' do the First List Control
		SetColumnHeadings(m_listCtrl1)
		' do the Second List Control
		SetColumnHeadings(m_listCtrl2)
	End Method

	Method SetColumnHeadings(ListControl:wxListCtrl)
		Select ListControl
			Case m_listCtrl1
				ListControl.InsertColumn(0, ("TM"), wxLIST_FORMAT_LEFT, 30)	' Team Member Initials
				ListControl.InsertColumn(1, ("Created"), wxLIST_FORMAT_LEFT, cListCtrlDateWidth)
				ListControl.InsertColumn(2, ("Due Date"), wxLIST_FORMAT_LEFT, cListCtrlDateWidth)
				ListControl.InsertColumn(3, ("Completed"), wxLIST_FORMAT_LEFT, cListCtrlDateWidth)
				ListControl.InsertColumn(4, ("Cancelled"), wxLIST_FORMAT_CENTRE, cListCtrlDateWidth)
				ListControl.InsertColumn(5, ("Main Task Name"), wxLIST_FORMAT_LEFT,200)
			Case m_listCtrl2
				ListControl.InsertColumn(0, ("TM"), wxLIST_FORMAT_LEFT, 30)	' Team Member Initials
				ListControl.InsertColumn(1, ("Created"), wxLIST_FORMAT_LEFT, cListCtrlDateWidth)
				ListControl.InsertColumn(2, ("Due Date"), wxLIST_FORMAT_LEFT, cListCtrlDateWidth)
				ListControl.InsertColumn(3, ("Completed"), wxLIST_FORMAT_LEFT, cListCtrlDateWidth)
				ListControl.InsertColumn(4, ("Cancelled"), wxLIST_FORMAT_CENTRE, cListCtrlDateWidth)
				ListControl.InsertColumn(5, ("Sub Task Name"), wxLIST_FORMAT_LEFT,200)
			Default
				DebugLog "You have passed a ListCtrl that doesn't exist"
		End Select
	End Method

	Method PopulateListCtrl(ListControl:wxListCtrl)
		' delete the Items, so the column headers don't need to be recreated
		ListControl.DeleteAllItems()
		' check the ListCtrl exists
		If ListControl Then
			' Get the data for the MainTask ListCtrl (using the id) from TSelector
			Local list:TList = TMainTask.GetListForTypeId()
			' i is the row number in the ListCtrl
			Local i:Int = 0	
			For Local dataset:TMainTask = EachIn list
				' InsertIntoList(Row Number, the data for this element of the list, the name of the ListCtrl)
				MTInsertIntoList(i, dataset, ListControl)
				' Increment i so you are always adding to the end of the ListCtrl
				' comment this line out if you want your listctrl populated in reverse, ie i = 0
				i:+ 1
			Next
		End If
	End Method

	Method MTInsertIntoList(index:Int, dataSet:TMainTask, list:wxListCtrl)
		' wxListItem - This type stores information about a wxListCtrl item or column.
		' Some comments taken from <a href="http://www.gibbongames.com/content/view/52/50/" target="_blank">http://www.gibbongames.com/content/view/52/50/</a>
		' wxListItem encapsulates the contents of a given listbox cell.
		' So each row/column combination has its own wxListItem structure.
		Local item:wxListItem = New wxListItem.Create()
		' Define all the parameters for the column, then use list.SetItem(item) to bind it
		' Column 0
		item.SetImage(-1)
		item.SetText(dataSet.TMID)
		item.SetId(index)
'******************* set the Client Data???? so it can be used in "Method MainTaskSelected(event:wxListEvent)"
		item.SetData(dataSet)
		' Apparently Column 0 requires InsertItem, but Column n > 0 requires SetItem (assuming that Column 0 has been previously inserted).
		list.InsertItem(item)
		' Column 1
		item.SetText(dataSet.MTCreateDate)
		item.SetColumn(1)
		list.SetItem(item)
		' Column 2
		item.SetText(dataSet.MTDueDate)
		item.SetColumn(2)
		list.SetItem(item)
		' Column 3
		item.SetText(dataSet.MTCompletedDate)
		item.SetColumn(3)
		list.SetItem(item)
		' Column 4
		If dataSet.MTCancelled = 1 Then
			item.SetText("Yes")
		Else
			item.SetText("No")
		End If
		item.SetColumn(4)
		list.SetItem(item)
		' Column 5
		item.SetText(dataSet.MTName)
		item.SetColumn(5)
		list.SetItem(item)
	End Method

	Method ListCtrl1Clicked(event:wxListEvent)
		DebugLog "ListCtrl1Clicked"
		' once the MainTask ListCtrl is clicked we need to populate the SubTask ListCtrl
		' Find the index of the item clicked
		Local index:Int = wxCommandEvent(event).GetInt()
		' Get the value of the item clicked
		' Note: This is the TMainTask object we stored earlier.
		' It gives us access to the Id, for example, which gives us a quicker
		' lookup in the SQL. (ie. no table joins, since the Id is a foreign key!)
' ***************************** GetItemData(index) looks correct but i keep getting a 1 sent to the PopulateSubTask as the id
		Local sMainTask:TMainTask = TMainTask(m_listCtrl1.GetItemData(index))
		' now fill in the SubTask ListCtrl
		Populate_m_listCtrl2(sMainTask.MainTaskID, m_listCtrl2)
	End Method

	Method Populate_m_listCtrl2(id:Int, ListControl:wxListCtrl)
		' delete the Items, so the column headers don't need to be recreated
		ListControl.DeleteAllItems()
		' check the ListCtrl exists
		If ListControl Then
			' Get the data for the MainTask ListCtrl (using the id) from TSelector
			Local list:TList = TSubTask.GetListForTypeId(id)
			' i is the row number in the ListCtrl
			Local i:Int = 0	
			For Local dataset:TSubTask = EachIn list
				' InsertIntoList(Row Number, the data for this element of the list, the name of the ListCtrl)
				STInsertIntoList(i, dataset, ListControl)
				' Increment i so you are always adding to the end of the ListCtrl
				' comment this line out if you want your listctrl populated in reverse, ie i = 0
				i:+ 1
			Next
		End If
	End Method

	Method STInsertIntoList(index:Int, dataSet:TSubTask, list:wxListCtrl)
		' wxListItem - This type stores information about a wxListCtrl item or column.
		' Some comments taken from <a href="http://www.gibbongames.com/content/view/52/50/" target="_blank">http://www.gibbongames.com/content/view/52/50/</a>
		' wxListItem encapsulates the contents of a given listbox cell.
		' So each row/column combination has its own wxListItem structure.
		Local item:wxListItem = New wxListItem.Create()
		' Define all the parameters for the column, then use list.SetItem(item) to bind it
		' Column 0
		item.SetImage(-1)
		item.SetText(dataSet.TMID)
		item.SetId(index)
'******************* set the Client Data???? so it can be used in "Method MainTaskSelected(event:wxListEvent)"
		item.SetData(dataSet)
		' Apparently Column 0 requires InsertItem, but Column n > 0 requires SetItem (assuming that Column 0 has been previously inserted).
		list.InsertItem(item)
		' Column 1
		item.SetText(dataSet.STCreateDate)
		item.SetColumn(1)
		list.SetItem(item)
		' Column 2
		item.SetText(dataSet.STDueDate)
		item.SetColumn(2)
		list.SetItem(item)
		' Column 3
		item.SetText(dataSet.STCompletedDate)
		item.SetColumn(3)
		list.SetItem(item)
		' Column 4
		If dataSet.STCancelled = 1 Then
			item.SetText("Yes")
		Else
			item.SetText("No")
		End If
		item.SetColumn(4)
		list.SetItem(item)
		' Column 5
		item.SetText(dataSet.STName)
		item.SetColumn(5)
		list.SetItem(item)
	End Method
	
End Type





Type TDatabaseTables

	Global db:TDBConnection

	Method Init:TDatabaseTables()
	
		If Not db Then
			DeleteFile("ListCtrlClientData.db")
			DebugLog "Deleted File to start with a fresh one"
			If Not FileType("ListCtrlClientData.db") Then
				CreateDB("ListCtrlClientData.db")
			End If
			db = LoadDatabase("SQLITE", "ListCtrlClientData.db")
		End If
	
		' Check that the DB connection has been made
		If Not db Then
			Notify("Unable to Open the Database ListCtrlClientData.db")
			End
		End If
	
		' Check if the database has an error on opening
		If db.hasError() Then
			errorAndClose(db)
		End If

		Return Self
	End Method
	
	Method CreateDB(filename:String)

		Local dbase:TDBConnection = LoadDatabase("SQLITE", "ListCtrlClientData.db")
		Local s:String
		' Create tblMainTask
		s = "CREATE TABLE tblMainTask (MainTaskID integer primary key AUTOINCREMENT, " + ..
	  		" MTName varchar(100), " + ..
	  		" MTCreateDate varchar(10), " + ..
	  		" MTDueDate varchar(10), " + ..
	  		" MTCompletedDate varchar(10), " + ..
	  		" MTCancelled integer, " + ..
	  		" SelectorID integer, " + ..
			" TMID integer) "
		dbase.executeQuery(s)
		If dbase.hasError() Then
			errorAndClose(dbase)
		End If
		DebugLog "tblMainTask Created"
		' Create tblSubTask
		s = "CREATE TABLE tblSubTask (SubTaskID integer primary key AUTOINCREMENT, " + ..
	  		" STName varchar(100), " + ..
	  		" STOrder integer, " + ..
	  		" STMemo varchar(1000), " + ..
	  		" STCreateDate varchar(10), " + ..
	  		" STDueDate varchar(10), " + ..
	  		" STCompletedDate varchar(10), " + ..
	  		" STLastUpdatedDate varchar(10), " + ..
	  		" STCancelled integer, " + ..
	  		" MainTaskID integer, " + ..
			" TMID integer) "
		dbase.executeQuery(s)
		If dbase.hasError() Then
			errorAndClose(dbase)
		End If
		DebugLog "tblSubTask Created"
		
		' create some data...
		' tblMainTask
		dbase.executeQuery("INSERT INTO tblMainTask values (NULL, 'MainTask One', '01 Jan 2008', '02 Feb 2008', '03 Mar 2008', 0, 1, 1)")
		dbase.executeQuery("INSERT INTO tblMainTask values (NULL, 'MainTask Two', '01 Mar 2008', '02 Feb 2008', '03 Jan 2008', 0, 1, 2)")

		' tblSubTask
		dbase.executeQuery("INSERT INTO tblSubTask values (NULL, 'SubTask One for MainTask One', 1, 'Memo Field','01 Jan 2008', '02 Feb 2008', '03 Mar 2008', '04 Mar 2008', 0, 1, 1)")
		dbase.executeQuery("INSERT INTO tblSubTask values (NULL, 'SubTask One for MainTask Two', 1, 'Memo Field','01 Jan 2007', '02 Feb 2007', '03 Mar 2007', '04 Mar 2007', 0, 2, 2)")
		dbase.executeQuery("INSERT INTO tblSubTask values (NULL, 'SubTask Two for MainTask Two', 1, 'Memo Field','02 Jan 2007', '03 Feb 2007', '04 Mar 2007', '05 Mar 2007', 0, 2, 2)")

		dbase.close()
		
	End Method

	Function errorAndClose(db:TDBConnection)
		DebugLog(db.error().toString())
		db.close()
		End
	End Function
		
End Type


Type TMainTask Extends TDatabaseTables

	Field MainTaskID:Int
	Field MTName:String
	Field MTCreateDate:String
	Field MTDueDate:String
	Field MTCompletedDate:String
	Field MTCancelled:Int
	Field SelectorID:Int
	Field TMID:Int
	
	Method Init:TMainTask()
		Return TMainTask(Super.init())
	End Method

	Function GetListForTypeId:TList()
		Local list:TList = New TList
	
		Local mt:TMainTask = New TMainTask.Init()
		
		If db.isOpen() Then
		
			Local sql:String = "SELECT MainTaskID, MTName, MTCreateDate, MTDueDate, MTCompletedDate, MTCancelled, SelectorID, TMID "
			sql = sql + "FROM tblMainTask "

			' Get the data from the table
			Local query:TDatabaseQuery = db.executeQuery(sql)
			
			' Loop through each record and add the values to the TList
			For Local record:TQueryRecord = EachIn query
				Local MainTask:TMainTask = New TMainTask 
				MainTask.MainTaskID = record.GetInt(0)
				MainTask.MTName = record.GetString(1)
				MainTask.MTCreateDate = record.GetString(2)
				MainTask.MTDueDate = record.GetString(3)
				MainTask.MTCompletedDate = record.GetString(4)
				MainTask.MTCancelled = record.GetInt(5)
				MainTask.SelectorID = record.GetInt(6)
				MainTask.TMID = record.GetInt(7)
				DebugLog "Start of Data for MainTask record"
				DebugLog "MainTaskID = " + MainTask.MainTaskID
				DebugLog "MTName = " + MainTask.MTName
				DebugLog "MTCreateDate = " + MainTask.MTCreateDate
				DebugLog "MTDueDate = " + MainTask.MTDueDate
				DebugLog "MTCompletedDate = " + MainTask.MTCompletedDate
				DebugLog "MTCancelled = " + MainTask.MTCancelled
				DebugLog "SelectorID = " + MainTask.SelectorID
				DebugLog "TMID = " + MainTask.TMID
				DebugLog "End of Data for MainTask record"
				
				list.AddLast(MainTask)
			Next

		EndIf
		
		Return list
	End Function

End Type

Type TSubTask Extends TDatabaseTables

	Field SubTaskID:Int
	Field STName:String
	Field STOrder:Int
	Field STMemo:String
	Field STCreateDate:String
	Field STDueDate:String
	Field STCompletedDate:String
	Field STLastUpdatedDate:String
	Field STCancelled:Int
	Field MainTaskID:Int
	Field TMID:Int
	
	Method Init:TSubTask()
		Return TSubTask(Super.init())
	End Method

	Function GetListForTypeId:TList(id:Int)
		Local list:TList = New TList
	
		Local st:TSubTask = New TSubTask.Init()
		
		If db.isOpen() Then
		
			Local sql:String = "SELECT SubTaskID, STName, STOrder, STMemo, STCreateDate, STDueDate, STCompletedDate, STLastUpdatedDate, STCancelled, MainTaskID, TMID "
			sql = sql + "FROM tblSubTask "
			sql = sql + "WHERE MainTaskID = " + id

			' Get the data from the table
			Local query:TDatabaseQuery = db.executeQuery(sql)
			
			' Loop through each record and add the values to the TList
			For Local record:TQueryRecord = EachIn query
				Local SubTask:TSubTask = New TSubTask 
				SubTask.SubTaskID = record.GetInt(0)
				SubTask.STName = record.GetString(1)
				SubTask.STOrder = record.GetInt(2)
				SubTask.STMemo = record.GetString(3)
				SubTask.STCreateDate = record.GetString(4)
				SubTask.STDueDate = record.GetString(5)
				SubTask.STCompletedDate = record.GetString(6)
				SubTask.STLastUpdatedDate = record.GetString(7)
				SubTask.STCancelled = record.GetInt(8)
				SubTask.MainTaskID = record.GetInt(9)
				SubTask.TMid = record.GetInt(10)
				DebugLog "Start of Data for SubTask record"
				DebugLog "SubTaskID = " + SubTask.SubTaskID
				DebugLog "STName = " + SubTask.STName
				DebugLog "STOrder = " + SubTask.STOrder
				DebugLog "STMemo = " + SubTask.STMemo
				DebugLog "STCreateDate = " + SubTask.STCreateDate
				DebugLog "STDueDate = " + SubTask.STDueDate
				DebugLog "STCompletedDate = " + SubTask.STCompletedDate
				DebugLog "STLastUpdatedDate = " + SubTask.STLastUpdatedDate
				DebugLog "STCancelled = " + SubTask.STCancelled
				DebugLog "MainTaskID = " + SubTask.MainTaskID
				DebugLog "TMID = " + SubTask.TMid
				DebugLog "End of Data for SubTask record"
				
				list.AddLast(SubTask)
			Next

		EndIf
		
		Return list
	End Function

End Type