How to load data into a combo box

I decided to take one of my apps to the next level and include an auto-complete function using the contents of a text file. Then I realized, I’d never implemented a combo box before.

I was unable to find anything new (asoc) regarding combo boxes. I certainly don’t mind doing the research myself if I can find it somewhere! Could I get some info on how to do this or help finding where I can find out?

Most of what you need to know is in the NSComboBox class reference. You need to use setCompletes: to turn on auto-completion, and you can populate it via a data source, via bindings, or directly.

Which do you think is the easiest method? Bindings seem easy but really all I’ve used them for is binding to Shared User Defaults. It’s not that easy, is it?

I’m a huge fan of bindings. The only downside is that errors – usually typos – can be hard to track down, but I have a bind-one, test-one habit that catches mistakes as I make them. They really are simple.

If you’re only setting the values once, though, addItemsWithObjectValues: is also pretty easy. Using a delegate is more work, IMO.

I’m completely different. Using the normal methods is better, in my opinion, because bindings’ values can be altered while in an array compared to text.

Well, this is certainly an interesting topic to me. Let me explain my particular situation. It’s a basic data post program. User fills in two forms, it gets posted via curl to my website, data goes into database.

I thought it’d be nice if if would save project names so if employee #1 works with a particular client again, they’re already listed in their local project list. That’s basically why I was asking if Bindings would be easier. The data isn’t sensitive or confidential or anything. I don’t care if it’s modified or anything. They click submit and that clients name gets added to the combos data source or binding.

So do you think it’d be worth the work to implement that (in my eyes, it’s a lot of work mainly because I don’t understand data source implementation very well)? Most cases I’d probably agree with you. With a physical data source, the data is safer, less likely for it to just disappear one day.

I agree 100% but one thing I’m struggling with for some reason is how to actually implement these things. I was able to check “Autocompletes” in IB, I set a manual list to test and all worked but I’m having trouble understanding how to take it from apple docs in ObjC to ASOC. I’m assuming it would be something like:

comboBox's addItemsWithObjectValues_(variableNameWithData)

Something along those lines?

Thanks again for the help and thoughts.

I’m not really sure what that means.

Just because you use bindings doesn’t mean there’s no “physical” source – whichever you use, you’ll still be responsible for saving the data to disk if you want to, unless you bind to user defaults.

Yes, except that the parameter will have to be a list (or use addItemWithObjectValue_ instead).

But using bindings frees you from worrying about the interface. You have a property, let’s call it choiceList, and you bind the Content Values of the combo box to it. Then you just deal with the variable, and let the interface look after itself. So if you want to add an item, you just use:

set my choiceList to my choiceList as list & {"Extra choice"}

The only gotchas are that you have yo use “my” when setting the value, you can’t use shortcuts like “set end of…”, and you usually have to use coercion when you get the value (you don’t in this case, but it’s a good habit to get used to).

You might also have another property, let’s say myChoice, and bind the combo box’s Value to it, clicking on Continuously Update Value. Then myChoice will contain whatever is entered in the combo box. So after the user clicks OK or whatever, you might want to add the current text to the list like this:

set my choiceList to my choiceList as list & {myChoice as text}

I’ve implemented this and when I hit enter (connected to saveComboInfo below) I am receiving an error:
Can’t make «class ocid» id «data kptr00000000209B5A0002000000» into type text. (error -1700)


script ComboBoxTutAppDelegate
	property parent : class "NSObject"
	property comboBox : missing value
	property comboBoxForm : missing value
	property saveButton : missing value
	
	on applicationWillFinishLaunching_(aNotification)
		set my comboBox to {"Test 1"} as list -- set list
		set my comboBox to (my comboBox as list) & {"Test 2"} -- add to that list
	end applicationWillFinishLaunching_

	on saveComboInfo_(sender)
		log "displaying newest entry"
		display dialog comboBoxForm's stringValue() as text -- show me stringValue of comboBoxForm
		
		log "adding to list"
		set my comboBox to (my comboBox as list) & {comboBoxForm as text} -- add stringValue of comboBoxForm to list
		
	end saveComboInfo_
	
end script

If I edit the last line to read {comboBoxForm as list} it goes thru with no errors but I’m still getting no list in my combo box.

I have connect from my App Delegate to comboBox and comboBoxForm (selected for comboBox, clicked again for comboBoxForm) and all of the outlets are set as well. Man, when I finally grasp this, ASOC is going to be many levels above AS Studio!

I honestly appreciate your help.

When you say “connect”, do you mean clicked on them, gone to the Bindings panel of the Inspector, and entered the names for Model Key Path?

And you’ll probably want to do something like this to avoid duplicates:

   if comboList does not contain (comboEntry as text) then
       set my comboList to (my comboList as list) & {comboEntry as text} 
   end if

That, too. I was referring to me actually connecting the IB outlets (Ctl+drag)

That doesn’t sound right – you can’t use a property for bindings and as an outlet.

Uh oh, I missed something then. I hate asking a question like this but if I don’t connect it, how do I access it in Xcode? I thought all GUI elements you want to interact with had to be connected? Let me be clear, you are miles ahead of me so I am not questioning your knowledge!

A connection is where you link a property to some view so that you can refer to it in code – you make the property an IBOutlet. Cocoa bindings are completely separate: they involve binding a property to an attribute of a view by entering the property name as Model Key Path in the relevant section of the Bindings panel of the Inspector. One property can’t server both roles.

So if you have a property called someText and you bind a text field’s Content to someText, whenever you change the value of someText (you have to use “my someText”), the value in the field will change – and vice versa. No need for any separate IB connection. This means that in many cases you can use bindings instead of connections.

Oh my gosh, I didn’t know you couldn’t use them for both. Guess what happened when I removed the connections? IT WORKED!

So for the sake of making this thread useful, here is my final script, at least the relevant parts (don’t forget error correction!):


script ComboBoxTutAppDelegate
	property parent : class "NSObject"
	property NSUserDefaults : class "NSUserDefaults" of current application
	property comboBoxText : missing value
	property comboBoxList : missing value
	property saveButton : missing value
	
	on applicationWillFinishLaunching_(aNotification)
		tell NSUserDefaults
			tell its standardUserDefaults()
				set comboBoxList to its objectForKey_("comboBoxList") --set variable to list according to plist
				my setComboBoxList_(comboBoxList)
				set comboBoxText to its objectForKey_("comboBoxText") --set variable to last string entered in combo's text field
				my setComboBoxText_(comboBoxText)
			end tell
		end tell
	end applicationWillFinishLaunching_
	
	on applicationShouldTerminate_(sender)
		return current application's NSTerminateNow
	end applicationShouldTerminate_
	
	on saveComboInfo_(sender)
		tell NSUserDefaults
			tell its standardUserDefaults()
				set comboBoxText to its objectForKey_("comboBoxText") -- update variable to current data in field
				my setComboBoxText_(comboBoxText)
				set my comboBoxList to (my comboBoxList as list) & {comboBoxText as text} -- update list to include new field
				its setObject_forKey_(comboBoxList, "comboBoxList")
			end tell
		end tell
		log "----- added to list -----"
	end saveComboInfo_
end script

Now, are there any shortcuts from here or am I pretty set?

Thanks again for your help, Shane. I can finally put this to use! Crap, I’m gonna be coding all night long now…:slight_smile:

Um, yes :slight_smile: Your set calls are largely redundant. So for example this:

               set comboBoxList to its objectForKey_("comboBoxList") --set variable to list according to plist
               my setComboBoxList_(comboBoxList)

really only needs to be this:

               set my comboBoxList to its objectForKey_("comboBoxList") --set variable to list according to plist

The binding takes care of the rest.

Nice tip. I got two more questions for you.

  1. How do I remove an entry (or can I)?
  2. Do bindings work as easily with tables as well?

Thanks

I’m not sure – but there should be no need.

Yes, if a bit differently. You want your data as records or objects for each row, and you use an NSArrayController as intermediary – you bind it’s contents to the variable that holds the data, and you bind the content of individual table columns to the array controller.