How do I Improve CollectionView Performance - xamarin

We have a landing page that has this layout:
When you click on one of the section buttons, the ObservableCollection in the ViewModel is set to another list as so:
private async void processtableOfContentsListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
var tocItem = e?.Item as TableOfContentsItem;
if (viewModel.SelectedTableOfContentsItem == tocItem)
{
return;
}
viewModel.CurrentSectionItems = tocItem.SectionItems;
viewModel.SelectedTableOfContentsItem = tocItem;
}
This is simplified as there's other operations going on.
My problem is that the CollectionView is taking too long to change sections. There's a noticeable delay when trying to load up the new items. To be fair, the DataTemplate's for the items are a little complex. I've simplified them as much as possible, but you can still notice a delay in trying to draw them.
I'd like to speed this change up as much as possible, but its starting to look like I may need to just deal with the delay itself instead of freezing. It would be nice to have a loading indication in between changing sections. Like an ActivityIndicator. But I can't seem to find how to make UI changes in the beginning of the EventHandler for the buttons. Even if I clear the ObservableCollection in the beginning of the event handler, it still will wait until the method finishes before showing any UI changes.
I've checked and the event handler is running on the main UI thread. I've even tried running the rest of the code on a background thread after changing the section to see if it would before continuing the method, but no luck:
private async void processtableOfContentsListView_ItemTapped(object sender, ItemTappedEventArgs e)
{
if (MainThread.IsMainThread) //is true
{
}
var tocItem = e?.Item as TableOfContentsItem;
await Task.Run(async () =>
{
await MainThread.InvokeOnMainThreadAsync(() => {
viewModel.SelectedTableOfContentsItem = tocItem;
viewModel.CurrentSectionItems = tocItem.SectionItems;
});
});
Task.Run(() => DoWork(sender, e));
}
My question is two parted.
Does anyone have an idea how to speed up the redrawing of the CollectionView?
If not, how do I set off the UI change to clear the CollectionView in the beginning of the event handler so I can add a loading indicator?

I ended up making separate collection views for each section. There is a delay in initializing the list when you first switch to it, but switching between sections has become instant.

Related

Xamarin, how can I gain a completed state from a command?

I have a ListView and am using the SelectedItem to call a command and show a modal view.
However, I have an issue where the user can tap multiple times on the listview row and multiple modal views are shown before the view has loaded. Granted this only happens on slower devices.
This is is caused because the command doesn't have any call back.
I wouldn't normally paste code here, but in this case I thought it was more descriptive to provide a screen shot.
I've looked into the AsyncCommands but these seem to be used more to handle errors.
I'm currently thinking about a subscribe approach which is triggered when the modal is exited, however I think there must be another way I haven't thought of.
Can you try using a Boolean as IsSelected & make IsSelected true when user clicks an item from list & change your condition in setter as below. When your operations are done reset the flag.
This was, there wont not be any duplication of modals. This is what I understood from your question, if there's something else, please let me know.
if( _locationAssetSelected || !IsSelected )
{
IsSelected = true;
_locationAssetSelected = value;
..... //your code
_locationAssetSelected = null;
IsSelected = false;
}
You could move the logic to the command to make sure it doesn't execute multiple times. This is a snippet on the sample/template Forms app which uses this method as a command. Lock seems unnecessary.
async void OnItemSelected(Item item)
{
//lock (selectLock)
//{
if (item == null || selectionOn)
return;
selectionOn = true;
//}
System.Diagnostics.Debug.WriteLine($"{item.Text} selected");
// This will push the ItemDetailPage onto the navigation stack
await Shell.Current.GoToAsync($"{nameof(ItemDetailPage)}?{nameof(ItemDetailViewModel.ItemId)}={item.Id}");
selectionOn = false;
}

I am trying to run a task in my async method but it's giving me a ui thread error

Here's what I am trying to do:
public async void CreateListSectionAsync()
{
details.Children.Clear();
details.Children.Add(new MessageGridTemplate()
{
Label = "Information",
Text = "Fetching card details, please be patient"
});
await Task.Run(() =>
{
// modify the UI
details.Children.Clear();
});
}
But this gives a UI error. Is the way I am calling the task not correct?
ListViews or other views that highly modify the UI hate being on a separate thread. So when you call
await Task.Run(() =>
{
// modify the UI
details.Children.Clear();
});
the UI will exception because you are modifying the screen from a non-UI thread. Try calling the details.Children.Clear in a Device.BeginInvokeOnMainThread call.
Even just adding or removing a single element won't be happy on a separate thread/context.
You cannot modify UI elements from a background thread.
The appropriate mitigation is not Device.BeginInvokeOnMainThread. A more correct approach depends on what kind of UI updates you're doing:
To display the results of a background thread operation, use await.
To display progress or multiple results of a background thread operation, use Progress<T> / IProgress<T>.
To display updates as events are pushed to your app, use SynchronizationContext or IObservable<T>.ObserveOn.

Delay shared element transition to complete statelist animation

I've been trying out shared element transition on Lollipop. i have a recyclerview which loads some cards and one click the card expands to its details in the next activity.
I have set a ripple effect and a StateListAnimator on the card. But those are not visible cause the transition starts before these effects are completed.
Is there any way to delay the transition so that it can wait for the statelist animator and ripple to complete?
Here is the code I use
ActivityOptions options = null;
if (Utilities.isLollipop()) {
options = ActivityOptions.makeSceneTransitionAnimation(this, Pair.create(view, "hero_view"), Pair.create((View) fab, "fab"));
startActivity(detailIntent, options.toBundle());
}
Thanks in advance
I ended up using a work around, but I still would want to know what is the right way of doing this and therefore leaving the question open.
The work around I did was
1. remove the state list animator and add that as an animator in the onclick method.
2. Used a Handler to post a delayed call to the activity
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
Intent i=new Intent(SearxhJobs.this,JobsTypes.class);
startActivity(i);
}
}, 200);
P.S- I did end up removing the effect as it was not very intuitive.
The way that may help some that already have the Transitions setup in Second Activity:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
getWindow().getSharedElementEnterTransition().setDuration(500);
getWindow().getSharedElementReturnTransition().setDuration(500)
.setInterpolator(new DecelerateInterpolator());
}

EaselJS : Help understanding animationend event

In trying to figure out how to start and stop Sprite animations from a SpriteSheet, I tried this:
// other code...
// define animations in SpriteSheet:
"animations": {"intro": [0, 19, "false"]}
// other code...
var spriteSheet = new createjs.SpriteSheet(data);
var intro = new createjs.Sprite(spriteSheet, "intro");
stage.addChild(intro);
createjs.Ticker.addEventListener("tick", stage);
intro.stop();
var btnStart = document.getElementById("btnStart");
btnStart.onclick = function() {
console.log("btnStart clicked");
intro.on("animationend", onStartAnimEnd);
intro.play();
};
function onStartAnimEnd(e) {
intro.removeEventListener("animationend", onStartAnimEnd);
console.log("Start anim ended");
}
In the above example, if the user clicks the btnStart button, the onStartAnimEnd callback fires indefinitely, even though by defining "false" in the animation configuration to signal we want to stop on the last frame, and the animation does in fact stop, and I'm removing the event listener in the first line of the callback.
If I add:
function onStartAnimEnd(e) {
intro.removeEventListener("animationend", onStartAnimEnd);
intro.stop();
console.log("Start anim ended");
}
The problem goes away, but that doesn't seem right... So, if I change the listener assignment of the animationend event from:
intro.on("animationend", onStartAnimEnd);
to:
ask.addEventListener("animationend", onAskAnimEnd);
...and with this change, the indefinite event captures goes away. So my questions are:
What's the difference with these two event listener assignments?
Is this animationend event continually firing in the background because we're updating the stage on the tick event, even though nothing needs re-rendering?
Thanks for your time!
This is actually a duplicated question. As I answered you previous question, your animation definition is wrong, you need to use the boolean value (false) and not the string value ("false").
Now sure what ask is, but the method on is a wrapper for addEventListener, and where you can specify things such as the scope of the callback and if it will run only once. Take a look at the API to know more:
http://www.createjs.com/Docs/EaselJS/classes/EventDispatcher.html#method_on

Filtering events in timeline stops drawing a new event in DHTMLX

dhtmlxscheduler timeline when I use filtering
scheduler.filter_timeline = scheduler.filter_month = scheduler.filter_day = scheduler.filter_week = function(id, event) {
// display event only if its type is set to true in filters obj
if (rules[event.user_id]) {
return true;
}
// default, do not display event
return false;
};
drag animation (drawing a Node/session) doesn't work.
if you look at the DHTMLX_scheduler samples you will see create a new event doesn't work properly.
/samples/09_api/09_filtering_events.html
I am using Trace Skin . Every thing is working well. even light box is loading . the main problem is when I use this statement filter_timeline then Timeline drawing stop draw event.(It can also create it but the it is like transparent)
It is not a bug in scheduler itself, but badly written sample
In code of sample, update next line
CODE: SELECT ALL
if (filters[event.type]) {
as
CODE: SELECT ALL
if (filters[event.type] || event.type==scheduler.undefined) {
When event just created it doesn't have the type defined yet, so it was filtered out with previous logic

Resources