Welcome!

Welcome to the official BlackBerry Support Community Forums.

This is your resource to discuss support topics with your peers, and learn from each other.

inside custom component

Native Development

Reply
Developer
greenmr
Posts: 912
Registered: ‎03-20-2013
My Device: Red LE Developer Z10
Accepted Solution

NavigationPane - Why won't this work?

I am frustrated by NavigationPane not behaving as I would expect when pushing pages recursively. Say I have three QML files; MyNavPane.qml, MyFirstPage.qml, and MySecondPage.qml, and suppose MyFirstPage.qml looks like this:

 

Page {
   property NavigationPane mainNavPane
   attachedObjects: [
      MySecondPage {
         id: mySecondPage
} ] function pushSecondPage() { mainNavPane.push( mySecondPage ); } }

...while MyNavPane.qml is this:

 

NavigationPane {
   id: myNavPane
   attachedObjects: [
      MyFirstPage {
         id: myFirstPage
         mainNavPane: myNavPane
      }
   ]
   function pushFirstPage() {
      push( myFirstPage );
   }
}

Now executing pushFirstPage() works fine and pushes the first page onto the navigation stack, however pushSecondPage() executes without error, but the second page is not pushed. Debugging shows that mainNavPane correctly points back to the main NavigationPane.

 

If I change MyFirstPage.qml to this:

 

Page {
   property NavigationPane mainNavPane
   function pushSecondPage() {
      mainNavPane.pushSecondPage();
   }
}

... and MyNavPane.qml to this:

 

NavigationPane {
   id: myNavPane
   attachedObjects: [
      MyFirstPage {
         id: myFirstPage
         mainNavPane: myNavPane
      },
      MySecondPage {
         id: mySecondPage
      }
   ]
   function pushFirstPage() {
      push( myFirstPage );
   }
   function pushSecondPage() {
      push( mySecondPage );
   }
}

 ... it works as intended.

 

I tried doing this too, but it didn't work either, the second page was not pushed but no error was reported:

 

Page {
   property NavigationPane mainNavPane
   attachedObjects: [
      MySecondPage {
         id: mySecondPage
      }
   ]
   function pushSecondPage() {
      mainNavPane.pushPage( mySecondPage );
   }
}

NavigationPane {
   id: myNavPane
   attachedObjects: [
      MyFirstPage {
         id: myFirstPage
         mainNavPane: myNavPane
      }
   ]
   function pushFirstPage() {
      push( myFirstPage );
   }
   function pushPage( page ) {
      push( page );
   }
}

Why does only the second method where both pages to be pushed are attached to the NavigationPane itself work, while the other two won't? What am I missing?



Developer of Built for BlackBerry certified multiFEED RSS/Atom feed reader and aggregator.
Developer
Zmey
Posts: 1,512
Registered: ‎12-18-2012
My Device: PlayBook, Z10, DAC

Re: NavigationPane - Why won't this work?

[ Edited ]

Hi,

 

NavigationPane should own the objects which are pushed to it.

Quoting the docs:

 

The NavigationPane takes the ownership of the pushed page. The owner of the pushed page must be 0.

If the owner of the pushed page is not 0, or if the pushed page itself is 0, nothing will happen.

 

This is not exactly accurate because pushing the pages which are owned by NavigationPane also seems to work.

 

If you want to create them elsewhere, you can try to reset their parent to NULL before pushing them. This can be done using QObject's setParent() function and will require a C++ helper function (QML's parent property is read-only). I don't know if this approach will work.

 

Another option is creating them dynamically using createObject and ComponentDefinition, this way their parent will be NULL initially. I think this is a cleaner approach.

 


Andrey Fidrya, @zmeyc on twitter
Developer
greenmr
Posts: 912
Registered: ‎03-20-2013
My Device: Red LE Developer Z10

Re: NavigationPane - Why won't this work?

Didn't see that requirement, I think that explains it. Thanks.

 

I suspect I can just set the parent to 0 before passing it, or maybe to mainNavPane, but I've already refactored to use the second method, so I'll probably leave it.

 

Thanks again.



Developer of Built for BlackBerry certified multiFEED RSS/Atom feed reader and aggregator.
Developer
greenmr
Posts: 912
Registered: ‎03-20-2013
My Device: Red LE Developer Z10

Re: NavigationPane - Why won't this work?

[ Edited ]

UPDATE: I decided I needed to figure out if changing the parent of the page to be pushed would work, and it does!

 

The first thing I had to do was make a way to change an object's parent in QML, which you can't normally do. I created a new Q_INVOKABLE function on my main UI class called changeParent():

 

void MyApp::changeParent( QObject* object, QObject* newParent ) {
   object->setParent( newParent );
}

Then I modified the pushPage() function from one of my earlier examples:

 

Page {
   property NavigationPane mainNavPane
   attachedObjects: [
      MySecondPage {
         id: mySecondPage
      }
   ]
   function pushSecondPage() {
      mainNavPane.pushPage( mySecondPage );
   }
}

NavigationPane {
   id: myNavPane
   attachedObjects: [
      MyFirstPage {
         id: myFirstPage
         mainNavPane: myNavPane
      }
   ]
   function pushFirstPage() {
      push( myFirstPage );
   }
   function pushPage( page ) {
      app.changeParent( page, myNavPane )
      push( page );
   }
}

... and voilà, the page pushes perfectly, even though it originally didn't belong to the NavigationPane. I also tried changing the parent to zero, and that worked too, but I felt more comfortable never leaving the page hanging around without a parent. Memory leaks can be insidious.

 

I also tried changing the parent in the creationCompleted handler but although that compiled fine it screwed up access to the MySecondPage object in other code. Since mainNavPane didn't have a value during the execution of the creationCompleted handler, QML got confused and thought that mySecondPage didn't exist anymore when I put this code in:

 

Page {
   property NavigationPane mainNavPane
   attachedObjects: [
      MySecondPage {
         id: mySecondPage
         onCreationCompleted: {
            app.changeParent( mySecondPage, mainNavPane )
         }
      }
   ]
   function pushSecondPage() {
      mainNavPane.pushPage( mySecondPage );
   }
}

Changing this to set the new parent to zero instead of mainNavPane DOES work but is unsafe since mySecondPage will be orphaned until you call pushSecondPage at least once. If you never execute the push you will leave mySecondPage behind when you destroy myfirstPage, which is a memory leak.

 

Of course, you could do this instead:

 

Page {
   property NavigationPane mainNavPane
   attachedObjects: [
      MySecondPage {
         id: mySecondPage
      }
   ]
   onMainNavPaneChanged: {
      app.changeParent( mySecondPage, mainNavPane )
   }
   function pushSecondPage() {
      mainNavPane.pushPage( mySecondPage );
   }
}

... in which case you don't need to change the parent again every time you push the page, probably a better solution.

 

Anyway, thanks again for pointing me in the right direction and hopefully this will be useful to others.



Developer of Built for BlackBerry certified multiFEED RSS/Atom feed reader and aggregator.