Binding an NSPopUpButton - objective-c

OK, so here's my situation :
I've got an NSDictionary, let's call it : myItem.
myItem.valueNames contains the values that I want to show up in the popup
myItem.values contains the values the popup must return (e.g. for valueName[0] -> value[0], and so on)
myItem.value contains the current value
How am I suppose to bind that? Even though I've studied the official reference, it still looks a bit obscure...
I'm currently binding :
myItem.valueNames to Content
myItem.values to Content Objects
myItem.value to Selected Value
and... all I've managed is that it shows the valueNames.
Any ideas?

Your model is a little strange to me. I wouldn't use an NSDictionary, I'd use a custom subclass with KVC/KVO compliant properties for each of these. Also, if the name of each value is a property of the value object itself, there's no need for a separate valueNames property. So, with that change, I'd do this:
Bind Content to modelObject with a key path of values
Bind Content Values to modelObject with a key path of values.name
Bind Selected Object to modelObject (or yourControllerObject if that makes more sense) with a key path of value (I'd name it selectedValue)

Related

Binding an NSPopupButton to an NSArrayContraller with NSManagedObject subclasses

There seem to be dozens of questions about binding a NSPopupButton, so I feel a little better about struggling so much with this, but none of them seem to fix my issue.
I have an NSManagedObject subclass that has a one to many relationship with another class. Let's say Foo has a relationship to Bar, so Foo has a property that is an NSSet of Bars.
I have created an NSArrayController and bound its contentSet to the Foo object and the 'bars' key path.
I then bind my NSPopupButton's content to the array controller's arrangedObjects and its contentValues to the array controller's arrangedObject (controller key) and 'name' (the property on Bar that I want to display).
This all works fine so far, but when I try to bind the NSPopupButton's selected object to the array controller's selection The NSPopupButton displays "<_NSArrayControllerObjectProxy". I also tried adding 'name' as the keyPath for this binding, and this does make the NSPopupButton display the name correctly, but then when I change the selection in the popup the app thrown an exception:
Unacceptable type of value for attribute: property = "name"; desired type = NSString; given type = Bar;
I guess this makes sense, as the popup is trying to set the string value of 'name' as the selected Bar. I would think I would therefore need to bind a selected object and a selected value, but the XIB will disable selected value if I have a selected object set.
I have also tried binding the selected value instead, and this half works (the array controller's selection does change) but the options in the popupmenu don't change to show the one that was deselected and hide the newly selected one).
Failing all of this I read an article here: http://blog.chrisblunt.com/cocoa-bindings-and-nspopupbutton/ that says NSPopupButton "NSPopUpButton does not record the user’s selection" and to instead store your selection somewhere other than the array controller. I tried putting a currentBar property in my window and binding the selection to that instead, and although I can see that currentBar is changing (because I have another view bound to it also) the label in the popup button does not change.
If anyone can help me out I'd be very appreciative.
The FooBar thing confuses me so here is my example based on real world objects.
ExpenseTransaction has attributes (date, trxDescription, category, amount).
Category has a single attribute (name)
ExpenseTransaction.category is a To-One relationship to Category (in other words a transaction can belong to only one Category).
Category.transactions is a To-Many relationship to ExpenseTransaction (in other words many transactions can belong to the same Category).
The UI for creating a new transaction or editing and existing one uses NSPopupButton to display the list of available Categories using the name attribute. For existing transactions the popup will display the selected transactions category.
Bindings for the Category popup are as follows:
Content (Category.arrangedObjects)
Content Objects (Category.arrangedObjects) - we want to link to the actual category not its name because the attribute is a relationship not a string value
Content Values (Category.arrangedObjects.name) - we want the name to be displayed in the popup list
Selected Object (ExpenseTransaction.selection.category)
Using your FooBar analogy:
Category has a relationship to ExpenseTransaction, so Category has a property (transactions) that is a NSSet of ExpenseTransactions. Now this is pretty much the inverse of my arrangement so I don't really know how or why you would populate the popup with ExpenseTransaction objects because only one selected item in the popup could be related to the Category object when in fact you need the whole set to be related. However the other way around works just fine because the popup would contain a list of all the Foo items and so whenever you select a Bar item the corresponding Foo item could be selected from the popup.
Hope this makes sense.

What does "this class is not key value coding-compliant" mean?

I'm working my way through Cocoa Programming for Mac OS X (4th Edition), and, like many others, I'm getting stuck on a challenge; specifically, Chapter 8, Challenge 1. Unable to come to the solution on my own, I tracked down a little help from the Life as Clay blog. I added the solution there but I'm getting the following error:
2013-05-09 00:28:49.049 RaiseMan[58078:303] Error setting value for key path sortDescriptors of object <NSArrayController: 0x7fcee2a51900>[object class: RMPerson, number of selected objects: 1] (from bound object <NSTableView: 0x7fcee2a48750>): [<RMPerson 0x7fcee1c5c870> valueForUndefinedKey:]: this class is not key value coding-compliant for the key [personName valueForKeyPath:#"length"].
The thing is, in trying to hunt down what's wrong, I've seen this error crop up all over the place on StackOverflow and I've yet to discern what information it's trying to convey about my project. I can't find any correlation between solutions to understand this error message, so I can't use it to figure out what's going on in my code.
So, long story short, I'm not really looking for help with what is specifically wrong with my code, and more trying to find out what this error message actually means, so I can hunt down the problem myself; however, here's what I'm trying to do anyway. If you can tell me not just what's wrong, but also why this error applies to what I'm doing, that would be great.
In this particular instance, I have a TableView bound to an ArrayController. The ArrayController is bound to File's Owner, with employees (an NSMutableArray) as its Model Key Path. employees is an array of RMPerson objects, each of which has two properties: personName (an NSString) and expectedRaise (a float). The first column of the table shows personName from the respective RMPerson; I am attempting to sort the first column using the length of personName as per the challenge. When I sort it using personName as the sort key and caseInsensitiveCompare: as the selector, all is well. This error crops up when I use either [personName valueForKeyPath:#"length"] or personName.length as the sort key and compare: as the selector.
What does “this class is not key value coding-compliant” mean?
Nothing.
The full exception message makes the question more meaningful:
What does “this class is not key value coding-compliant for <key>” mean?
It means that objects of that class do not have a property named <key>, at least as far as Key-Value Coding is concerned, so you can't use KVC to get a value for such a property from them.
Bindings, of which NSArrayController is a vital part, uses KVC to get and set both bound properties and the properties you've bound them to, so trying to bind something to a property that doesn't exist will get you this exception.
You've got a special case of this, though:
…RMPerson …: this class is not key value coding-compliant
for the key [personName valueForKeyPath:#"length"].
What this means is that you tried to use “[personName valueForKeyPath:#"length"]” as a key.
Strictly speaking, KVC will accept as a key pretty much anything that doesn't have a period in it, but in practice, very few objects recognize any key that isn't an identifier, and all of them (except NSDictionary) expect keys to at least be single words. An Objective-C message expression is not a valid key and cannot be part of a key path.
I'm guessing that you actually pasted “[personName valueForKeyPath:#"length"]” into a Model Key Path field in your nib—for an array controller's sortDescriptors binding, from the looks of it. That won't work. The Model Key Path field expects the key path itself, alone.
personName.length would be a valid key path, but assuming that your RMPersons' names are strings, that isn't going to get you an array of sort descriptors, so you can expect the array controller to choke on the array of numbers.
Whatever you're trying to do (sort by length of name?), you should ask another question about that.
When I sort it using personName as the sort key and caseInsensitiveCompare: as the selector, all is well. This error crops up when I use either [personName valueForKeyPath:#"length"] or personName.length as the sort key and compare: as the selector.
The selector isn't important.
Pay attention to whether something expects a key path or just a key—the distinction is subtle, but app-breakingly important. A sort descriptor requires a single key.
It means you are trying to access/set the value of the object by it's key but that it doesn't have any key-value pair.
In your case, you are trying to set the value of an NSMutableArray by it's key, which is not possible.
So , if you found this "Class is not coding value complient" strange error with some IBOutlet variable name not present in your Project, Try Delete app and Reinstall it Again on Simulator.
Worked for Me!

How do I use a GtkComboBox with objects, as opposed to strings?

The usual use for a combo box is to let it display options to the user, and then you get an OBJECT out of it. In Win32, you do it by using the CB_SETITEMDATA and CB_GETITEMDATA messages, casting between int and object pointers. In XAML, you set up a data template and the item in the list IS the object.
What is the Correct way to get this effect with a GtkComboBox?
GtkComboBox normally uses a GtkListStore as the underlaying model.
You need to create one with an extra column for the object you want to store and as you insert new items in the combo's model you also need to provide the object you want to associate with that row/item.

List View Item Deleting parameters not passed to Object Data source

I have a ListView bound to an ObjectDataSource, I'm passing some custom parameters to the Insert and Update methods on my bound class methods by adding them to the event.Values map in the ListView ItemInserting/ItemUpdating events.
However when I try to do the same thing on the ItemDeletingEvent the additional parameters do not seem to be passed to the datasource ( If I register a listener for ObjectDataSource.ItemSourceDeleting I only see one parameter, effectively the 'id' of the row).
Is this an expected behavior? I can't see anything in the documentation that indicates as such.
I found a solution -
I Added a 'DeleteParameter' value with the same name as my desired 'custom' parameter to the ObjectDataSource declaration.
Then in the ItemDeleting Event get the ObjectDataSource.DeleteParameters["myparam"] and set the DefaultValue property. Seems like a hack, but it does work.

Reusing property editors for Blend 4

I have a custom silverlight control, which exposes a property with DataGridLength type. Now I want that property to have the same editor as a common DataGridColumn's Width property, with the combobox and everything, like this:
instead, I only get a simple TextBox, with "Auto" written in, with no way to set to SizeToCells and so on.
I assume I need a DesignTime attribute, but none of the ones I found in ComponentModel namespace even came close...
I guess you just have to create an Enum with the all the values autorized (Pixels, SizeToCells, etc ...), you that Enum as the type of your property DataGridLength and then, in the code of your control, take the corresponding action regarding the value sent.