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.
Related
I have the following scenario in a NSoutlineView:
ParentObject [checkbox]
- ChildObject 1 [checkbox]
- ChildObject 2 [checkbox]
Each checkbox has a binding set up to a bool value of the respective object in a NSTreeController. When a user selects the parentObject checkbox, the respective children checkboxes should also be set. However, when a child object checkbox is set, the parent checkbox should not be affected. I cannot seem to get the parent functionality working properly.
My current attempted solution to the problem is:
when the checkbox is set call:
-(IBAction)CheckSelected:(NSButtonCell *)sender
{
// Somehow access the cells bound object in the NSTreeController ?????
}
However from my research I have not been able to find a way to get access to the cell's respective object in the NSTreeController.
Any insight on the problem would be greatly appreciated. I feel like this is a common problem that people would run into using an NStreeController and I am curious if I am taking the proper approach.
Thanks :)
The checkboxes shouldn’t be set up to call an action—they should be bound to a property, like, say, “isChecked”.
In your ParentObject you’ll have code similar to:
- (void)setIsChecked:(BOOL)isChecked;
{
_isChecked = isChecked;
for (ChildObject *childObject in self.children)
childObject.isChecked = isChecked;
}
Since the children’s checkboxes are also bound, the children’s new state will be reflected in the outline view immediately.
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)
I have a set of groups of different types of item, all inheriting form a common base type (ItemBase). Each of my groups has an Items collection of type ObservableCollection<ItemBase>.
In my SemanticZoom.ZoomedInView, I can set a DataTemplateSelector, and in SelectTemplateCore() I can cast the item parameter to detrmine which template to apply.
In my zoomed out view, though, the objects are passed around as DependencyObjects, and I can't for the life of me figure out how I can take the data passsed in to determine which template to use.
To set the items source of the GridView in the zoomed out view, I use
(semZm.ZoomedOutView as ListViewBase).ItemsSource = this.groupedItemsViewSource.View.CollectionGroups;
as this appears to be the only way to get the zoomedin and zoomedout views to synchronise (when you click on a group in the zoomed out view it should take me to the appropriate place in the zoomedinview to see that group's detail.
So, am I missing something obvious in terms of getting the actual group in SelectTemplateCore(), or failing that is there a better way of binding my ItemsSource of the ZoomedOutView?
For the appropriate way to handle casting of the DependencyObject to a usable type, see the answer to This question.
Essentially, cast it to ICollectionViewGroup to access the members.
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.
I am using a TreeView with a ListStore as model. When the user clicks on a row I want to take some action but not using the values in the cells, but using the data I created the row from...
Currently I have the TreeView, the TreeModel (ListStore) and my own data (which I ironically call model)..
So the Questions are:
Is it "right" to have a model - an object representation of the data I want to display and fill a ListStore with that data to display in a TreeView, or would it be better to implement an own version of TreeModel (wrapping my data-model) to display the data?
And also:
If someone double-clicks in a row I can get the RowActivated event (using C#/Gtk#) which provides a Path to the activated row. With that I can get a TreeIter and using that I can get the value of a cell. But what is the best practice to find the data object from which the row was constructed in the first place?\
(Somehow this question got me to the first one - by thinking would getting the data object more easy if I tried to implement my own TreeModel...)
It's quite awkward/difficult to implement TreeModel, so most people simply synch the data from their "real" model into a TreeStore or ListStore.
The columns in the store do not have to match the columns in the view in any way. For example, you can have a column that contains your real managed data objects.
When you add a cellrenderer to a TreeView (visual) column, you can add mappings between its properties and the columns of the store. For example, you could map one store column to the font of a text cellrenderer, and another store column to the text property of the same cellrenderer. Each time the cellrenderer is used to render a particular cell, the mappings will be used to retrieve the values from the store and apply them to the properties of the renderer before it renders.
Here's an example of a mapping:
treeView.AppendColumn ("Title", renderer, "text", 0, "editable", 4);
This maps store column 0 to the renderer's text GTK property and maps store column 4 to the editable property. For GTK property names you can check the GTK docs. Note that the example above uses a convenience method that adds a column, adds a renderer to it and add an arbitrary number of mapping via params. To add mappings directly to a column, for example a column with multiple renderers, pack the renderers into the column then use TreeViewColumn.AddAttribute or TreeViewColumn.SetAttributes.
You can also set up a custom data function that will be used instead of mappings. This allows you to set the properties of the renderer directly, given a TreeIter and the store - so, if all the data you want to display is trivially derived from your real data objects, you could even have your store only contain a single column of these objects, and use data funcs for all the view columns.
Here's an example of a data func that does exactly what the mapping example above does:
treeColumn.SetCellDataFunc (renderer, delegate (TreeViewColumn col,
CellRenderer cell, TreeModel model, TreeIter iter)
{
var textCell = (CellRendererText) cell;
textCell.Text = (string) model.GetValue (iter, 0);
textCell.Editable = (bool) model.GetValue (iter, 4);
});
Obviously data functions are much more powerful because they enable you not only to use properties of more complex GTK objects, but also to implement more complex display logic - for example, lazily processing derived values only when the cell is actually rendered.