英文:
MAUI : await Shell.Current.GoToAsync take too much time
问题
I am developing a project using MAUI and I have some issues with the shell navigation.
There is the line in my code:
await Shell.Current.GoToAsync(nameof(DetailMenuPage));
This execution time code is very long. I have some business code before it takes no time compared to the navigation. Also, I have other business which is executed during the navigation (in a command for the Appearing event) and takes no time too.
There is the full code before and during the navigation:
public async Task SelectItem(object selectedCategorie)
{
if (selectedCategorie is CategorieMenu)
{
var cSelectedCategorie = selectedCategorie as CategorieMenu;
var sousCategorie = await _serviceToSousCategorieMenu.GetByCategorieMenuId(new List
StaticContext.CategorieMenuName = cSelectedCategorie.Name;
StaticContext.SousCategorieMenu = sousCategorie;
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
await Shell.Current.GoToAsync(nameof(DetailMenuPage));
watch.Stop();
Console.WriteLine($"Execution Time: {watch.ElapsedMilliseconds} ms");
}
}
Here the watch.ElapsedMilliseconds
takes 4692 ms.
[RelayCommand]
public async Task Load()
{
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
var allItem = await _serviceMenuItem.GetBySousCategorieMenuId(StaticContext.SousCategorieMenu.Select(sc => sc.SousCategorieMenuId).ToList());
if (allItem != null && allItem.Count > 0)
{
var groupedItem = allItem.GroupBy(u => u.SousCategorieMenuId).Select(grp => grp.ToList()).ToList();
Title = StaticContext.CategorieMenuName;
groupedItem.ForEach(c =>
{
Items.Add(new CategorieGroup
(
StaticContext.SousCategorieMenu.FirstOrDefault(sc => sc.SousCategorieMenuId == c.FirstOrDefault().SousCategorieMenuId).Name,
c
));
});
}
watch.Stop();
Console.WriteLine($"Execution Time: {watch.ElapsedMilliseconds} ms");
}
Here the watch.ElapsedMilliseconds
takes 168 ms.
Did I miss something or did you have any tips?
EDIT:
There the constructor of DetailMenuPage
:
public DetailMenuPage(DetailMenuPageViewModel vm)
{
InitializeComponent();
BindingContext = vm;
}
The content of DetailMenuPage
:
<ContentPage.Behaviors>
<toolkit:EventToCommandBehavior EventName="Appearing" Command="{Binding LoadCommand}" />
</ContentPage.Behaviors>
<ContentPage.Resources>
<Converter:DoubleToStringConverter x:Key="doubleToStringConverter" />
</ContentPage.Resources>
<CollectionView.GroupHeaderTemplate>
</CollectionView.GroupHeaderTemplate>
<CollectionView.ItemTemplate>
</CollectionView.ItemTemplate>
And the constructor of the view model:
public DetailMenuPageViewModel(Services.SQLite.Interfaces.IServiceMenuItem serviceMenuItem)
{
_serviceMenuItem = serviceMenuItem;
}
英文:
I am developing a project using MAUI and I have some issues with the shell navigation.
There is the line in my code :
await Shell.Current.GoToAsync(nameof(DetailMenuPage));
This execution time code is very long . I have some business code before it takes no time compared to the navigation. Also, I have other business which is executed during the navigation (in a command for the Appearing event) and takes no time too.
There is the full code before and during the navigation :
public async Task SelectItem(object selectedCategorie)
{
if (selectedCategorie is CategorieMenu)
{
var cSelectedCategorie = selectedCategorie as CategorieMenu;
var sousCategorie = await _serviceToSousCategorieMenu.GetByCategorieMenuId(new List<int> { cSelectedCategorie.CategorieMenuId });
StaticContext.CategorieMenuName = cSelectedCategorie.Name;
StaticContext.SousCategorieMenu = sousCategorie;
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
await Shell.Current.GoToAsync(nameof(DetailMenuPage));
watch.Stop();
Console.WriteLine($"Execution Time: {watch.ElapsedMilliseconds} ms");
}
}
Here the watch.ElapsedMilliseconds
takes 4692 ms.
[RelayCommand]
public async Task Load()
{
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
var allItem = await _serviceMenuItem.GetBySousCategorieMenuId(StaticContext.SousCategorieMenu.Select(sc => sc.SousCategorieMenuId).ToList());
if (allItem != null && allItem.Count > 0)
{
var groupedItem = allItem.GroupBy(u => u.SousCategorieMenuId).Select(grp => grp.ToList()).ToList();
Title = StaticContext.CategorieMenuName;
groupedItem.ForEach(c =>
{
Items.Add(new CategorieGroup
(
StaticContext.SousCategorieMenu.FirstOrDefault(sc => sc.SousCategorieMenuId == c.FirstOrDefault().SousCategorieMenuId).Name,
c
));
});
}
watch.Stop();
Console.WriteLine($"Execution Time: {watch.ElapsedMilliseconds} ms");
}
Here the watch.ElapsedMilliseconds
takes 168 ms.
Did I miss something or did you have any tips ?
EDIT :
There the constructor of DetailMenuPage
:
public DetailMenuPage(DetailMenuPageViewModel vm)
{
InitializeComponent();
BindingContext = vm;
}
The content of DetailMenuPage
:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Byron.Page.DetailMenuPage"
xmlns:ViewModels="clr-namespace:Byron.ViewModel"
xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
x:DataType="ViewModels:DetailMenuPageViewModel"
xmlns:Models ="clr-namespace:Byron.Model.DTO"
xmlns:Converter="clr-namespace:Byron.Helper.Converter"
xmlns:IHM ="clr-namespace:Byron.Model.IHM"
Routing.Route="DetailPage"
Title="{Binding Title}">
<ContentPage.Behaviors>
<toolkit:EventToCommandBehavior EventName="Appearing" Command="{Binding LoadCommand}" />
</ContentPage.Behaviors>
<ContentPage.Resources>
<Converter:DoubleToStringConverter x:Key="doubleToStringConverter" />
</ContentPage.Resources>
<ScrollView>
<StackLayout>
<CollectionView ItemsSource="{Binding Items}" IsGrouped="True" >
<CollectionView.GroupHeaderTemplate>
<DataTemplate x:DataType="IHM:CategorieGroup">
<StackLayout HorizontalOptions="FillAndExpand">
<Label Text="{Binding GroupTitle}" Style="{StaticResource TitleGroupMenu}"/>
</StackLayout>
</DataTemplate>
</CollectionView.GroupHeaderTemplate>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="Models:MenuItem">
<Frame CornerRadius="10" Margin="10" BorderColor="{AppThemeBinding Light=Black, Dark=White}" BackgroundColor="Transparent">
<StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="CenterAndExpand">
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}" Style="{StaticResource ItemMenu}" />
<Label Text="{Binding Prix, Converter={StaticResource doubleToStringConverter}}" HorizontalOptions="EndAndExpand" Style="{StaticResource ItemMenu}"/>
</StackLayout>
<Rectangle HeightRequest="1" BackgroundColor="Black" Margin="0,0,100,0" />
<Label Text="{Binding Description}" VerticalOptions="Center" TextColor="{AppThemeBinding Light=Black, Dark=White}" />
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ScrollView>
</ContentPage>
And the constructor of the view model :
public DetailMenuPageViewModel(Services.SQLite.Interfaces.IServiceMenuItem serviceMenuItem)
{
_serviceMenuItem = serviceMenuItem;
}
答案1
得分: 3
<ScrollView>
<StackLayout>
<CollectionView ItemsSource="{Binding Items}" IsGrouped="True">
非垂直受限制的视觉元素不应该放在其他非垂直受限制的容器内。
有趣的是,当人们抱怨性能时,我通常看到上述两者的组合。您的代码远远超出了这个范围。
限制您的VisualElements。无论是通过请求高度还是其他方式。
我认为我可以安全地推荐编辑:
<Grid>
<CollectionView...
(完全不知道您试图做什么)
英文:
<ScrollView>
<StackLayout>
<CollectionView ItemsSource="{Binding Items}" IsGrouped="True" >
Non-vertically restricted visual elements should not be inside other non-vertically restricted containers.
The interesting part is that I usually see combination of two of those above, when people complain about performance. Your code goes way beyond that.
Limit your VisualElements. Be it with requesting height or otherwise.
I think I can safely recommend the edit:
<Grid>
<CollectionView...
(without having any idea what you are trying to do)
答案2
得分: 0
-
除了 H.A.H 的回答之外,请考虑以下更改:
1.1 你需要使用
ScrollView
吗?CollectionView
已经知道如何滚动。如果你移除ScrollView
,那么也应该移除 H.A.H 的回答中的StackLayout
(或者Grid
):<CollectionView
-
无论何时使用
StackLayout
,都要更改为VerticalStackLayout
或HorizontalStackLayout
,取决于方向。 -
使用
Border
代替Frame
。Frame
保留了与 Xamarin 的向后兼容性,但使用了旧的渲染方法。Frame
的文档建议使用Border
。 -
你的
Load
方法在Appearing
事件期间运行。在填充集合时,不要在那个时候使用Items.Add
(我认为),因为每次添加都会触发布局逻辑。相反,建立一个本地变量的集合,然后将其分配给Items
:
var items = new ...与 Items 相同的类型...();
...
items.Add(...); // 添加到本地的 `items` 而不是 `Items`。
...
// 当所有项目都添加完毕时。
Items = items;
- 考虑在
<CollectionView.ItemTemplate>
内设置HeightRequest
。这适用于外部布局。除非你有Frame
或Border
,否则将其放在框架/边框的内容上:<VerticalStackLayout HeightRequest="99" ...>
。用适当的高度替换"99"
。固定高度的项目布局要快得多。
英文:
In addition to H.A.H's answer, consider these changes:
- Do you need
ScrollView
?CollectionView
knows how to scroll. If you eliminateScrollView
, then also remove theStackLayout
(orGrid
from H.A.H.'s answer).:
<ScrollView>
<StackLayout>
<CollectionView
becomes:
<CollectionView
-
Whereever you use
StackLayout
, change toVerticalStackLayout
orHorizontalStackLayout
, depending on direction. -
Use
Border
instead ofFrame
. Frame was kept for backwards compatibility with Xamarin, but uses an old rendering approach. The Frame doc recommends using Border instead. -
Your
Load
method runs duringAppearing
event. Don't useItems.Add
at that time, when filling the collection. (I think) this triggers layout logic on each Add. Instead, build the collection in a local variable, then assign it toItems
:
var items = new ...same-type-as-Items...();
...
items.Add(...); // Add to local `items` instead of `Items`.
...
// When all items have been added.
Items = items;
- Consider setting
HeightRequest
inside<CollectionView.ItemTemplate>
. This goes on the outer layout. Except when you haveFrame
orBorder
put it on the content of the frame/border:<VerticalStackLayout HeightRequest="99" ...>
. Replace "99" with whatever height looks good. Fixed-height items are MUCH faster to lay out.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论