Please help. At wit's end binding NSPopupButtonCell selection

  • Hi

    I've been reading and re-reading the Apple master detail documentation
    for two days now, googling, etc and can't to get the selection in an
    NSPopUpButtonCell to work correctly.  I've tried every permutation of
    binding I could think up without success. What happens is that when I
    choose an item in one row's popup cell, the selection in every popup
    in the entire table changes to the new selection.

    Here's how I'm setting up the bindings

    1. bind an NSArrayController (pageController ) to an NSArray of pages
    in a project
    [pageController bind: @"contentArray" toObject: inProject
    withKeyPath: @"pages" options: nil];

    2. bind an NSArrayController (popupController ) to an NSArray of
    master page types that can be applied to a page
    [popupController bind: @"contentArray" toObject: inProject
    withKeyPath: @"masters" options: nil];

    3. bind array of popup menu items to a popup menu column
    [popupColumn bind: @"contentValues" toObject: popupController
    withKeyPath: @"arrangedObjects.name" options: nil];

    4. bind the popup selection to the "master" page property of the
    currently selected page
    [popupColumn bind: @"selectedValue" toObject: pageController
    withKeyPath: @"selection.master" options: nil];

    I'm pretty certain that steps 1, 2, 3 are correct as the menus do get
    populated with the correct strings. It's step 4, binding a row's popup
    selection to the corresponding field in a page object that has eluded
    all my efforts.

    Obviously this kind of thing is possible because Apple (and many other
    applications) do it all the time, but whatever they are doing, I can't
    seem to get it working.

    Thanks for any help
  • On Nov 4, 2008, at 10:05 AM, Ken Tozier wrote:

    > What happens is that when I choose an item in one row's popup cell,
    > the selection in every popup in the entire table changes to the new
    > selection.

    Don't forget that there is only one NSPopUpButtonCell per column.  If
    you change it's selection, it's actually correct that when the table
    redraws, the other rows will redraw to show the new selection.

    Basically - If you have 5 rows, it appears that there are 5 different
    popup menus, but really, there is one that has been drawn 5 times at
    different locations.

    The table view tells you when it's about to draw a cell and you can
    set the correct selection of your popup cell here.  All you need to do
    is implement the delegate method:

    - (BOOL)tableView:(NSTableView *)theTableView willDisplayCell:
    (id)theCell forTableColumn:(NSTableColumn*)theTableColumn row:
    (int)theRowIndex
    {
    if([[theTableColumn identifier]
    isEqualToString:@"MyPopUpButtonColumn"])
      [theCell selectItemAtIndex:anIndex];
    }

    HTH,
    Cathy
  • Hmmm. So there's no way to set this up just through bindings?

    Also, in your snippet, where does "anIndex" come from?

    On Nov 4, 2008, at 5:32 AM, Cathy Shive wrote:

    > On Nov 4, 2008, at 10:05 AM, Ken Tozier wrote:
    >
    >> What happens is that when I choose an item in one row's popup cell,
    >> the selection in every popup in the entire table changes to the new
    >> selection.
    >
    > Don't forget that there is only one NSPopUpButtonCell per column.
    > If you change it's selection, it's actually correct that when the
    > table redraws, the other rows will redraw to show the new selection.
    >
    > Basically - If you have 5 rows, it appears that there are 5
    > different popup menus, but really, there is one that has been drawn
    > 5 times at different locations.
    >
    > The table view tells you when it's about to draw a cell and you can
    > set the correct selection of your popup cell here.  All you need to
    > do is implement the delegate method:
    >
    > - (BOOL)tableView:(NSTableView *)theTableView willDisplayCell:
    > (id)theCell forTableColumn:(NSTableColumn*)theTableColumn row:
    > (int)theRowIndex
    > {
    > if([[theTableColumn identifier]
    > isEqualToString:@"MyPopUpButtonColumn"])
    > [theCell selectItemAtIndex:anIndex];
    > }
    >
    >
    > HTH,
    > Cathy
    >
  • The 'anIndex'  in my snippet is just nothing - you'd have to determine
    that.  I guess it would be something like the selected object index of
    the array controller you're binding the popup's content to?

    As for whether or not it's possible to do this with bindings, I'm not
    100% sure...

    When the table is first displayed, are the proper selections displayed
    for each row?  Does this problem only come up when you change the
    selection selection in one of the pop up cells?

    On Nov 4, 2008, at 11:49 AM, Ken Tozier wrote:

    > Hmmm. So there's no way to set this up just through bindings?
    >
    > Also, in your snippet, where does "anIndex" come from?
    >
    > On Nov 4, 2008, at 5:32 AM, Cathy Shive wrote:
    >
    >> On Nov 4, 2008, at 10:05 AM, Ken Tozier wrote:
    >>
    >>> What happens is that when I choose an item in one row's popup
    >>> cell, the selection in every popup in the entire table changes to
    >>> the new selection.
    >>
    >> Don't forget that there is only one NSPopUpButtonCell per column.
    >> If you change it's selection, it's actually correct that when the
    >> table redraws, the other rows will redraw to show the new selection.
    >>
    >> Basically - If you have 5 rows, it appears that there are 5
    >> different popup menus, but really, there is one that has been drawn
    >> 5 times at different locations.
    >>
    >> The table view tells you when it's about to draw a cell and you can
    >> set the correct selection of your popup cell here.  All you need to
    >> do is implement the delegate method:
    >>
    >> - (BOOL)tableView:(NSTableView *)theTableView willDisplayCell:
    >> (id)theCell forTableColumn:(NSTableColumn*)theTableColumn row:
    >> (int)theRowIndex
    >> {
    >> if([[theTableColumn identifier]
    >> isEqualToString:@"MyPopUpButtonColumn"])
    >> [theCell selectItemAtIndex:anIndex];
    >> }
    >>
    >>
    >> HTH,
    >> Cathy
    >>

  • On Nov 4, 2008, at 7:07 AM, Cathy Shive wrote:

    > As for whether or not it's possible to do this with bindings, I'm
    > not 100% sure...
    >
    > When the table is first displayed, are the proper selections
    > displayed for each row?

    Yes and no. The popups have all the correct choices in them but all
    pages in the project group are set to the correct choice for the first
    page.

    > Does this problem only come up when you change the selection
    > selection in one of the pop up cells?

    Yes. When I select a new master page option, every other page in the
    group instantly changes to the same selection.

    The reason I think this might be possible through bindings, is that
    other bound fields in the table have values specific to their
    associated page. For example, pages also have a status string,
    indicating whether stories need trims, are missing photos etc and
    these are populated correctly with the following binding

    [pageController bind: @"contentArray" toObject: inProject withKeyPath:
    @"childNodes" options: nil];
    [statusColumn bind: @"value" toObject: pageController withKeyPath:
    @"arrangedObjects.status" options: nil];
  • On Nov 4, 2008, at 1:27 PM, Ken Tozier wrote:
    > The reason I think this might be possible through bindings, is that
    > other bound fields in the table have values specific to their
    > associated page. For example, pages also have a status string,
    > indicating whether stories need trims, are missing photos etc and
    > these are populated correctly with the following binding
    >
    > [pageController bind: @"contentArray" toObject: inProject
    > withKeyPath: @"childNodes" options: nil];
    > [statusColumn bind: @"value" toObject: pageController withKeyPath:
    > @"arrangedObjects.status" options: nil];
    >

    It is possible:
    http://homepage.mac.com/mmalc/CocoaExamples/controllers.html

    Look at the "To Dos" example project. Seems like a similar setup:

    "Shows two array controllers, one to manage the contents of a table
    view, the other to manage a pop-up menu in a table column."

    I think this can help you get it working :)

    - c
  • Success!

    Here's the correct incantation

    1. bind page controller to page list
    [pageController bind: @"contentArray" toObject: inProject
    withKeyPath: @"pages" options: nil];

    2. bind popup controller to master page list
    [popupController bind: @"contentArray" toObject: inProject
    withKeyPath: @"masters" options: nil];

    3. bind popup column to pageController and popupController
    [popupColumn bind: @"content" toObject: popupController withKeyPath:
    @"arrangedObjects" options: nil];
    [popupColumn bind: @"contentValues" toObject: popupController
    withKeyPath: @"arrangedObjects.name" options: nil];
    [popupColumn bind: @"selectedValue" toObject: pageController
    withKeyPath: @"arrangedObjects.master" options: nil];

    Thank you so much Cathy. You're a life saver :)

    -Ken


    On Nov 4, 2008, at 7:38 AM, Cathy Shive wrote:

    > It is possible:
    > http://homepage.mac.com/mmalc/CocoaExamples/controllers.html
    >
    > Look at the "To Dos" example project. Seems like a similar setup:
    >
    > "Shows two array controllers, one to manage the contents of a table
    > view, the other to manage a pop-up menu in a table column."
    >
    > I think this can help you get it working :)
    >
    > - c
    >
    >
    >
    >
    >
    >
    >
  • On Nov 4, 2008, at 1:05 AM, Ken Tozier wrote:

    > Here's how I'm setting up the bindings
    >
    > 1. bind an NSArrayController (pageController ) to an NSArray of
    > pages in a project
    > [pageController bind: @"contentArray" toObject: inProject
    > withKeyPath: @"pages" options: nil];

    One tip that has nothing to do directly with the issue you're
    requesting help with:

    Never use a literal string for the binding name; always use the
    appropriate constant.

    Thus the above should be:

      [pageController bind:NSContentArrayBinding toObject:inProject
    withKeyPath:@"pages" options:nil];

    The same is true for things like NSNotification names, various known
    NSDictionary keys, and so on -- if there's a named constant, use it,
    rather than make an assumption about what its value is.

      -- Chris
  • To summarize what you've done:

    1. You set up a Pages NSArrayController.
    2. You set up a Masters NSArrayController.
    3. You bind the pop-up's Content Values to the Masters controller's
    "arrangedObjects.name".
    4. You bind the pop-up's Selected Value to the Pages controller's
    "selection.master" property.

    I think the issue is that Step #4 is incorrect, and you're missing
    Step #5.  There's also one other caveat.

    Step 4 should be:

    4. You bind the pop-up's Selected Object to the Pages controller's
    "selection.master" property.

    The Selected Value binding is for the title of the selected item; the
    Selected Object binding is used to actually choose the selected item.

    The missing Step 5 is:

    5. Bind the pop-up's Content to the Masters controller's
    "arrangedObjects".

    This will ensure that each item in the pop-up represents an item
    available via the Masters controller.  Think of it this way:  "Content
    Values" and "Selected Value" are about what the pop-up shows to the
    user, while "Content" and "Selected Object" are about the underlying
    objects the pop-up represents.

    One other thing to ensure is that you're *only* using the specific
    Masters controller you set up in Step #2 for populating this pop-up.
    One common issue is to try to "consolidate" controllers; since
    bindings are generally two-way, this can cause unexpected behavior.
    Use separate controllers for separate situations, even if they're
    bound to the same thing.

      -- Chris
previous month november 2008 next month
MTWTFSS
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
Go to today