Why ActivityIndicator changes state after entire method is completed? - xamarin.forms

I would like to show ActivityIndicator object after user tap the login button on page. Unfortunately there is small problem to do that because it seems like ActivityIndicator change state after entire method is completed. This is code I wrote so far:
private void Login(object sender, EventArgs ev)
{
BusyIndicator.IsVisible = true; //<- here I want to show indicator
try
{
//some input validation, connection opening etc
ConnectionHandler.OpenConnection(ServerIP, "dmg", false);
}
catch (Exception e)
{
Logging.Error(e.Message, "Connection", e);
}
}
When I set breakpoint after BusyIndicator.IsVisible = true; there is absolutely no change in app. However I noticed that when method is completed then indicator is shown. Is this a correct behavior of this control?
Why I need this? Because field validation and connecting with server takes some time and I need to show to user that something happens in background. Login function takes ~1 sec so indicator show and hide quickly I can't even see any change.
How can I show indicator immediately after user tap a button?

Your problem is that Login() method is being executed in the UI thread. So, despite setting BusyIndicator.IsVisible = true;, the thread continues tio execute the method to get data, so the UI does not respond.
Solution, run the OpenConnection in a different thread:
private async void Login(object sender, EventArgs ev)
{
BusyIndicator.IsVisible = true; //<- here I want to show indicator
try
{
//some input validation, connection opening etc
await Task.Run(() => { ConnectionHandler.OpenConnection(ServerIP, "dmg", false);});
}
catch (Exception e)
{
Logging.Error(e.Message, "Connection", e);
}
}

Related

Xamarin forms DisplayAlert not Awaiting

I have an event that fires on Android Hardware back button pressed. This is how its implemented in the AppShell class:
public event EventHandler<CancelEventArgs> BackButtonPressed;
protected override bool OnBackButtonPressed()
{
var cancelArgs = new CancelEventArgs();
BackButtonPressed?.Invoke(this, cancelArgs);
return (cancelArgs.Cancel) ? true : base.OnBackButtonPressed();
}
Elsewhere I have subscribed to this event in a view model. Here is the code:
private async void _AppShell_BackButtonPressed(object sender, CancelEventArgs e)
{
if (!await App.Current.MainPage.DisplayAlert(
"My App",
"Are you sure you want to cancel, you have unsaved changes",
"ok",
"cancel"))
{
e.Cancel = true;
}
}
Some other stuff happens before DisplayAlert, but for simplicity I have removed all that.
The issue is that when DisplayAlert is called, the execution returns to OnBackButtonPressed and because of this the cancel argument is not set accordingly. So it seems that DisplayAlert is not waiting for the user to respond. How can this problem be solved?
Apologies if I've been unclear, I can provide further clarification.. just ask:)
Any help on this would be much appreciated.
By design my solution is not feasible. The answer is to always cancel out of the OnBackButtonPressed() method and raise a MessagingCenter event like so:
protected override bool OnBackButtonPressed()
{
MessagingCenter.Send(this, "ANDROID_HARDWARE_BACK_BUTTON_TAPPED");
return true;
}
Then when you handle the event call:
await Shell.Current.Navigation.PopAsync(true);
instead of:
await Shell.Current.SendBackButtonPressed
From this I ascertain the best practice is to always use PopAsync instead of SendBackButtonPressed to save you from unwanted recursion.
What about using Display Alert by overriding the OnAppearing() method?
protected async override void OnAppearing() {
var result = await App.Current.MainPage.DisplayAlert(
"My App",
"Are you sure you want to cancel, you have unsaved changes",
"ok",
"cancel"
)
base.OnAppearing();
if (!result)
{
e.Cancel = true;
}
}

Vaadin button setEnabled(false) doesn't enable

Vaadin newbie: When a user presses a button, I like to disable it so he knows that he clicked it and there's some work going on in the background. When the (long) task is completed, I'd like to enable the button.
For this, I'm using 2 threads (background and work) but for some reason the button doesn't enabled at the end of the task.
In other words, once clicked it goes to enabled(false) and never coming back. Why? and how can I fix it?
button.addClickListener(new ClickListener()
{
public void buttonClick(ClickEvent event)
{
Thread background = new Thread(new Runnable(){
#Override
public void run()
{
Thread work = new Thread(new Runnable(){
#Override
public void run()
{
button.setEnabled(false);
try
{
Thread.sleep(2000); //long work here!
} catch (InterruptedException e)
{
e.printStackTrace();
}
button.setEnabled(true); //doesn't enable at the end of the long work!
}});
work.start();
try
{
work.join();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}});
background.start();
}
});
Maybe the best approach would be to use Button.setDisableOnClick(true) for the button and do the processing directly in the event handler without a background thread. This will show the standard loading indicator to the user until processing is done.
Otherwise you need to enable server push (#Push) and remember to use UI.access() in the background thread before updating the UI. See https://vaadin.com/book/-/page/advanced.push.html

Windows Form TopMost don't work with BackgroundWorker?

I'm trying to show window when user need to be notify about some work to do. Every think work fine, but i want to show form absolute topmost. I set form property TopMost = true but it does not work, window still show behind other forms.
I figure out that TopMost = true don't work only with BackgroundWorker, when i use Timer class it work fine. I'm wonder why? Anybody can explain me this?
Here is simple example what i want to do.
static void Main(string[] args)
{
try
{
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += new DoWorkEventHandler(worker_DoWork);
worker.RunWorkerAsync();
Application.Run(new Form());
}
catch (Exception exp)
{
Console.WriteLine(exp);
}
}
static void worker_DoWork(object sender, DoWorkEventArgs e)
{
while (true)
{
System.Threading.Thread.Sleep(1000);
if (NeedNotify())
{
NotifyForm myNotifyForm = new NotifyForm();
myNotifyForm.TopMost = true;
myNotifyForm.ShowDialog(); // NotifyForm still show behind others windows
}
}
}
private static bool NeedNotify()
{
return true;
}
}
Creating the form within the background worker causes the form to be created on a different thread. Instead, create and show the form in your main thread before calling RunWorkerAsync.
Another problem may arise from the fact that you're creating the "notification" before the application's main loop is even started. You may consider reorganizing your code so that the background worker is started from the main form's OnLoad event.

login validation on asp page

Since I'm a newbie for asp, I'm trying to take a name as input,trying to put that name in list, then check the list to find a match. I'm doing this as basics keeping the log in procedure in mind, which I will try to implement later. I have the following code:
I have made a class like this:
public class Login
{
public string name { get; set; }
}
The two button events are as follows:
List<Login> list;
protected void Button1_Click(object sender, EventArgs e)
{
list = new List<Login>(){
new Login { name = TextBox1.Text },
new Login { name = "Badhon"}
};
Label1.Text = "Inserted";
}
protected void btnLogIn_Click(object sender, EventArgs e)
{
foreach (var s in list)
{
if (s.name == TextBox1.Text)
{
Label1.Text = "Found";
break;
}
else
Label1.Text = "Not Found";
}
}
when I'm trying to insert,its working fine, but when clicking on the login button showing any error message like "Object reference not set to an instance of an object."
When you press the login button you're in a different scope to when you pressed the first button, so the list is not initialised (Each time you press an ASP button you get a new state: The web is designed to be stateless).
Why not use the asp:login control?
You code is not exactly what it is supposed to be. You'd better go searching for some (of the various) examples on how to hanle logins in ASP.NET.
Each click is a new HTTP request. The list initialized in the first request is not the same with the list in the other request because each request will use its own instance of the Page object.
You need to read & understand the life cycle of a ASP.net page: http://msdn.microsoft.com/en-us/library/ms178472.aspx
make protected properties to store list of logins
It should looks like this:
protected List<Login> LoginStore
{
get{ return ViewState["store"] =! null ? (List<Login>)ViewState["store"] : new List<Login>; }
set{ ViewState["store"]=value; }
}
You can use Session as well as ViewState. It will make your list doesn't disapear every time you make PostBack.
then in event btnLogIn
create List<Login> list = LoginStore;
then make the rest of your code.
click on here
why do go forloop, use session variables in Global.asax, try googlin you can find many example..

asp.net set sessions before postback

is there an easy way, to store all needed global variables in sessions at once, before the PostBack starts? Or have I to store them in each step where I change them?
I will do something like:
// Global variable.
bool test = true;
// Store all needed information in a session.
protected void Before_globalvariable_is_set_to_default_value(...)
{
Session["Test"] = test;
...
}
protected void Page_Load(object sender, EventArgs e)
{
if(IsPostBack)
{
//if(Session["Test"] != null)
//{
test = (bool)Session["Test"];
Session.Contents.Remove("Test");
//}
}
}
Is something like that possible?
Additional Information
At the Page_Load (!IsPostBack) I check if the user gets more vision, if he gets, I set a global var to true. Later in my code I check if that var is true and add additional columns to a GridView.
Now if a PostBack occurs, I can’t check that var, because I lose the information. I knew that I need to store the information in a Session. If I set the Session at the time where I set the global var to true, I get problems with the session timeout (If the user is on the site, but doesn’t do something for a while). So I thought it will be good, if I set the Session shortly before I lose the information of the global var and delete the Session after reinitialization.
That’s my idea, but I don’t know if something like that is possible.
Edit2:
If I do following it works:
//Global variable
bool test = false;
protected void Page_PreRender(object sender, EventArgs e)
{
Session["Test"] = test;
}
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
test = (bool)Session["Test"]; // Session is true!!!
Session.Contents.Remove("Test");
}
else
{
test = true; // Set at the PageLoad the var to true.
}
}
I’m a little bit confused, I thought PreRender is after the PageLoad, why suddenly the test var is true and if I remove the PreRender it isn’t?
Greetz
If you're worried about losing a specific value between requests, because you've maintained the state of that variable in the Session object and it might have been cleared by a timeout, you could consider using another, more durable, mechanism to save the state: for example, cookies or database.
If the value only needs to live during that one Request, you can use class-level fields of the code-behind class. Set them in the Init or Load phase, then you can use those values in all other phases.
For a lifetime of just a single request:
public partial class MyPage: Page
{
private bool test = true;
public void Page_Load(...)
{
// maybe set 'test' to another value
}
public void Button_Click(...)
{
// you can still access 'test'
}
public void Page_PreRender(...)
{
// you can still access 'test'
}
}
If however you need that value to live from request to the next postback, you can use ViewState instead of Session. Advantage: no timeout as it is stored in the html and posted back from the browser along with other data. Disadvantage: it only works in postback-scanario's, not when you link to a different page.

Resources