wxNotebook - inner wxPanel - call method when page is changed - wxwidgets

I have wxNotebook with several wxNotebookPage. Each page has a different content that inherits from wxPanel. What method from wxPanel is called when active page is changed? If I change panel, I would like to update panel content.
I have the similar functionality binded to resize event which is working.
void MyPanel::Init() {
this->Bind(wxEVT_SIZE, &MyPanel::OnResize, this);
}
void MyPanel::OnResize(wxSizeEvent& ev){
//do something to update panel
}
Note: This is C++ code, but the programming language is not important (it can be Python). I will update the solution, I just need the logic or wxWidget API calls that are same.

What method from wxPanel is called when active page is changed?
There is no method called on the panel. Instead a page changed event is sent to the notebook control. You can set up a handler for this event similar to how you set up a handler for the size events on your panels above. Usually this is done in the constructor for your frame like so:
MyFrame::MyFrame(...):wxFrame(...)
{
...
<notebookobject>->Bind(wxEVT_NOTEBOOK_PAGE_CHANGED, &MyFrame::OnPageChanged, this);
...
}
...
void MyFrame::OnPageChanged(wxBookCtrlEvent& event)
{
// do something in response to the page change here.
}
There is also a page changing event that is sent before the page is actually changed. The page changing event has an extra feature that if you want to disallow the page from being changed, you can call Veto() on the event object in the page changing handler.

Related

How to implement correct Back behaviour when using Frame.Navigate?

I have a Windows 10 Universal Windows Platform app with multiple pages, a main page, a list page and a details page and use the following to navigate to List page:
this.Frame.Navigate(typeof(ListPage), parameter);
When you are on the list page you can select an item which will launch a details page like so:
this.Frame.Navigate(typeof(DetailsPage), parameter);
Which works fine, the parameter is a selected Id or information then when using the Back button which on a Desktop app or Phone uses:
this.Frame.GoBack();
This always returns to the MainPage, that is when go from Main, to List to Details hitting back goes to Main, how do I get the GoBack to Go back to the previous page, it always seems to go home rather than the user expected behaviour, an ideas how to resolve this?
I’ve seen this before when you subscribe to the HardwareButtons.BackPressed event (or whatever the equivalent is in a Win10 UWP app) on a page, but then don’t unsubscribe from it. This means two event handlers get called when pressing Back, and both event handlers call Frame.GoBack().
I always subscribe to the event in the page’s NavigatedTo event, and unsubscribe in the NavigatedFrom event.
Could this be happening with your app?
If every page in your app should have the same behaviour, i.e. go back to the previous page, then subscribe to the back button event in the app class as suggested by #RoguePlanetoid in the comments:
SystemNavigationManager.GetForCurrentView().BackRequested += OnBackRequested;
The OnLaunched method would be a good place to do this. Don't forget to tell the OS to display the back button when the app is running on a desktop or tablet:
SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
Then, add an event handler in the app class like this:
private void OnBackRequested(object sender, BackRequestedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame.CanGoBack)
{
e.Handled = true;
rootFrame.GoBack();
}
}
If you want different behaviour on different pages when back is pressed, i.e. ask the user to confirm losing their changes or something, then subscribe to the back button event in a pages OnNavigatedTo method (the code will be same as above), but make sure you unsubscribe in the page's OnNavigatedFrom event:
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
SystemNavigationManager.GetForCurrentView().BackRequested -= this.OnBackPressed;
base.OnNavigatedFrom(e);
}

how to see mouseReleased() event outside canvas using processing.js

I have an object I want to drag around the screen with the mouse in Processing. I set acquired to true on mouse down over the object, and to false on mouse up, thus:
void mousePressed() {
if (overThing()) {
acquired = true;
}
}
void mouseReleased() {
acquired = false;
}
I then query acquired in my update(), and drag the object if it is true.
void update() {
\\ other stuff...
if (acquired) {
\\ drag thing code ...
}
}
This all works fine in Processing. mouseReleased() gets called whether I release the mouse inside or outside the active window.
However, when I move the code to Chrome, using processing.js (v1.4.8), mouseReleased() is not called if I release the mouse outside the canvas (whether the mouse is still over the web page, or outside the browser window). So when I return the (now unclicked) mouse to the canvas, the object is still getting dragged around.
I tried including a test of mousePressed in update(), but that also returns true in these circumstances.
Any help on what I need to do to make mouse state changes outside the canvas visible with processing.js?
I don't know about Processing specifically, but releasing mouse buttons outside a widget is a common issue in GUI development.
I suspect that you have no way of knowing the precise time when the mouse is released outside the widget, but you do have two options:
Set acquired = false in mouseOut(), as #Kevin suggests.
I assume there is some type of mouseEntered() method in Processing, and also some way of knowing if the mouse button is currently pressed (either a global variable, or an event object passed to mouseEntered()). You can catch the mouse entered event, check if the mouse has been released, and set acquired = false then.
Like so:
void mouseEntered() {
if (mouse button is pressed) {
acquired = false;
}
}
Edit: From your comments, #Susan, it seems like there is a bug in processing.js, where mousePressed is not set to false if the mouse button is released outside the canvas. One thing pointing to this being a bug is that the mouse movement example on the processing website also shows this behaviour.
Depending upon how much control you have over the website this is going on, and how much effort you want to go to, you could fix the bug yourself by writing some javascript (separate from your processing code):
Define a mouseUp() event on the page <body>, to catch all mouse release events on the page.
In the mouseUp() event, check if the event comes from your Processing control. (There is probably an event object passed to the mouseUp() function, and you might have to give your Processing control an ID to identify it)
If the event doesn't come from your Processing control, then fire a mouseUp event yourself, on the Processing control. This should (hopefully!) trigger a mouse event inside your Processing code.
I'm not sure what Processing will make of the mouse (x,y) position being outside its control when it handles the event you send it. You might want to set a flag on the event object (assuming you can add extra data to the event object) to say "don't use the (x,y) position of this event - it's outside the control".
Edit2: It was easier than I thought! Here is the JavaScript code to detect the mouse being released outside of the Processing canvas and send the mouseReleased event to the canvas. I've tested it on the mouse movement example from the Processing website, and it fixes the bug.
It uses jQuery (although it could be re-written to not use jQuery), and it assumes your Processing canvas has the ID "processingCanvas":
$(':not(processingCanvas)').mouseup(function(){
Processing.getInstanceById('processingCanvas').mouseReleased();
});
To use this code, include it anywhere in your page (in a JavaScript file or in <script> tags) and make sure you have the jQuery library included before this code.
The Processing object allows JavaScript to call any functions defined in your Processing code. Here I've used it to call Processing's built in mouseReleased() function, but if you wanted to call a custom function to handle the mouse-released-outside state differently, then you could.
You should use the mouseOut() function to detect when the mouse leaves the sketch:
void mouseOut() {
acquired = false;
}
More info in the reference here.

Durandal 2.0 Dialog Repositioning

At the bottom of the durandal docs for dialogs / modals (http://durandaljs.com/documentation/Showing-Message-Boxes-And-Modals.html) there's some syntax for repositioning a dialog. The dialog I have gets gradually bigger as the user selects stuff, so every time a selection is made, I'd like to call reposition(). I tried following along with this:
vm.compositionComplete = function (child, parent, context) {
var addEditDialog = dialog.getDialog(context.model); // resolves
// whenever something is selected:
addEditDialog.context.reposition(vm); // no method 'reposition'
}
But I get an error - there is no function reposition. What am I doing wrong?
You can set up a custom dialog context that responds to a reposition message (using Durandal's app.trigger()). You would trigger the message upon some event in your dialog (such as, as you say, the user's selecting stuff).
Also in that custom dialog context, create a method call reposition. In the activate or attached handler of that custom dialog context, subscribe to the message you use to trigger a reposition.
I'm advocating a messaging approach because you may wish to isolate your "selection viewModel" in its own viewModel, and then compose it into your custom dialog context. With this approach, your selection viewModel and your custom dialog context would be loosely bound. That way, you could use your selection viewModel elsewhere in your code (and even have some other viewModel, instead of the custom dialog context, respond to the reposition message).

Reloading a page in WinJS fails to attach event handlers

I have a WinJS application (a Windows 8.1 app using HTML/JS, not C#/XAML).
I've implemented a custom navbar in my default.html, with some buttons that have click event listeners attached to them. Each handler calls nav.navigate() with the url of the page corresponding to the nav button.
One of my pages (call it /pages/myPage/myPage.html) has several buttons on it. Each button has a click event listener bound to it in the page's ready function. This works fine when navigating between several pages.
However, if I'm on myPage (with working button click handlers) and click the navbar button for myPage again, the page looks like it reloads. The ready function seems to be called (i.e. it console.log statements in it are executed), but the buttons on the page seem to completely lose their click handlers!
If I navigate to another page, then navigate back, the buttons work fine again. But no matter what I do, "reloading" the page by navigating to itself (nav.navigate("/pages/myPage/myPage.html") while on myPage) causes my click handlers to be lost.
Why does this happen? My ready function is called, but somehow the click handlers are never re-attached.
Here's what the ready function for myPage looks like:
ready: function (element, options) {
document.getElementById("myButton").addEventListener("click", this.myButtonClicked);
},
Here's what the click event listener for the myPage nav button looks like (this code is in default.js):
myPageNavButton.addEventListener("click", function () {
nav.navigate('/pages/myPage/myPage.html');
});
Page nav in WinJS is just a matter of DOM replacement. When you do a nav, the target “page” contents are loaded into the DOM and then the previous “page’s” contents are unloaded. You can see this in navigator.js in the _navigating method. It creates a new element for the page being loaded, renders that fragment therein, and then unloads the old page.
The ready method for the new page, however, is called before the old page is unloaded (this was a change in WinJS 2.0, as WinJS 1.0 unloaded the old page before calling ready). The upshot of this is that when you navigate to the same page that’s already loaded, myPage.html(A) is in the DOM when you load myPage.html(B). When you execute the code in your ready method, then, getElementById will find the buttons in myPage.html(A) and so you're attaching handlers to that element. But then after you return from ready, myPage.html(A) is unloaded, so you lose the handlers. And because you never attached handlers to the buttons in myPage.html(B), they're just inert.
So what can you do about it? The best solution, in my mind, is to avoid navigating to the same page in the first place, because it's just fraught with other peril in the long run. To do this, wrap your call to nav.navigate with a check for whether you're already on the target page. Here's an implementation of a function that does that:
function navigateIfDifferent(target) {
var page = document.getElementById("contenthost").winControl.pageControl;
var fullTarget = "ms-appx://" + Windows.ApplicationModel.Package.current.id.name + target;
if (fullTarget !== page.uri) {
WinJS.Navigation.navigate(target);
}
}
This assumes that the PageControlNavigator control that you're using is in a div called "contenthost" in default.html (which is what the VS template gives you). What I'm then doing is building the full in-package URI for the target page and comparing that to the uri of the current page control, which is also a full in-package URI. You could also strip off the ms-appx:// part from the current page URI and compare to the target URI. Either way.
Anyway, with this function, replace your calls to nav.navigate with navigateIfDifferent, and you should be good.

Windows 8 app: How to pass a parameter on GoBack()?

There are a lot of samples showing how to pass a parameter on navigating to a page in Window 8 (WinRT). But I could not find any hint for passing parameters going back.
Situation: The user navigates to a details page of same data. The data is passed to the page by
Frame.Navigate(typeof(DedtailsPage), data);
How can I pass back the changed data on GoBack()?
Store the reference to data somewhere and retrieve it when you navigate back?
Also note that it's best not to pass objects other than simple strings or values between pages, since only simple types are supported for storing frame navigation state in case your app gets suspended.
I know, that this is a very bad idea, but usualy I use this:
To write:
Frame rootFrame = Window.Current.Content as Frame;
rootFrame.Tag = myObject1;
To read:
Frame rootFrame = Window.Current.Content as Frame;
var myObject2 = rootFrame.Tag;
I've made another choice to handle this.
Keep in mind that I'm a Xamarin developer (not Forms!) so i was looking for a solution which was similar for all platforms: iOS, Android and Windows.
I am a great fun of events, rather than passing objects or storing globals.
So the best choice to pass data from PageB (the child) to PageA (the parent) is to communicate via events.
Some notes: In iOS and Android, when you navigate from a "page" to "page" you can do this by passing an instance of the target object you want to navigate to. In iOS you create an instance of a custom UIViewController. In Android you create an instance of a custom Fragment. Doing this allow you to attach events handler to your instances.
An example:
var controller = new ExtrasViewController();
controller.ExtraSelected += ExtrasFragment_ExtraSelected;
controller.ExtrasCleared += ExtrasFragment_ExtrasCleared;
this.NavigationController.PushViewController(controller, false);
In WinRT Apps you are only allowed to pass "types" of target page to navigate to. So the only way to attach event handlers to your page instance is to use the OnNavigatedFrom method from the calling page. So suppose you are in PageA and want to attach some event handlers to your PageB, before it become active, simply write in your PageA "code behind":
protected override void OnNavigatedFrom(NavigationEventArgs e)
{
base.OnNavigatedFrom(e);
var page = e.Content as ExtraBody;
if(page != null)
{
page.ExtraSelected += ExtrasFragment_ExtraSelected;
page.ExtrasCleared += ExtrasFragment_ExtrasCleared;
}
}