UI freezes for sometime after UI binding

Shashank Gaddam [C] 20 Reputation points
2025-06-15T17:09:15.28+00:00

My data is binding to UI quickly but the UI freezes for few seconds and I have only 2 records.

I am loading data in ContentView Loaded event

var newStops = await Task.Run(() =>
{
    return response.Stops
        .OrderByDescending(x => x.stopNumber)
        .Select(item => new StopListUIModel
        {
            StopId = item.stopId,
            Name = item.consigneeName,
            Email = item.consigneeEmail,
            ClientCode = item.clientCode,
            ServiceType = item.serviceType,
            StopNumber = item.stopNumber,
            TimeWindow = $"{FormatTime(item.windowStartTime)} - {FormatTime(item.windowEndTime)}",
            ActualTime = $"{FormatTime(item.actualStartTime)} - {FormatTime(item.actualEndTime)}",
            RouteStatus = item.status,
            IsSynced = false,
            ImageUrls = item.pictures.Select(e => e.url).ToList(),
            HasImages = item.pictures.Any(),
            Location = string.Join(", ", new[]
            {
        item.consigneeAddressLine1,
        item.consigneeCity,
        item.consigneeState,
        item.consigneeCountry,
        item.consigneeZip
            }.Where(s => !string.IsNullOrEmpty(s))),
            RouteStatusBackgroundColor = GetColor(item.status)
        })
        .ToList();
});
// ✅ Add all items at once on the UI thread
MainThread.BeginInvokeOnMainThread(() =>
{
    Model.StopsList = new ObservableCollection<StopListUIModel>(newStops);
    Model.IsSortVisible = newStops.Count > 0;
});
 <CollectionView
     x:Name="stopsList"
     Grid.Row="1"
     Margin="8,0,8,0"
     ItemsSource="{Binding Model.StopsList}">
     <CollectionView.ItemsLayout>
         <LinearItemsLayout ItemSpacing="8" Orientation="Vertical" />
     </CollectionView.ItemsLayout>
     <CollectionView.EmptyView>
         <Border
             BackgroundColor="White"
             IsVisible="{Binding Source={x:Static App.DynamicTabbedPageViewModel}, Path=IsDyamicPageBusy, Converter={StaticResource InvertedBoolConverter}}"
             StrokeShape="RoundRectangle 8"
             StrokeThickness="0"
             VerticalOptions="FillAndExpand">
             <Label
                 Margin="20"
                 FontFamily="RobotoRegular"
                 HorizontalTextAlignment="Center"
                 Text="{x:Static res:ResourcesFile.StopListInformationMsg}"
                 TextColor="Black"
                 VerticalOptions="CenterAndExpand" />
         </Border>
     </CollectionView.EmptyView>
     <CollectionView.ItemTemplate>
         <DataTemplate>
             <Border
                 x:Name="mainBorder"
                 Grid.Row="1"
                 Margin="{OnPlatform iOS='9,5,9,5',
                                     Android='0,0,0,0'}"
                 BackgroundColor="White"
                 StrokeShape="RoundRectangle 8"
                 StrokeThickness="0"
                 VerticalOptions="StartAndExpand">
                 <StackLayout>
                     <Grid
                         Margin="12"
                         ColumnDefinitions="0.15*,0.75*,0.1*"
                         ColumnSpacing="10"
                         HorizontalOptions="FillAndExpand"
                         RowDefinitions="*,*">
                         <Border
                             Grid.Row="0"
                             Grid.RowSpan="2"
                             Grid.Column="0"
                             Margin="0,0,0,8"
                             BackgroundColor="Black"
                             HeightRequest="50"
                             StrokeShape="RoundRectangle 30"
                             StrokeThickness="0"
                             WidthRequest="50">
                             <Label
                                 FontSize="Medium"
                                 HorizontalOptions="Center"
                                 Text="{Binding StopNumber}"
                                 TextColor="White"
                                 VerticalOptions="Center" />
                         </Border>
                         <Grid
                             Grid.Row="0"
                             Grid.RowSpan="2"
                             Grid.Column="1"
                             ColumnDefinitions="Auto,*"
                             ColumnSpacing="7"
                             RowDefinitions="*, *"
                             VerticalOptions="FillAndExpand">
                             <Image
                                 Grid.Column="0"
                                 Margin="0,3,0,0"
                                 Source="driverusericon.png"
                                 VerticalOptions="StartAndExpand" />
                             <Label
                                 Grid.Column="1"
                                 FontFamily="RobotoRegular"
                                 FontSize="14"
                                 Text="{Binding Name}"
                                 TextColor="Black"
                                 VerticalOptions="StartAndExpand"
                                 VerticalTextAlignment="Center" />
                             <Image
                                 Grid.Row="1"
                                 Grid.Column="0"
                                 Margin="0,0,0,0"
                                 Source="stoplisticon.png"
                                 VerticalOptions="StartAndExpand" />
                             <Label
                                 Grid.Row="1"
                                 Grid.Column="1"
                                 FontFamily="RobotoRegular"
                                 FontSize="14"
                                 Text="{Binding Location}"
                                 TextColor="Black"
                                 VerticalOptions="StartAndExpand"
                                 VerticalTextAlignment="Center" />
                         </Grid>
                         <Image
                             Grid.Column="3"
                             Margin="0,0,5,0"
                             HeightRequest="25"
                             HorizontalOptions="End"
                             IsVisible="False"
                             Source="synced.png">
                             <Image.Triggers>
                                 <DataTrigger
                                     Binding="{Binding IsSynced}"
                                     TargetType="Image"
                                     Value="False">
                                     <Setter Property="Source" Value="pending.png" />
                                 </DataTrigger>
                             </Image.Triggers>
                         </Image>
                     </Grid>
                     <Grid
                         Margin="8,2,0,12"
                         ColumnDefinitions="0.6*,0.4*"
                         RowDefinitions="*,*"
                         RowSpacing="10">
                         <StackLayout HorizontalOptions="StartAndExpand" Spacing="5">
                             <Label FontSize="12">
                                 <Label.FormattedText>
                                     <FormattedString>
                                         <FormattedString.Spans>
                                             <Span
                                                 FontFamily="RobotoMedium"
                                                 Text="{x:Static res:Resources.TimeWindowText}"
                                                 TextColor="#667085" />
                                             <Span
                                                 FontFamily="RobotoMedium"
                                                 Text="  "
                                                 TextColor="#667085" />
                                             <Span
                                                 FontFamily="RobotoMedium"
                                                 Text="{Binding TimeWindow}"
                                                 TextColor="#000000" />
                                         </FormattedString.Spans>
                                     </FormattedString>
                                 </Label.FormattedText>
                             </Label>
                             <Label
                                 Margin="0,0,8,0"
                                 FontSize="12"
                                 HorizontalOptions="StartAndExpand">
                                 <Label.FormattedText>
                                     <FormattedString>
                                         <FormattedString.Spans>
                                             <Span
                                                 FontFamily="RobotoMedium"
                                                 Text="{x:Static res:Resources.ActualTimeText}"
                                                 TextColor="#667085" />
                                             <Span
                                                 FontFamily="RobotoMedium"
                                                 Text="  "
                                                 TextColor="#667085" />
                                             <Span
                                                 FontFamily="RobotoMedium"
                                                 Text="{Binding ActualTime}"
                                                 TextColor="#000000" />
                                         </FormattedString.Spans>
                                     </FormattedString>
                                 </Label.FormattedText>
                             </Label>
                         </StackLayout>
                         <StackLayout
                             Grid.Column="1"
                             HorizontalOptions="FillAndExpand"
                             Spacing="5">
                             <Border
                                 HorizontalOptions="EndAndExpand"
                                 IsVisible="{Binding HasImages}"
                                 Stroke="#FFC1CA"
                                 StrokeThickness="1.5">
                                 <StackLayout
                                     Margin="12,2,12,2"
                                     Orientation="Horizontal"
                                     Spacing="8">
                                     <Image HeightRequest="15" Source="imageicon.png" />
                                     <Label
                                         FontFamily="RobotoMedium"
                                         FontSize="13"
                                         Text="{x:Static res:Resources.ViewText}"
                                         TextColor="#E41937" />
                                 </StackLayout>
                                 <Border.GestureRecognizers>
                                     <TapGestureRecognizer
                                         BindingContext="{Binding Source={x:Reference stopsList}, Path=BindingContext}"
                                         Command="{Binding ViewCommand}"
                                         CommandParameter="{Binding Source={x:Reference mainBorder}, Path=BindingContext}" />
                                 </Border.GestureRecognizers>
                             </Border>
                             <Border
                                 BackgroundColor="{Binding RouteStatusBackgroundColor}"
                                 HorizontalOptions="End"
                                 StrokeShape="RoundRectangle 15,0,15,0"
                                 StrokeThickness="0">
                                 <Label
                                     Margin="18,2,9,3"
                                     FontFamily="RobotoMedium"
                                     HorizontalOptions="Start"
                                     Text="{Binding RouteStatus}"
                                     TextColor="White"
                                     VerticalOptions="Center" />
                             </Border>
                         </StackLayout>
                     </Grid>
                 </StackLayout>
                 <Border.GestureRecognizers>
                     <TapGestureRecognizer Command="{Binding Source={x:Reference stopsList}, Path=BindingContext.StopClickedCommand}" CommandParameter="{Binding .}" />
                 </Border.GestureRecognizers>
             </Border>
         </DataTemplate>
     </CollectionView.ItemTemplate>
 </CollectionView>
Developer technologies | .NET | .NET MAUI
0 comments No comments
{count} votes

3 answers

Sort by: Most helpful
  1. Doaa Ali Hamdan AL-Jarwani 340 Reputation points
    2025-06-15T19:48:53.3566667+00:00

    Your UI is freezing because you’re assigning a large ObservableCollection all at once, even with just 2 records — the CollectionView layout and bindings can be layout-heavy and cause a visible delay.

    Fix in 2 Steps (Very Efficient):

    1. Clear and Add Items One by One Instead of Replacing the Whole Collection:

    MainThread.BeginInvokeOnMainThread(() =>

    {

        Model.StopsList.Clear();

        foreach (var item in newStops)

            Model.StopsList.Add(item);

        Model.IsSortVisible = Model.StopsList.Count > 0;

    });

    This reduces layout recalculation and avoids freezing the UI.

    2. Delay UI Update Slightly (Optional Improvement):

    If still laggy, add a slight delay before UI binding:

    await Task.Delay(100); // Small delay before touching UI

    MainThread.BeginInvokeOnMainThread(() => {

        // add items...

    });

    Why This Works:

    • Creating a new ObservableCollection triggers a full re-render of the CollectionView.

    • Using .Clear() and .Add() avoids XAML overhead caused by re-applying the entire DataTemplate.


  2. Shashank Gaddam [C] 20 Reputation points
    2025-06-16T05:35:47.4466667+00:00

    I found the issue, there is some other UI thread is running in the background

    0 comments No comments

  3. Michael Le (WICLOUD CORPORATION) 995 Reputation points Microsoft External Staff
    2025-08-08T04:21:52.16+00:00

    Hello,

    Glad that the problem has been solved.

    The root cause, as you discovered, was a separate background process that was blocking the main UI thread, making it unavailable to process the update from your MainThread.BeginInvokeOnMainThread call. The UI freezing was a symptom of this external blocking operation, not a flaw in your provided code snippet—which actually demonstrates excellent async/await practices.

    Your code snippet serves as perfect reference for others facing similar issues. I would like to provide additional information about this problem for future developers who might encounter it:

    Recommendation

    Never perform blocking, long-running operations on the main UI thread.

    1. Async/Await for I/O Operations Use async and await for any operation involving waiting (network requests, database access, file I/O). The await keyword keeps the UI thread free and responsive while waiting for tasks to complete.

    // Correct approach
    var data = await httpClient.GetFromJsonAsync<List<Item>>(url);
    

    2. Offload Heavy Computation with Task.Run()

    For computationally intensive tasks without natural async equivalents (complex calculations, sorting large collections, image processing), use Task.Run() to explicitly move work to background threads.

    // Your excellent example demonstrates this perfectly
    var processedData = await Task.Run(() =>
    {
        return response.Stops
            .OrderByDescending(x => x.stopNumber)
            .Select(item => new StopListUIModel { ... })
            .ToList();
    });
    

    3. UI Thread Marshaling for Updates

    UI can only be safely modified from the main UI thread. After background tasks complete, you must switch back to the main thread to update controls. Keep the code inside this block minimal and fast—avoid heavy logic or calculations within this section.

    // Proper UI thread marshaling (as you demonstrated)
    MainThread.BeginInvokeOnMainThread(() =>
    {
        Model.StopsList = new ObservableCollection<StopListUIModel>(processedData);
        Model.IsSortVisible = processedData.Count > 0;
    });
    

    Additional Debugging Tips

    When your async code looks correct but UI still freezes, check for:

    • Timers doing synchronous work
    • Property setters with heavy operations
    • Third-party libraries blocking the UI thread
    • Animation callbacks performing expensive tasks

    I hope this helps anyone facing similar issues.

    References

    You could also refer to these resources for further reading:


Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.