Xamarin app crashes on ObservableCollection.Add? - xamarin

I have an Xamarin.Forms app running on iOS simulator that just stops as soon as it hits code which adds a new member to an observable collection that is the template source for a CarouselView. Oddly, it doesnt happen immediately, but only when I add more items to the collection when the user gets close to the end of the carousel. I can't trap the error in a try catch block. In debug mode, when I stop at the line in question and either step in or step over, the app just closes and goes back to the home screen. I recently updated my packages, maybe there is some issue there?
Code as follows:
private ObservableCollection<ResultsScroller> mySource;
public void PopulateResultsPages(List<NominalResult> resultList)
{
foreach (var nr in resultList)
{
var template = new ResultsScroller();
template.LoadData(nr);
try
{
mySource.Add(template); // <----app quits here
}
catch (System.Exception e)
{
System.Diagnostics.Debug.WriteLine(e.Message);
}
}
lastPosition = mySource.Count - 1;
}

initialize observablecollevtion mySource in constructor like this
mySource = new ObservableCollection<ResultsScroller>();
after that add item in it.

Currently you have to somehow programmatically make the ListView appear before adding an item. If the ListView is inside a tab that is not visible, show it by setting the CurrentPage Property. If it is in a MasterDetailPage, you could try showing it by toggling the IsPresented Property.
An upcoming Update of XF should fix this issue:
- https://github.com/xamarin/Xamarin.Forms/issues/1542
- https://github.com/xamarin/Xamarin.Forms/issues/1927

Related

Prompts on top of popup windows in Xamarin and MAUI

I have discovered the limitation of displaying prompts when they are invoked from a popup window. Specifically verified with CommunityToolkit.Maui Popups.
Here's the details:
In the Map page I have this handler for the map clicked event:
void mapClicked(object sender, MapClickedEventArgs e) {
var pin = new Pin {
Label = "Here's where it is",
Location = e.Location
};
map.Pins.Add(pin);
}
I wanted to allow the user to edit the pin label when clicking on the it, like so:
pin.InfoWindowClicked += async (s, args) => {
string pinName = ((Pin)s).Label;
await DisplayPromptAsync("Enter new label", "enter new label");
};
However, this didn't work as no DisplayPrompt was shown. I tried running it in the main thread, to no avail either.
UPDATE. I've figured it out, see answer below.
The problem arises when attempting to bring up the prompt from a popup window. Evidently, one can't have a DisplayPromptAsync (or DisplayAlert for that matter) on top of a popup.
On a platform-specific level in iOS the error message reads:
Attempt to present <UIAlertController> on <Microsoft_Maui_Controls_Platform_Compatibility_ShellFlyoutRenderer> (from <Microsoft_Maui_Controls_Platform_Compatibility_ShellFlyoutRenderer>) which is already presenting <CommunityToolkit_Maui_Core_Views_MauiPopup>.

Will it be a problem to have a TapGesture added inside of a data template in Xamarin? Should I somehow remove the event I added?

Within my DataTemplate (written in C#), I have this code:
var plusMinusGrid = new Grid
{
Children =
{
_minusFrame.Column(0).Bind(IsVisibleProperty, nameof(DeckRow.FRMIsVisible), source: this),
_plusFrame.Column(0).Bind(IsVisibleProperty, nameof(DeckRow.FRPIsVisible), source: this)
},
};
var plusMinusTapGesture = new TapGestureRecognizer();
plusMinusTapGesture.Tapped += PlusMinusTap;
plusMinusGrid.GestureRecognizers.Add(plusMinusTapGesture);
So I am adding the tap event to a part of each row.
My question is, will this be an issue as a memory leak and if that's the case is there a way that I can deal with this.
Here is what I have. Override the Disappearing method add a call to a CleanUp() that you write yourself. As I am doing the += on in the view model I add the -= there also. This is annoying as I also found that I needed to set itemsource to null, but itemsource is in the view. Since Disappearing is part of the view, I call the VM's CleanUp() method!
As my page is used modally this works fine. If your page is not modal, and you refresh the grid, make sure you do the -= on existing items before you repopulate.
View page :
public void CleanUp()
{
this.athleteDetailVM.CleanUp();
this.collectionV.ItemsSource = null;
}
VM Implementation:
public void CleanUp()
{
foreach (CheckedItem<EventResult> item in eventResults)
{
item.PropertyChanged -= null;
}
}

Why does SkiaSharp Touch SKTouchAction.Moved event not work?

Summary
The Touch event is never raised with the ActionType of SKTouchAction.Moved, but SKTouchAction.Pressed is raised. Why does the .Moved event never get raised?
Detail
I am attempting to create a slider in SkiaSharp. This control will need to update the thumb (the little movable circle) when the user either touches on the slider or drags. This is hosted in a Xamarin Forms app, and I have created a SKCanvasView to draw the slider and to respond to touch events.
I have successfully responded to touch events, so I know the SKCanvasView is receiving some UI events, but the SKTouchAction.Moved is never raised.
Example Code
private SKCanvasView CreateSliderControl()
{
var control = new SKCanvasView();
control.PaintSurface += HandlePaintHeightControl;
control.EnableTouchEvents = true;
control.Touch += (sender, args) =>
{
var pt = args.Location;
switch (args.ActionType)
{
case SKTouchAction.Pressed:
// 1. This code gets hit whenever I touch the canvas
control.InvalidateSurface();
break;
case SKTouchAction.Moved:
// 2. This code never gets hit, even if I push, touch, slide, etc
control.InvalidateSurface();
break;
}
};
control.InputTransparent = false;
return control;
}
As shown above #1 gets hit (I can put a breakpoint there, and my control surface is invalidated). #2 never gets hit ever, but I expect it to get hit when I slide my finger over the control.
What I've Tried
Clean/rebuild to make sure my code actually was being deployed
Upgrade from SkiaSharp 1.60.3 to 1.68.0
All the crazy finger movements I could come up with
Read through https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/transforms/touch , but that uses XAML to assign an Effect (which I'm not familiar with) and I don't see a TouchEffect class available anywhere in my code to add it to the parent Grid that contains my SkiaCanvasView.
Found a similar issue on SkiaSharp's Github.
Try telling the OS that you want to "continue receiving touch events" by setting the Handled property in the event's args to true.
private SKCanvasView CreateSliderControl()
{
var control = new SKCanvasView();
control.PaintSurface += HandlePaintHeightControl;
control.EnableTouchEvents = true;
control.Touch += (sender, args) =>
{
var pt = args.Location;
switch (args.ActionType)
{
case SKTouchAction.Pressed:
control.InvalidateSurface();
break;
case SKTouchAction.Moved:
control.InvalidateSurface();
break;
}
// Let the OS know that we want to receive more touch events
args.Handled = true;
};
control.InputTransparent = false;
return control;
}

JavaFX button event triggers only once

So i have this JavaFX application, which contains a button, that is supposed to open the DirectoryChooser onclick. When i trigger it once, it does what it was supposed to do, perfectly fine. As soon as i close the DirectoryChooser Dialog, the button doesn't do anything anymore. I was searching the web for some "event resetting" or something similar, because i thought maybe the Event was still "active" and therefore doesn't trigger anymore, but without any results:
// first attempt
button.addEventFilter(MouseEvent.MOUSE_CLICKED,
new EventHandler<MouseEvent>() {
public void handle(MouseEvent e) {
// dirChooser.setTitle("Select Directory:");
file = dirChooser.showDialog(primaryStage);
// just incase only the DirectoryChooser wasn't opening
System.out.println("asdf");
// updates the application view with the new selected path
update();
// not sure, if this affects anything
// found it while looking for resetting of events
e.consume();
};
}
);
// secont attempt
button.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
DirectoryChooser dirChooser = new DirectoryChooser();
dirChooser.setTitle("Select Directory:");
file = dirChooser.showDialog(primaryStage);
update();
}
});
Not sure if this is just a complete wrong approach, or if i'm missing something important there. I hope you guys can figure it out.
So after the help of Uluk Biy, i got the problem. In the update routine, the button is newly created and therefore it's event handler is not existent anymore. I added the button to the attributes so I don't need to replace it everytime when calling the update routine, and the event handler is still existent.

Windows Phone 7: How to notify data is updated in ListBox?

I have above scenario: If user click on ListBox, it will either have sub items (again ListBox) or detail view.
So what i did currently is: Whenever user clicks any item, made a web call and filled up the same ListBox if clicked item is having further sub items.
Now, issue comes in picture:
Suppose i am in 4th screen (detail view),
Moved to the 3rd and 2nd screen with data maintained as stack (Its working fine, yes i am maintaining data in ObservableCollection<ObservableCollection<MyObjects>> so while moving back, i am fetching data from there)
Now, if i click any item in screen 2, it will open detail view for the screen 3 ListBox data.
Means that ListBox is not getting notified that we have filled data inside OnBackKeyPress()
FYI, i am filling up ListBox and WebBrowser in the same page., so my problem is that how do i notify ListBox once i filled up data from stack which i have maintained?
Yes i have also implemented INotifyPropertyChanged but don't know why its not working.
Please check my code:
ListBox and WebView screen: http://pastebin.com/K1G27Yji
RootPageItem class file with the implementation of INotifyPropertyChanged: http://pastebin.com/E0uqLtVG
sorry for pasting code in above way, i did as question is being long.
Problem:
How do i notify ListBox that data is changed from OnBackKeyPress?
And what is the behavior if you set:
listBox1.ItemsSource = null;
before
listBox1.ItemsSource = listRootPageItems;
This is just wrong architecture. Instead of reloading the same listbox, please add a single page for each screen. Share data between them inside the App class (internal static) and use the built in navigation stack for handling "going back". Don't override OnBackKeyPress for this purpose.
You will get your desired functionality for "free" with easier to maintain and use codebase.
Oops it was a silly mistake i made.
I forgot to set items[] array inside OnBackKeyPress() but was accessing while clicking item, hence its having items[] data of last step we moved in forward direction, it was executing the same data.
Now, i have just included a single line and it has solved my problem.
items = listRootPageItems.ToArray(); // resolution point
So final code of onBackKeyPress() is:
/**
* While moving back, taking data from stack and displayed inside the same ListBox
* */
protected override void OnBackKeyPress(CancelEventArgs e)
{
listBox1.Visibility = Visibility.Visible;
webBrowser1.Visibility = Visibility.Collapsed;
listBox1.SelectedIndex = -1;
if (dataStack.Count != 0)
{
listBox1.ItemsSource = null;
listRootPageItems = dataStack[dataStack.Count-1];
listBox1.ItemsSource = listRootPageItems;
items = listRootPageItems.ToArray(); // resolution point
dataStack.Remove(listRootPageItems);
e.Cancel = true;
}
}

Resources