Question

I've already killed a day on this subject and still got no idea on how could this be done in a correct way.

I'm using NSOutlineView to display filesystem hierarchy. For each row in the first column I need to display checkbox, associated icon and name of the file or directory. Since there's no standard way to make this, I've subclassed NSTextFieldCell using both SourceView and PhotoSearch examples, binding value in IB to name property of my tree item class though NSTreeController. I'm using drawWithFrame:inView: override to paint checkbox and image, forwarding text drawing to super. I'm also using trackMouse:inRect:ofView:untilMouseUp: override to handle checkbox interaction.

Everything was fine up until I noticed that once I press mouse button down inside my custom cell, cell object is being copied with copyWithZone: and this temporary object is then being sent a trackMouse:inRect:ofView:untilMouseUp: message, making it impossible to modify check state of the original cell residing in the view.

Since the question subject is about binding, I thought this might be the answer, but I totally don't get how should I connect all this mess to function as expected. Tried this:

[[[treeView outlineTableColumn] dataCell] bind:@"state"
                                      toObject:treeController
                                   withKeyPath:@"selection.state"
                                       options:nil];

but didn't succeed at all. Seems like I'm not getting it.

May this be a completely wrong way I've taken? Could you suggest a better alternative or any links for further reading?


UPD 1/21/11: I've also tried this:

[[[treeView outlineTableColumn] dataCell] bind:@"state"
                                      toObject:treeController
                                   withKeyPath:@"arrangedObjects.state"
                                       options:nil];

but kept getting errors like "[<_NSControllerTreeProxy 0x...> valueForUndefinedKey:]: this class is not key value coding-compliant for the key state." and similar.

Was it helpful?

Solution 2

Okay, I've managed to do what I needed by binding columns's value to arrangedObject's self (in IB) and overriding cell's setObjectValue: so that it looks like:

- (void) setObjectValue:(id)value
{
    if ([value isMemberOfClass:[MyNodeClass class]])
    {
        [super setObjectValue:[value name]];
        [self setIcon:[value icon]];
        [self setState:[value state]];
    }
    else
    {
        if (!value)
        {
            [self setIcon:nil];
            [self setState:NSOffState];
        }
        [super setObjectValue:value];
    }
}

Actual state change is performed within another class, connecting its method to cell's selector (in IB) which I call using

[NSApp sendAction:[self action] to:[self target] from:[self controlView]];

from cell's trackMouse:inRect:ofView:untilMouseUp:. This another class'es method looks like this:

- (IBAction) itemChecked:(id)sender
{
    MyNodeClass* node = [[sender itemAtRow:[sender clickedRow]] representedObject];
    if (node)
    {
        [node setState:[node state] == NSOnState ? NSOffState : NSOnState];
    }
}

OTHER TIPS

You bind a table (or outline) column's value, not an individual data cell's state. The data cell's object value is set to the current row/col's value then drawn so you don't have potentially thousands (or millions?) of cells created for no good reason.

Further, you want the tree or array controller's arrangedObjects, not its selection.

Bind the column's value to the tree controller's arrangedObjects as the controller key, and "state" as the model key path in IB; or @"arrangedObjects.state" in code as above.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top