开发者

NSOutlineView not redrawing

开发者 https://www.devze.com 2022-12-29 06:34 出处:网络
I have an NSOutlineView with checkboxes. I have the checkbox state bound to a node item with the key shouldBeCopied. In the node item I have the getters and setters like so:

I have an NSOutlineView with checkboxes. I have the checkbox state bound to a node item with the key shouldBeCopied. In the node item I have the getters and setters like so:

-(BOOL)shouldBeCopied {
    if([[self parent] shouldBeCopied])
        return YES;
    return shouldBeCopied;
}

-(void)setShouldBeCopied:(BOOL)value {
    shouldBeCopied =开发者_C百科 value; 
    if(value && [[self children] count] > 0)
        [[self delegate] reloadData];
}

The idea here is that if the parent is checked, so should the children. The problem I'm having is that when I check the parent, it does not update that view of the children if they are already expanded. I can understand that it should not be updated by the bindings because i'm not actually changing the value. But should reloadData not cause the bindings to re-get the value, thus calling -shouldBeCopied for the children? I have tried a few other things such as -setNeedsDisplay and -reloadItem:nil reloadChildren:YES but none work. I've noticed that the display gets refreshed when I swap to xcode and then back again and that's all I want, so how do I get it to behave that way?


I have an NSOutlineView with checkboxes. I have the checkbox state bound to a node item with the key shouldBeCopied.

Are you binding the column or the cell? You should bind the column.

-(BOOL)shouldBeCopied {
    if([[self parent] shouldBeCopied])
        return YES;
    return shouldBeCopied;
}

Would it not be better to use the child's value first? If nothing else, it's cheaper to retrieve (no message required).

The amended getter would then look like this:

- (BOOL) shouldBeCopied {
     return (shouldBeCopied || [[self parent] shouldBeCopied]);
}

The problem I'm having is that when I check the parent, it does not update that view of the children if they are already expanded.

There are two solutions. One is cleaner and will work on 10.5 and later. The other is slightly dirty and will work on any version of Mac OS X.

The dirty solution is to have the parent, from the setter method, post KVO notifications on behalf of all of its children. Something like:

[children performSelector:@selector(willChangeValueForKey:) withObject:@"shouldBeCopied"];
//Actually change the value here.
[children performSelector:@selector(didChangeValueForKey:) withObject:@"shouldBeCopied"];

This is dirty because it has one object posting KVO notifications about a property of another object. Each object should only claim to know the values of its own properties; an object that claims to know the values of another object's properties risks being wrong, leading to wrong and/or inefficient behavior, not to mention a propensity for the code to induce headache.

The cleaner solution is to have each object observe this property of its parent. Pass the NSKeyValueObservingOptionPrior option when adding yourself as an observer in order to get a notification before the change (which you'll respond to, in the observation method, by sending [self willChangeValueForKey:]) and a notification after the change (which you'll respond to, in the observation method, by sending [self didChangeValueForKey:]).

With the clean solution, each object only sends KVO notifications about its own property changing; no object is posting notifications about other objects' properties.

You might be tempted to have the child not send itself these KVO notifications when its own value for the property is YES, because in that case the parent's value doesn't matter. You shouldn't, though, as this would only work for children; if a farther-down descendant has the property set to NO, then that object's ancestors' values matter after all, and that object will only get the notification if every one of its ancestors posts it.


Your setter does not send -willChangeValueForKey: and -didChangeValueForKey: before and after the change, therefore the changes won't be "noticed" by the bindings mechanism.

Also, telling a view anything directly from a model object is ... not a good approach. In this case, since you're using Bindings, your tree controller should note the change (once you fix your setter to send the proper notifications) and update the outline view.

0

精彩评论

暂无评论...
验证码 换一张
取 消