Xamarin.forms listview - System.ArgumentOutOfRangeException - c#

Currently in the process of trying to build a simple app, I want to create an object using Sqlite and then navigate back to overview so I can see the created item in my listview.
Creating the objects and storing works fine, but I'm getting a System.ArgumentOutOfRangeException when trying to navigate back to the overview.
The weird thing is, that when I restart the app my model is correctly shown in the table. Meaning that it did in fact get created.
Code as follows:
// Code in viewModel
async Task Store()
{
// Do some db operations
await Shell.Current.GoToAsync($"/dashboard");
}
It works fine the first time I navigate but after the second time I will get the following error:
\*\*System.ArgumentOutOfRangeException:\*\* 'Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index'
I've tried to put the navigation part in a try/catch block but with no success.
I don't understand why it works the first time but not the second time around.

Related

calling StateHasChanged() async in Blazor Server-Side application effects all open browsers, how can I stop this?

This is my first question here so please go easy on me. :)
I'm also new to Blazor but not c#.
I am building a Blazor server-side application where a user can enter some information and check if it's valid or not based on some data on the server.
So far I have no issues with calling the function of the class that does the lookup and returns a record to display on the browser.
My problem comes when I want to display that information.
In the c# code, I have a variable called SdItem which contains the record that comes back from the query.
To display the data on the razor page I use the following lines.
#if (SdItem != null)
{
<div>
Code: #SdItem.Code<br />
Desc.: #SdItem.Desc<br />
</div>
}
When SdItem gets the data it obviously does not display the information until I call StateHasChanged();
This of course throws an exception so I had to change it to await InvokeAsync(StateHasChanged);
Although after this change the information shows on the screen and all seems to be fine, I came across a new issue which I could not find a solution or an explanation anywhere on the internet.
During debugging, MS Edge automatically starts and displays the website.
To test this strange issue I also start Firefox to point to the same local address.
Then I use my smartphone as well and that is where I start the query.
When I get the results back, not only do they show up on the phone but on all active browsers that are currently displaying the site.
Why does this happen and how can I stop it.
At the moment I managed to stop this from happening with an ugly code
try { StateHasChanged(); } catch { }
This suppresses the exception and the result is only being displayed on the browser that does the request. This is ugly and I don't like to use it.
Any help would be appreciated.
Just as information, in case this could be the reason. The queried data is in a List in a class which is added as a scoped service. But T is a private variable in the code on the razor file.
"When SdItem gets the data it obviously does not display the information until I call StateHasChanged" Why is this obvious? I would expect Blazor to update the display whenever displayed data is changed. You should very rarely need to call StateHasChanged. This makes me wonder what else is going on.
First thought that springs to mind is that SdItem should be a property...
public Whatever SdItem { get; set; }
...and not a private field...
private Whatever SdItem;
I'm not sure if needs to be public, but when using properties, Blazor (almost) always updates the display. When using private fields, if often doesn't.
"This of course throws an exception" Again, I don't see why this is so obvious that you say "of course." It all depends on where you are calling StateHasChanged. Yes, if you're inside an async block then you'll need to call InvokeAsync, but if you arent, then you shouldn't need to do that.
More code would be useful, as its hard to know exactly what you're doing from the small snippet you provided, but try using a property (if you aren't already), and see if that avoids the need to call StateHasChanged.
If not, please update your question with more information.

Xamarin Android - OutOfMemoryError exception - large image

I'm getting an InflateException preceded by an OutOfMemoryError exception thrown. I understand that I should be able to solve the issue by either reducing the size/resolution of the image, or by adding android:largeHeap="true" to my manifest, but something peculiar is happening.
This exception is not thrown when I first inflate the view. I have an Activity that I'm using which contains a PreferenceFragment. When you select a row in the PreferenceFragment you are brought to a Fragment which shows some images and text. The exception is only thrown after navigating back and forth between several of these fragments. It seems to me that something is not being properly disposed of since this will occur on any one of the fragments, but never occurs the first, second, or third time.
Is there a way I can ensure that everything is being disposed of when going back from one of these fragments?
Line where exception is thrown in OnCreateView of Fragment:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(MyResource, container, false);
}
Also worth noting, exception is only thrown after several different fragments are opened from the activity. You could go back and forth between the same one all day and the exception would not be thrown.
I came across something similar while developing one of my apps. In my app, clicking a list item, showed the user an image in a separate fragment. After clicking three or four list items. I saw an out of memory exception, similar to the one you describe.
I solved this by explicitly calling the garbage collection in my image display fragment. C# code for Xamarin as follows:
System.GC.Collect ();
I ported this from Android to C#
https://github.com/koush/UrlImageViewHelper
To deal with large images, I can post the code if you need it.

pass variables as array specflow c#

I am attempting to use Specflow to automate web tests using Selenium. So far, things are going mostly fine, but I am now running into a problem. One of my steps allow for a user to input a variable, the step looks like this:
Given I click the (VARIABLE) Menu
And the code behind it is fairly simple, just clicking on a link based on the text that is passed:
driver.FindElement(By.XPath("Xpath to get to the variable")).Click();
However, there is a later step that must use this information. That is fine, you can use "ScenarioContext.Current.Add(string, variable)" and I know about that and have been using it. It functions for the needs that I was first informed of.
My problem is that now the business wants to be able to add multiple items at the same time. This presents two problems. Attempting to just call the step a second time throws an exception: "An item with the same key has already been added." and if I put this into a Scenario Outline, which would allow me to call the variable a second time in a second run, I cannot use the first variable in the final step.
Logically, this means that passing in a variable multiple times is the problem (which makes sense, given it's passing in as a string) and so passing the variable in as an array seems the logical way to go. The idea is that when I pass the parameter from one step to another as an array instead of as a string I theoretically won't run into this error and then I will be able to iterate through the items in the array in that later step with a for loop. This seems like something that SpecFlow should be able to do, but I am having issues finding out just how to achieve this. Does anyone have an idea on how to do this? I attempted to merely use:
Scenario.Context.Current.Add(string, variable).ToArray();
However, that does not work, and all of the examples of "ToArray" I can find in the SpecFlow documentation doesn't seem to be actually changing the variables you pass from one step to another into an array, it seems to be used solely inside of individual steps and never passed between steps. Is passing parameters using ScenarioContext.Current.Add(string, variable) as an array possible in SpecFlow?
Thanks in advance.
the simplest solution to your problem is to add an array (or list) to the context in the first step and then to get it out and add to it and then replace it again in future steps:
List<string> list = new List<String>();
list.Add(variable)
ScenarioContext.Current.Add(name, list);
then later
List<String> currentList = (List<String>) ScenarioContext.Current[string];
currentList.Add(variable);
ScenarioContext.Current[name]=list;
However I feel duty bound to point out some issues with your current solution. You should investigate the PageObject pattern and hide your element selection XPath inside your page objects. Imagine the business decides to change the element that information is stored in. Now you have to change every test that does this:
driver.FindElement(By.XPath("Xpath to get to the variable")).Click();
for that variable. Using the page object pattern this is hidden inside the page object and you would only have a single place to change.
I personally would also consider sharing data using context injection as I find this allows strong typing of the data (so no cast is required like in the example above) and it allows you to know what data is stored, its not just a random bag of stuff).

ArgumentException when trying to add a child to Canvas

I have some mysterious ArgumentException I have been beating the whole day - still have no idea why does it happen.
I have the next simple method in my MainPage:
public void FavsRefresh()
{
favsCanvas.Children.Clear();
for (short i = 0; i < (App.Current as App).favUnits.Count; i++)
{
FavsItems tmpUnit;
(App.Current as App).favUnits.TryGetValue((App.Current as App).ids[i], out tmpUnit);
Canvas.SetTop(tmpUnit.subCanvas, i * 120);
favsCanvas.Children.Add(tmpUnit.subCanvas);
}
}
Here tmpUnit is an instance of my class FavsCanvas. Its code doesn't matter - it merges some elements into Canvas, which is called here subCanvas and a series of them must be added into parent Canvas, called favsCanvas.
The sense in all this, that we have several items initially and the user may delete existing and add new. Every time an item is deleted or added I call this procedure (including initially program loading).
The joke is that it works during loading and when I call it from another pages, but when I call it from class method throws an exception, besides it adds the first element properly and refuses to do that with others.
Every item has unique name, I even tried not to use names at all or use random ones - not a chance. I have no idea why this exception appears?!
I call this method using following:
MainPage.MPInstance.FavsRefresh();
This way works good from another pages, but from class - fails. I even left only one line (simple reload those items in Canvas):
private void FavMenuItem_Click(object sender, RoutedEventArgs e)
{
//Delete favorite
if (((MenuItem)sender).Header.ToString() == AppRes.FavsMenuDeleteFav)
{
MainPage.MPInstance.FavsRefresh();
}
}
The fun is that this code worked when I wrote it first a couple weeks ago, but now somehow stopped.
Another thing I tried is to make this particular call from a method in App.xaml.cs, which in its turn is called from the class, but it didn't help either.
In fact I have studied most of parameters - everything is the same in both cases: when it works and when not, except the place from where the method is called. But I don't see any proper alternative.
--- added 05 Aug.
I am not sure if it is important, but it always point the next line after the line where exception is thrown:
(the forum does not allow me to post images, so here.
I tried to move this method to class both to class itself and to App.xaml.cs - the same problem.
It works properly when is called during loading (my MainPage is Pivot and this page which contains this favCanvas is one of the pivots, but not the first) and when I call it from another page while overriding its OnNavigatingFrom. And when it is called while the the MainPage and this pivot is active. May be something with that?
Well guys, I still can't catch the reason itself, but at last have found a dirty way to walk around.
The page, for some reason, does not like to be modified when it is active (at least this way). So for now I am forced to simply redirect to another page, where I just call my method and then go back.
If you have some ideas, they are still of demand as my current was is like a crutch and I dislike it

Loading Dataset for two different sessions

I am experiencing a problem with loading of my work order page.
When I load my page I am just passing the id of the work order WO.aspx?inspectionid=12345
Now on computer 1 I am loading
WO.aspx?inspectionid=1
Now on computer 2 I am loading
WO.aspx?inspectionid=2
To reproduce my issue I click the respective inspection on each separate computer at the exact same time (well as close as I can to a manual simultaneous click)
Now when I debug my LoadWorkOrder function in my code, it calls a stored procedure and populates a Dataset. Now before the first call completes and loads up the rest of the page, I see that my second call is already hitting my LoadWorkOrder function, thus causing one of the pages to error out.
So my question is, is there a way to properly handle multiple calls on the same function. Is this a threading related issue? I am not quite sure even what to google to help solve my issue.
private void LoadWorkOrder()
{
//Bizlogic.cInspectionsBizLogic.GetInspection(dstInspection, mintInspectionID);
dstInspection = new cInspectionsDST();
Bizlogic.cInspectionsBizLogic.GetInspection(ref objDataAdapterForUpdate, dstInspection, mintInspectionID);
if (dstInspection != null && dstInspection.Tables[0].Rows.Count > 0)
{
Bizlogic.cSchedulerDST dstItem = new cSchedulerDST();
dstItem = Bizlogic.cSchedulerBizLogic.GetScheduleItemForInspectionID(mintInspectionID);
LoadInspectionAddressHeader();
I don't see anything special here, its just a simple loading of a dataset. The error message I do seem to be getting "There is no row at position 0". Within my cInspectionsDST.Designer.cs file. These DataSets are created within Visual Studio I've inherited so changing those is unfortunately not an option.
dstInspection was a static variable once I removed that and reworked the code I no longer had this issue

Categories