How do you correctly use wxListView/Ctr user data?

BlitzMax Modules Forums/Brucey's Modules/How do you correctly use wxListView/Ctr user data?

Hi Brucey :-)

I'm currently struggling with a memory leak in my listbox code. I've narrowed it down to the user data element that may be associated with each list item. Obviously I'm misusing this functionality, as the way I'm doing things causes a very decent leak.

The idea is to associate a String with each listbox cell item. In my app, this string stores the database unique id of the contents of this cell. The question is, how do I correctly do this?

Thanks!

Example code:
SuperStrict

Framework wx.wxApp
Import wx.wxFrame
Import wx.wxListView

Type MyApp Extends wxApp

	Field Window:TWindow
	Field List:TListBox
		
	Method OnInit:Int()
		
		'	Create window
		Window = New TWindow.CreateGadget( "Test Listbox context (object data) leaks" ,,,500,500)
		Window.Centre()	
	
		Repeat
	
			Local temp_rect:TRect = New TRect.Init(50,25,150,400)
			List = New TListBox.CreateGadget(Window,temp_rect)
			
			' Populate the listboxes
			Local s_items:String[] = ["one","two","three","four","five"]
			Local s_static_context:String[] = ["1","2","3","4","5"]
					
			List.Freeze()
			
			Local row:Int
			
			'	Put heaps of items in
			For Local i:Int = 0 Until 100
				For Local item:Int = 0 Until s_items.length
				
					Local s_context:String = "context " +item
				
					' **** PROBLEM IS WITH THE CONTEXT (USER DATA) THIS VERSION LEAKS. The idea is to associate a string with each cell element
				
					List.Add(s_items[item]+" " +String(i) +"_1",s_context, row, 0)
					List.Add(s_items[item]+" " +String(i) +"_2",s_context, row, 1)
					
					' **** THIS VERSION DOES NOT *** OBVIOUSLY I DON'T UNDERSTAND THE WAY OBJECTS ARE BEING HANDLED HERE? :-)
				'	List.Add(s_items[item]+" " +String(i) +"_1",s_static_context[item], row, 0)
				'	List.Add(s_items[item]+" " +String(i) +"_2",s_static_context[item], row, 1)
					
					row :+ 1
				
				Next
			Next
									
			List.SetColumnWidth( 0, wxLIST_AUTOSIZE )
			List.SetColumnWidth( 1, wxLIST_AUTOSIZE )
			List.Thaw()
		
			Window.Show(True)
			Window.Refresh()
			
			List.ClearAll()
			List.Destroy()
			List = Null
	
			Print GCMemAlloced()
		
		Forever
				
		Window.Close()
		Window.Destroy()
		Window = Null
				
		Return True
	
	End Method

End Type

New MyApp.run()

Type TWindow Extends wxFrame

	Method CreateGadget:TWindow(_s_title:String = "TWindow" ,_x:Int = -1,_y:Int = -1,_w:Int = -1,_h:Int = -1,_parent_window:wxWindow = Null , _style:Int = wxDEFAULT_FRAME_STYLE,_b_maxmise:Int = False,_s_description:String = "Window"	)
				
		Create(_parent_window, -1, _s_title, _x, _y, _w, _h, _style)
			
		Return Self
	
	End Method

EndType

Type TListBox Extends wxListView

	Field NumberOfRows:Int
	Field b_Header:Int
	Field s_ColumnTitles:String[] = ["col 1", "col 2"]

	Method Add(_s_row_text:String,_s_context:String = Null, _row:Int, _col:Int, _icon:Int =-1)
	
		
		'	This is the list row data structure - contains Text, Context (Data), and Icon 
		Local item:wxListItem = New wxListItem.Create()
		 
		'	If we haven't inserted an item yet, add column header
		If Not b_Header
		
			'	For each column in the table
			For Local col:Int = 0 Until s_ColumnTitles.length
		
				' Set the column name
				item.SetText(s_ColumnTitles[col])
						
				' Set the column number
				item.SetColumn(col)
												
				' Insert our new column header	
				InsertColumnItem(col, item)
							
			Next
				
			b_Header = True
		
		EndIf
				
		'	Set row text
		item.SetText(_s_row_text)
		
		'	Set the item row
		item.SetId(_row)
		
		
		' ***** THIS IS THE PROBLEM - COMMENT OUT TO PROVE...			
		' 	Please note that client data is associated with the item And Not with subitems.
		item.SetData(_s_context)
		
		'	Which column are we destined for?
		item.SetColumn(_col)
							
		'	Use this to flag we are interested in image, text and data -
		item.SetMask(wxLIST_MASK_FORMAT|wxLIST_MASK_IMAGE|wxLIST_MASK_TEXT | wxLIST_MASK_DATA)
		
		'	Place the item on the listbox - col 0 must always be InsertItem, not SetItem
		If _col  = 0
			 InsertItem(item )
		'	Note we can't use SetItem until we have col 0 in place - app with crash otherwise -InsertItem on a col> 0 does nothing but overwrite col 0
		Else
			SetItem(item)
		EndIf	
		
		NumberOfRows= _row+1
									
					
	End Method

	Method CreateGadget:TListBox(_parent_gadget:wxWindow,_rect:TRect, _style:Int = wxLC_REPORT |  wxLC_SINGLE_SEL)
			
		Create(_parent_gadget, wxID_ANY, _rect.x, _rect.y, _rect.w, _rect.h, _style)
	
		Return Self
	End Method

End Type

Type TRect

	Field x:Int
	Field y:Int
	Field w:Int
	Field h:Int
	
	Method Init:TRect(_x:Int, _y:Int, _w:Int, _h:Int)
		x = _x;y = _y;w = _w;h = _h
		Return Self
	End Method
	
EndType


You always manage to dig out those nasty little bugs, eh? :-)

Should be sorted now - at least... your example doesn't leak any more.

So, what was up?

Well, this is where the leak was occurring :
		' 	Please note that client data is associated with the item And Not with subitems.
		item.SetData(_s_context)
		
		'	Which column are we destined for?
		item.SetColumn(_col)

Note that your initial comment mentions the data is associated only with the item.
Well, you were essentially setting the subitems too. The underlying code didn't care about that, so was ref-counting everything you were setting, even for columns > 0.
When it came time to remove the data, some of those references were not available to release. (they had been replaced by subsequent calls to SetData)

What happens now, when you SetData on an item, it checks the current column. If > 0 it skips it.
However, you SetColumn after setting data... so I also check on SetColumn, if > 0 and data is set, we release it - doesn't belong there!

This *should* mean that only data set on a column of 0 should be valid.

Let me know how you get on with it :-)

Thanks so much Brucey. The fix has stopped the leaks in both the test code in XP and in my app. Very happy!

Bonus plus is I now know what a "subitem" is - I had just copied that comment straight from wxDocs :-)