Search code examples
.netmaui

.net MAUI dynamic picker not showing data


I trying to show data in dynamically created pickers. This is my code.This data is loading in viewmodel but its not visible in my picker. When user enter No Of Nights as 2 then 2 pickers are generating. I can't found any issues here data also loading, Only issue is not visible. Anyone have idea to fix this. Please help.

NewPostPage.xaml

<?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"
             xmlns:ur="http://schemas.enisn-projects.io/dotnet/maui/uraniumui/material"
             x:Class="DriveMeSL.View.NewPostPage"
             xmlns:vm="clr-namespace:DriveMeSL.ViewModels"
             NavigationPage.HasNavigationBar="True"
             Title="New Post">
    <ContentPage.BindingContext>
        <vm:NewPostViewModel />
    </ContentPage.BindingContext>
    <ScrollView>
        <VerticalStackLayout VerticalOptions="Center"
                             Margin="20,0,20,0"
                             Spacing="20">
            <Label Text="Create Post"
                   TextColor="#101010"
                   FontSize="Large"
                   HorizontalOptions="Center"/>   
            <ur:DatePickerField x:Name="EntDate"
                                Title="Date" />
            <ur:TimePickerField x:Name="EntTime"
                                Title="Pick a Time" />
            <StackLayout Padding="20">
                <Label Text="Enter Number of Nights:" />
                <Entry Text="{Binding NoOfNights, Mode=TwoWay}" Keyboard="Numeric" /> 
                <CollectionView ItemsSource="{Binding NightLocations}">
                    <CollectionView.ItemTemplate>
                        <DataTemplate>
                            <ur:PickerField Title="Enter location"
                                            ItemsSource="{Binding Locations}"
                                            ItemDisplayBinding="{Binding LocationName}"
                                            SelectedItem="{Binding SelectedLocation}" />
                        </DataTemplate>
                    </CollectionView.ItemTemplate>
                </CollectionView>
            </StackLayout>
            <Button Text="Save" Command="{Binding SaveCommand}" />
            <Button x:Name="BtnNewPost" StyleClass="FilledButton" Text="Add Post" Clicked="BtnNewPost_Clicked" />
        </VerticalStackLayout>
    </ScrollView>
</ContentPage>

NewPostPage.xaml.cs

    public partial class NewPostPage : ContentPage
    {
        private NewPostViewModel viewModel;
    
        public NewPostPage()
        {
            InitializeComponent();
    
            viewModel = new NewPostViewModel();
            BindingContext = viewModel;
    
            // Call the async method to load locations
            _ = LoadLocationsAsync();
        }
    
        private async Task LoadLocationsAsync()
        {
            await viewModel.LoadLocationsAsync();
        }
    } 
}

NewPostViewModel.cs

public class NewPostViewModel : INotifyPropertyChanged
{

    private ObservableCollection<Location> _locations;
    public ObservableCollection<Location> Locations
    {
        get => _locations;
        set
        {
            _locations = value;
            OnPropertyChanged(nameof(Locations));
        }
    }

    private Location _selectedLocation;
    public Location SelectedLocation
    {
        get { return _selectedLocation; }
        set
        {
            _selectedLocation = value;
            OnPropertyChanged(nameof(SelectedLocation));
        }

    }

   public async Task LoadLocationsAsync()
   {
       try
       {
           var locations = await ApiService.GetLocation(); // Replace with your actual API call
           Locations = new ObservableCollection<Location>(locations);
           OnPropertyChanged(nameof(Locations)); // Notify UI of change
       }
       catch (Exception ex)
       {
           // Handle exceptions or logging
           Console.WriteLine($"Error loading locations: {ex.Message}");
       }
   }

  protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
  {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  }
}

Solution

  • Binding to a property in your ViewModel from within an item in a collection, requires relative binding to work.

    The CollectionView needs a name attribute. You can then use binding for the items by referencing the binding context of the collectionview.

    <CollectionView ItemsSource="{Binding NightLocations}"
                    x:Name="NightLocationsCollectionView">
        <CollectionView.ItemTemplate>
            <DataTemplate>
                <ur:PickerField Title="Enter location"
                                ItemsSource="{Binding Source={x:Reference NightLocationsCollectionView}, Path=BindingContext.Locations}"
                                SelectedItem="{Binding SelectedLocation}"
                                ItemDisplayBinding="{Binding LocationName}"/>
            </DataTemplate>
        </CollectionView.ItemTemplate>
    </CollectionView>