I know i know: probably it is one of the most asked questions. Before you send me some LMGTFY links, let me tell that I've been some hours with this question, and I've made some tries with Invalidate, PostInvalidate, RunOnUIThread, etc; with no success. I do not discard that the solution can be one of the previously mentioned, and I have not been using it properly. I am actually learning Xamarin, doing my first crossplatform app, so my knowledge of the framework is very poor.
So now let's go to my concrete problem, and let's see if someone can help me. I want a intro page for my app, with a progress bar and a text below saying what is doing the app (when starting the app, it calls a WS to download changes, and has to load info from text files and put it into some static data structures to use for all pages). What i want to do in the loading page is the following sequence:
1. Change text to tell what is the app doing.
2. Call WS or load a file.
3. Update progress bar.
4. Go to next update or to the welcome page if all is loaded.
What i get for my actual code is that the page is load when all the stuff is done, so I see the progress bar completed and the last text change. But is a static page, i don't see the progress bar growing neither the text changing.
This is my code:
public partial class LoadingPage : ContentPage
{
InitializeComponent();
this.lpb.Text = "Connecting to web server to check updates";
App.localInfo.updateInfo(); //Connect web server to check updates
this.pb.Progress = 0.2;
this.lpb.Text = "Loading info from local files";
App.localInfo.cargarInfo(); //Load local files to memory for quick access
this.pb.Progress = 0.7;
this.lpb.Text = "Doing other stuff"; //This is only for proves
Task.Run(async () =>
{
await Task.Delay(2000);
});
this.pb.Progress = 1;
this.lpb.Text = "Load completed. The app will start now";
}
And this is my ContentPage:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Prueba1.Views.LoadingPage">
<ContentPage.Content>
<StackLayout>
<ProgressBar x:Name="pb" Progress="0.0" ProgressColor="Red"/>
<Label x:Name="lpb" Text="Welcome to Xamarin.Forms!"/>
</StackLayout>
</ContentPage.Content>
</ContentPage>
This is only the alpha version. I would like to concrete a little more, because I have to load around 10 different text files, and I would like to update progress bar and label inside the App.localInfo methods. But first I have to learn how to do this simple stuff, and then trying something more complicated.
Thanks in advance!
Instead of setting the progress property like you are, try using the progress bar's ProgressTo method inside an async method. Something like:
public MainPage()
{
InitializeComponent();
FireProgressBar();
}
async void FireProgressBar()
{
lpb.Text = "Connecting to web server to check updates";
// Task.Delay to simulate network call.
await Task.Delay(2000);
await pb.ProgressTo(.2, 250, Easing.Linear);
lpb.Text = "Loading info from local files";
// Task.Delay to simulate network call.
await Task.Delay(2000);
await pb.ProgressTo(.7, 250, Easing.Linear);
lpb.Text = "Doing other stuff";
// Task.Delay to simulate network call.
await Task.Delay(2000);
await pb.ProgressTo(1.0, 250, Easing.Linear);
lpb.Text = "Load completed. The app will start now";
}
This has the added benefit of actually seeing the progress bar moving, not just jerking from one value to the next.
Related
I am trying to open a page in a xamarin forms project in this away:
public async Task<string> AbrirPaginaEscaner()
{
ScannerView miScannerView = new ScannerView();
ScannerViewModel miScannerViewModel = miScannerView.BindingContext as ScannerViewModel;
await _navigation.PushModalAsync(miScannerView);
//await _navigation.PopModalAsync();
return miScannerViewModel.CodigoQr;
}
The new page is opened, but I was expecting that code is not continuing until I close the new page, but the code return the result before the page is close.
Why the code is continuing beyond the await? How could I wait until the second page is close? I have commented the line in which I close the page, becuase if not, I can't see the page opened because it is close immediately.
Thanks.
My Dialog is a simple Frame with an Image, a label to display a question and two more labels (Yes / No) with TapCommand.
I've set up the container with the DialogPage.xaml and DialogPageViewModel and injected in the ViewModel I want to open the dialog.
Here is the code I'm using to call the Dialog:
public void ShowDialog()
{
_dialogService.ShowDialog("DiscardPopup", CloseDialogCallback);
}
void CloseDialogCallback(IDialogResult dialogResult)
{
var goBack = dialogResult.Parameters.GetValue<bool>("GoBack");
if (goBack)
NavigationService.GoBackAsync();
}
If the user taps over the "Yes label", I execute this command:
YesCommand = new DelegateCommand(() => YesTapped());
private void YesTapped()
{
IDialogParameters pa = new DialogParameters();
pa.Add("GoBack", true);
RequestClose(pa);
}
If the user taps over the "No label", I simply call:
NoCommand = new DelegateCommand(() => RequestClose(null));
The "problem" is when the ShowDialog is fired, the DiscardPopup is taking up to 3 seconds to show up.
Is there a way to make it faster?
The same happens with the TapCommands, 2 - 3 seconds when the RequestClose is invoked.
Without actual code telling you exactly what the issue is, is going to be best guess. Based on your feedback to my comments above I would suggest the following:
Try displaying the dialog on a test page that doesn't have a complex layout. My guess is that you won't see such a long load time. If that's the case this would point to your layout being overly complex and that the lag time is due to the device struggling to re-render the View
Try using Prism.Plugin.Popups. You'll need to initialize Rg.Plugins.Popup and register the DialogService. You can see docs on that at http://popups.prismplugins.com
I have an activity indicator on xaml page. Initially its IsVisible property is false. I have a button on page. When user click on button it calls a web service to get data. I change the value of IsVisible property to true before calling the service so that activity indicator starts to display on page and after successful calling of service I change its value to again false so that it doesn't show any more on page.
But it is not working. I know the actual problem. When we call the web service the UI thread gets block and it doesn't show the activity indicator.
How I can enable the UI thread when web service gets called so that activity indicator can show on page until we get the data?
Try making your webservice call into an async and await it.
Depending on how you've structured things you may have to use a TaskCompletionSource as the following example demonstrates.
In this example when the button is clicked, the button is made invisible, and the ActivityIndicator is set to IsRunning=True to show it.
It then executes your long running task / webservice in the function ExecuteSomeLongTask using a TaskCompletionSource.
The reason for this is that in our button click code, we have the final lines:-
objActivityIndicator1.IsRunning = false;
objButton1.IsVisible = true;
That stop the ActivityIndicator from running and showing, and also set the button back to a visible state.
If we did not use a TaskCompletionSource these lines would execute immediately after calling the ExecuteSomeLongTask if it was a normal async method / function, and would result in the ActivityIndicator not running and the button still being visible.
Example:-
Grid objGrid = new Grid()
{
};
ActivityIndicator objActivityIndicator1 = new ActivityIndicator();
objGrid.Children.Add(objActivityIndicator1);
Button objButton1 = new Button();
objButton1.Text = "Execute webservice call.";
objButton1.Clicked += (async (o2, e2) =>
{
objButton1.IsVisible = false;
objActivityIndicator1.IsRunning = true;
//
bool blnResult = await ExecuteSomeLongTask();
//
objActivityIndicator1.IsRunning = false;
objButton1.IsVisible = true;
});
objGrid.Children.Add(objButton1);
return objGrid;
Supporting function:-
private Task<bool> ExecuteSomeLongTask()
{
TaskCompletionSource<bool> objTaskCompletionSource1 = new TaskCompletionSource<bool>();
//
Xamarin.Forms.Device.StartTimer(new TimeSpan(0, 0, 5), new Func<bool>(() =>
{
objTaskCompletionSource1.SetResult(true);
//
return false;
}));
//
return objTaskCompletionSource1.Task;
}
You need to do your work in an asynchronous way. Or in other words: Use Asnyc & Await to ensure, that you UI works well during the call.
You can find more informations in the Xamarin Docs.
async and await are new C# language features that work in conjunction
with the Task Parallel Library to make it easy to write threaded code
to perform long-running tasks without blocking the main thread of your
application.
If you need further asistance, please update your question and post your code or what you have tried so far.
I am currently assisting in a proof of concept using Selenium 2 / WebDriver with C# against an ASP.NET MVC application using the InternetExplorerDriver.
The application uses a standard pattern for notifying users that a record has saved. This works by settings TempData to include "Record saved successefully", and if TempData is present in the View, the view will alert the message.
Whilst working on Selenium tests for this functionality, we are receiving inconstitant behaviour from the below C# / Selenium test code:
_driver.Navigate().GoToUrl(_baseUrl + "/Amraam/List");
_driver.FindElement(By.LinkText("Create New")).Click();
_driver.FindElement(By.Id("txtAmraamSerialNumber")).SendKeys("CC12345");
var selectElement = new SelectElement(_driver.FindElement(By.Id("LocationId")));
selectElement.SelectByText("Tamworth");
_driver.FindElement(By.Id("btnSave")).Click();
var wait = new WebDriverWait(_driver, defaultTimeout);
IAlert alert = wait.Until(drv => drv.SwitchTo().Alert());
_alertText = alert.Text;
alert.Accept();
Assert.That(_alertText, Is.EqualTo("Record successfully saved"));
Approximately 50% of the time, Selinium will fail with a
OpenQA.Selenium.NoAlertPresentException : No alert is active
I struggle to find an exact way to replicate the issue, and worry regarding the inconsistency aspect. If it failed consistently, then we could debug and track the problem down.
The handling of alerts and prompts within Selenium 2 is pretty new and is still under active development.
Your failures are probably due to timing, so I would suggest writing a wrapper method around the call to SwitchTo().Alert() so that you catch the OpenQA.Selenium.NoAlertPresentException and ignore it until the timeout expires.
Something as simple as this should work:
private IAlert AlertIsPresent(IWebDriver drv)
{
try
{
// Attempt to switch to an alert
return drv.SwitchTo().Alert();
}
catch (OpenQA.Selenium.NoAlertPresentException)
{
// We ignore this execption, as it means there is no alert present...yet.
return null;
}
// Other exceptions will be ignored and up the stack
}
This line
IAlert alert = wait.Until(drv => drv.SwitchTo().Alert());
would then become
IAlert alert = wait.Until(drv => AlertIsPresent(drv));
Hope this will be helpful for waiting and click
WebDriverWait(driver,4).until(EC.alert_is_present())
driver.switch_to.alert.accept()
I have an ASP.NET Web Forms application. I want to have a button to post back to the server that will use my fields on my form (after validation) as parameters to a server process that will generate a document and stream it back to the browser. I want the form to be updated with some status results.
What is the best way to achieve this? Right now, I've got the button click generating the document and streaming it back to the browser (it's a Word document and the dialog pops up, and the Word document can be opened successfully) but the page doesn't get updated.
I have jQuery in my solution, so using js isn't an issue if that is required.
I have a very similar process on one of my servers, and the way I've handled it is to create a temporary document on the server rather than doing a live stream. It requires a bit of housekeeping code to tidy it up, but it does mean that you can return the results of the generation and then do a client-side redirect to the generated document if successful. In my case, I'm using jQuery and AJAX to do the document generation and page update, but the same principle should also apply to a pure WebForms approach.
This was way more difficult to do than I thought. The main issue is with opening a new browser window for a Word document. The window briefly flashes up, then closes - no Word document appears. It seems to be a security issue.
If i click a button on my page, I can stream the Word doc back as the response, and the browser dialog pops up allowing me to Open/Save/Cancel, but of course, my page doesn't refresh.
My final solution to this was to use a client script on the button click to temporarily set the form's target to _blank. This forces the response to the click on the postback to go to a new browser window (which automatically closes after the download dialog is dismissed):
<asp:Button Text="Generate Doc" runat="server" ID="btnGenerateDoc"
onclick="btnGenerateDoc_Click" OnClientClick="SetupPageRefresh()" />
My SetupPageRefresh function is as follows:
function SetupPageRefresh() {
// Force the button to open a new browser window.
form1.target = '_blank';
// Immediately reset the form's target back to this page, and setup a poll
// to the server to wait until the document has been generated.
setTimeout("OnTimeout();", 1);
}
Then my OnTimeout function resets the target for the form, then starts polling a web service to wait until the server process is complete. (I have a counter in my Session that I update once the process has completed.)
function OnTimeout() {
// Reset the form's target back to this page (from _blank).
form1.target = '_self';
// Poll for a change.
Poll();
}
And the Poll function simply uses jQuery's ajax function to poll my web service:
function Poll() {
var currentCount = $("#hidCount").val();
$.ajax({
url: "/WebService1.asmx/CheckCount",
data: JSON.stringify({ currentCount: currentCount }),
success: function (data) {
var changed = data.d;
if (changed) {
// Change recorded, so refresh the page.
window.location = window.location;
}
else {
// No change - check again in 1 second.
setTimeout("Poll();", 1000);
}
}
});
}
So this does a 1 second poll to my web service waiting for the Session's counter to change from the value in the hidden field on the page. This means it doesn't matter how long the server process takes to generate the Word document (and update the database, etc.) the page won't refresh until it's done.
When the web service call comes back with true, the page is refreshed with the window.location = window.location statement.
For completeness, my Web Service looks like this:
/// <summary>
/// Summary description for WebService1
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
[System.Web.Script.Services.ScriptService]
public class WebService1 : WebService
{
[WebMethod(EnableSession=true)]
public bool CheckCount(int currentCount)
{
if (Session["Count"] == null)
Session["Count"] = 0;
var count = (int)Session["Count"];
var changed = count != currentCount;
return changed;
}
}
Hopefully that helps somebody else!