TornadoFx: Show progressindicator on button by hand - tornadofx

It's about TornadoFx' button.
If i use
button("push") {
action {
runAsyncWithProgress {
some()
}
}
}
I see progressindicator on the button.
How can I show/hide it programmatically?

The runAsyncWithProgress function automatically adds and removes the progress indicator so it only shows when the task is running.
The function saves the current graphic property of the node and displays a progress indicator in it's place. When the task is completed, the old graphic is reinstated. You can however modify the graphic property at any time, as long as you do it on the UI thread. Inside of your long running function you can do runLater { graphic = null } to remove the progress indicator for example.

Related

How to detect if user requested a tab change on TabbedPage in Xamarin.Forms

I have a Xamarin.Forms application which uses a TabbedPage, let's call it T, T consists of 3 ContentPage children A, B and C. Since the usere has the possibility to edit some data on tab B, I want to notify user before leaving tab in order to allow him to cancel the navigation change and save changes first or to discard changes and leave. So far I have managed to override OnBackButtonPressed() method and the navigation bar back button (which would exit TabbedPage). However I quickly noticed that I am still loosing changes when switching between tabs. I would like to override the click on new tab, so I could first present user with the leaving dialog and the skip the change or continue with it. What would be the best way to do this? I am currently working only on Android platform, so solutions on the platform level are also acceptible.
Thank you for your suggestions and feedback :)
I do not think there is an easy way to do this ,
you can use OnDissappearing and OnAppearing for the pages, that is as easy as it gets .
However I think you are using the wrong design.
Having tabs are ment to make it easier to navigate between pages, if you are going to notify the user when changing the tabs then it would be annoying . If I were you i would save the data for each page locally. so when you get back to the page you will have the data anyway.
So in the end I followed the advice of Ahmad and implemented the persisting of data on individual tabs so they are not lost when tabs are switched. (I no longer refresh input fields from data from model when OnAppearing is called).
But in order to know if there are some unsaved changes on my ChildB page, I had to implement the following procedures:
I created the method HandleExit on my ChildB page, which checks for unsaved changes in fields (at least one value in input fields is different from the ones in stored model) and the either prompts the user that there are unsaved changes (if there are some) or pops the navigation stack if there are no changes.
private async Task HandleExit()
{
if(HasUnsavedChanges())
{
var action = await DisplayAlert("Alert", "There are unsaved changes, do you want to discard them?", "Discard changes", "Cancel");
if(!action)
{
return;
}
}
await Navigation.PopAsync();
}
Since there are two ways on how user can return from Tabbed page (pressing the back button on device or pressing the back button in navigation bar, I had to:
A: override the back button method on my ChildB page, so it calls the HandleExit method. But since Navigation.PopAsync() needs to be called on UI thread, I had to explicitly execute the method on UI thread as written below:
protected override bool OnBackButtonPressed()
{
Device.BeginInvokeOnMainThread(new Action(async () =>
{
await HandleExit();
}));
return true;
}
B: Since there is no way to intercept the navigation bar back button on the ContentPage, I had to intercept the event on the platform level (Android) and then pass the event to the ContentPage if necessary via MessagingCenter. So first we need to intercept the event, when navigation bar button is pressed in one of the child pages and send the event via MessagingCenter. We can do that but adding the following method in our MainActivity.cs class:
public override bool OnOptionsItemSelected(IMenuItem item)
{
// check if the current item id
// is equals to the back button id
if (item.ItemId == 16908332)
{
// retrieve the current xamarin forms page instance
var currentpage = Xamarin.Forms.Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault();
var name = currentpage.GetType().Name;
if(name == "ChildA" || name == "ChildB" || name == "ChildC")
{
MessagingCenter.Send("1", "NavigationBack");
return false;
}
}
return base.OnOptionsItemSelected(item);
}
Now whenever we will press the navigation bar back button in one of the child pages (ChildA, ChildB, ChildC) nothing will happen. But the button will work as before on the rest of the pages. For the second part of solution we need to handle the message from MessagingCenter, so we need to subscribe to it in our ChildB page. We can subsribe to the message topic in OnAppearing method as follows:
MessagingCenter.Subscribe<string>(this, "NavigationBack", async (arg) => {
await HandleExit();
});
Be careful to unsubscribe to the topic in OnDisappearing() otherwise strange things could happen, since there will be references left to your ContentPage even if you pop it from your navigation stack.
Now that we have handled both requests for back navigation in our ChildB page, we also need to handle them in all of remaining child pages (ChildA, ChildC), so they will know if there are unsaved changes in ChildB page, even if it is currently not selected. So the solution is again compraised of handling the device back button, and navigation bar back button, but first we heed a way to check if ChildB has unsaved changes when we are on one of the remaining pages, so we again write HandleExit method but this time it is as follows:
private async Task HandleExit()
{
var root = (TabbedPage)this.Parent;
var editPage = root.Children.Where(x => x.GetType() == typeof(ChildB)).FirstOrDefault();
if(editPage != null)
{
var casted = editPage as ChildB;
if (casted.HasUnsavedChanges())
{
var action = await DisplayAlert("Alert", "There are unsaved changes, do you want to discard them?", "Discard changes", "Cancel");
if (!action)
{
return;
}
}
}
await Navigation.PopAsync();
}
The only thing that remains now is to handle both navigation back events inside remaing child pages. The code for them is the same as in the actual ChildB page.
A: Handling the device back button.
protected override bool OnBackButtonPressed()
{
Device.BeginInvokeOnMainThread(new Action(async () =>
{
await HandleExit();
}));
return true;
}
B: Subscribing to topic from MessagingCenter
MessagingCenter.Subscribe<string>(this, "NavigationBack", async (arg) => {
await HandleExit();
});
If everthing has been done correctly, we should now be prompted with a dialog on any of the child pages if there are unsaved changes on the ChildB page. I hope this will help somebody in the future :)

Navigation Drawer back button Xamarin

I am using this binding for this awesome Material Drawer Library by MikePenz.
I have implemented the Navigation Drawer with this library and I have also managed to change the hamburger menu to the back arrow when I go level deep. Now I have some problems to get the back arrow to work correctly. When I click on the back arrow, rather than going back to the previous page, it opens up the navigation drawer.
After looking into the original library, I have identified, the following code is responsible to manage the back arrow button. I would appreciate , if someone can help me a bit to write this listener code in C#.
.withOnDrawerNavigationListener(new Drawer.OnDrawerNavigationListener() {
#Override
public boolean onNavigationClickListener(View clickedView) {
//this method is only called if the Arrow icon is shown. The hamburger is automatically managed by the MaterialDrawer
//if the back arrow is shown. close the activity
AdvancedActivity.this.finish();
//return true if we have consumed the event
return true;
}
})
Here is the binding libray that I use : MaterialDrawer-Xamarin
And this is the link to the original Library : MaterialDrawer
Try something like this:
var result = new DrawerBuilder()
.WithActivity(this)
.AddDrawerItems(
//Add some items here
new DividerDrawerItem()
)
.WithOnDrawerNavigationListener(this);
and implement Drawer.IOnDrawerNavigationListener in your activity like this:
public bool OnNavigationClickListener(View clickedView)
{
this.Finish();
return true;
}

onPrepareOptionsMenu acts weird if API is called from inside

I have a need to enable and disable menu items based on the API call response and it has to be called everytime the menu shows up.
I need asynctask because I have to show progressbar
#Override
public boolean onPrepareOptionsMenu (Menu menu) {
handleMenuItems(menu)
}
private void handleMenuItems(menu)
{
new AsyncTask<Void, Void, Void>(){
#Override
protected Void doInBackground(Void... params) {
//API call
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
if (progressDialog != null)
progressDialog.hide();
//necessary menu items are enabled and disabled
super.onPrepareOptionsMenu(menu);
}
#Override
protected void onPreExecute() {
if (progressDialog != null){
progressDialog.setMessage("Checking");
progressDialog.show();
}
super.onPreExecute();
}
}.execute();
}
Whenever I touch the options menu the onPrepareOptionsMenu gets called and the options menu doesn't appear then when I press it again the onPrepareOptionsMenu doesn't get called and the options menu appears.
I want the api to get called every time and menu to be shown whenever I touch for options menu.
You shouldn't show and hide progress dialog in OnPrepareOptionsMenu
Comment out the progress dialog codings and rest of the codes are ok and works as you expected
Note:
You have called super.OnPrepareOptionsMenu in OnPostExecute(UI
Thread)
As the progress bar is not needed don't remove the AsyncTask
otherwise app my crash
I don't think this code does what you expect it to do. When onPrepareOptionsMenu is called the first time, it only schedules the AsyncTask to run at a future time and not right away. Next it executes the super.onPrepareOptionsMenu line which should show whatever the option menu is set to be at that time. I think (have to check on that) the UI will then wait for the user input as it is showing some sort of a menu (maybe not what you had expected). When the menu is touched for the second time, I think it will dismiss the menu and that might be the time that the AsyncTask is executed which results in the first menu that you had expected in the first place. The second instance of the AsyncTask will not run until the first one finishes (unless executeOnExecutor is called instead of execute, but that is not the case here nor I believe it to help the expected outcome.)
You might want to have a "dummy" menu prepared (maybe something that shows "waiting...") and start a background thread where it builds up the right menu and then when it is ready update and display a new menu (programmatically).
I personally would advise in finding a way to build and show the menu on the UI thread (maybe prep the menu as the changes are made, ahead of time, and just show it when needed.
Kaamel

Multiple leave events generated in Custom widget in Qt

I receive a Leave Event every time i move a pixel with my mouse over a customized QFrame i did. Why is this happening?.
I reimplemmented the leave and enter event as follows. As you can see i tried to comment the QFrame enterEvent, and restrict the repetition with a boolean, but it doesnt work because an enter and leave are continuously generated:
void enterEvent( QEvent *event ){
//QFrame::enterEvent(event);
if (!mouseHover_)
{
mouseHover_ = true;
emit hoverInSignal("");
}
}
void leaveEvent( QEvent *event ){
//QFrame::leaveEvent(event);
if (mouseHover_)
{
SmartUIWrapper::Instance()->addInfoMessage("out");
emit hoverOutSignal();
mouseHover_ = false;
}
}
Does it have something to be with the focus?
I discovered the reason.
I created a panel over this QFrame when i hovered.
When i moved the mouse, since the panel is positioned on the right bottom side of the cursor position, everytime i moved over the QFrame to the right or bottom, when i hovered this new panel i created, it looses the focus, so it gets closed again, then another hover comes, and then it´s created again... and again... everytime i hover the new panel.

Page Transitions not working for ForwardIn upon navigation

I have multiple pages in my WP7 app and I move in and out of apps by selections made during usage of the app. it is a data heavy app, but i'm not doing anything else but fill up the control by using its own ViewModel.
The ForwardIn transition animation fails to show up - which results in an ugly black screen pause for about 1.5 seconds and the page suddenly seems to popup. I have a white background in some pages so after appearing the phone tries to adjust the brightness automatically, which looks bad too. is there anythign specific I need to look out for?
Is there a way I can preload the page before navigation so that it happens smoothly, I'm using the performance progress bar in the previous pages to load data anyway. are there any ways to profile this page load so that I can check what took the most time.
I have seen this effect a lot when binding to lists. What I do is delay the binding until after the navigation/animation is complete. I usually show a progress bar after animation completes, wait about 50ms on a background thread to allow the progress bar to update it's UI and then bind to the list and hide the progress bar.
Here is the code I run when the animation completes:
progressBar.IsIndeterminate = true;
progressBar.Visibility = Visibility.Visible;
SynchronizationContext context = SynchronizationContext.Current;
Thread t = new Thread(() =>
{
/* Allow the UI to catch up */
Thread.Sleep(50);
context.Post((state) =>
{
list.ItemsSource = dataSource;
/* Hide the progress bar */
progressBar.IsIndeterminate = false;
progressBar.Visibility = Visibility.Collapsed;
}, null);
});
t.IsBackground = true;
t.Start();

Resources