Hay Abados
Hay Abados

Reputation: 53

Binding Issue in MAUI CollectionView: Property "FileName" Not Found in DataContext

I am working on a .NET MAUI application and am having trouble binding a property in a CollectionView. Specifically, I am trying to display a list of uploaded files and their filenames, but I am encountering an issue where the FileName property is not recognized in the CollectionView.

Here’s an overview of my setup and the problem I’m facing:

ViewModel: I have a UserProfileMauiViewModel that includes an ObservableCollection:

using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using webapplib.Client.Services;
using webapp.worktime.Client.ClientApi;
using webapp.worktime.Client.Services;
using webapp.worktime.Client.ViewModel;
using webapplib.Shared.Data;
using webapplib.Shared.Model;
using Deploy.WorkTime.MAUI.Services;



namespace Deploy.WorkTime.MAUI.ViewModel;
public partial class UserProfileMauiViewModel : UserProfileViewModel {

    private readonly IFileUploadService _fileUploadService;
    public ObservableCollection<FileData> UploadedFiles { get; }
    public UserProfileMauiViewModel(
        AppDataService appData,
        IExtraDataService extraData,
        IPageNavigator pageNavigator,
        WorkersClientApi workersClientApi,
        IStorageService storageService,
        SalaryService salaryService,
         IFileUploadService fileUploadService)
        : base(appData, extraData, pageNavigator, workersClientApi, storageService, salaryService) {
        _fileUploadService = fileUploadService;
        UploadedFiles = new ObservableCollection<FileData>();
        UploadFileCommand = new AsyncRelayCommand(UploadFileAsync);
        RemoveFileCommand = new RelayCommand<FileData?>(RemoveFile);

    }
    public IAsyncRelayCommand UploadFileCommand { get; }
    public IRelayCommand<FileData> RemoveFileCommand { get; }

    private async Task UploadFileAsync()
    {
        var (files, emptyFiles, bigFiles) = await _fileUploadService.UploadFilesAsync();
        foreach (var file in files)
        {
            UploadedFiles.Add(file);
            
        }
    }
    private void RemoveFile(FileData? file)
    {
        if (file != null)
        {
            UploadedFiles.Remove(file);
        }
    }

XAML Page: In my XAML page, I am trying to bind to the UploadedFiles collection:

<pages:WorkTimePage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                    xmlns:toolkit="http://schemas.microsoft.com/dotnet/2022/maui/toolkit"
                    xmlns:material="http://schemas.enisn-projects.io/dotnet/maui/uraniumui/material"
                    xmlns:uranium="http://schemas.enisn-projects.io/dotnet/maui/uraniumui"
                    xmlns:cv="clr-namespace:Camera.MAUI;assembly=Camera.MAUI"
                    xmlns:pages="clr-namespace:Deploy.WorkTime.MAUI.Pages"
                    xmlns:local="clr-namespace:Deploy.WorkTime.MAUI.Views"
                    xmlns:viewmodel="clr-namespace:Deploy.WorkTime.MAUI.ViewModel"
                    xmlns:converters="clr-namespace:webapplib.MAUI.Converters;assembly=webapplib.MAUI"
                    xmlns:lang="clr-namespace:webapplib.Client.Resources;assembly=webapplib.Client"
                    x:Class="Deploy.WorkTime.MAUI.Pages.UserProfilePage"
                    x:TypeArguments="viewmodel:UserProfileMauiViewModel"
                    x:DataType="viewmodel:UserProfileMauiViewModel"
                    Title="{Binding Title}">

    <ContentPage.Resources>
        <ResourceDictionary>
            <!-- Define converters and styles here -->
            <converters:ColorConverter x:Key="ColorConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>
     <VerticalStackLayout Padding="10" Spacing="10">
     <Grid Margin="20" Padding="10" BackgroundColor="{StaticResource SomeBackgroundColor}" ColumnSpacing="10" RowSpacing="10">
         <Grid.RowDefinitions>
             <RowDefinition Height="Auto" />
             <RowDefinition Height="Auto" />
             <RowDefinition Height="Auto" />
         </Grid.RowDefinitions>
         <Grid.ColumnDefinitions>
             <ColumnDefinition Width="*" />
         </Grid.ColumnDefinitions>

         <Button Text="Upload Identification File"
         Command="{Binding UploadFileCommand}" Grid.Row="0" Grid.Column="1" />

         <Label Text="Uploaded Files:" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"/>
         <CollectionView ItemsSource="{Binding UploadedFiles}" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2">
             <CollectionView.ItemTemplate>
                 <DataTemplate>
                     <Grid Padding="5" ColumnSpacing="10">
                         <Grid.ColumnDefinitions>
                             <ColumnDefinition Width="*" />
                             <ColumnDefinition Width="Auto" />
                         </Grid.ColumnDefinitions>

                         <!-- Binding to FileData properties -->
                         <Label Text="{**Binding FileName**}" Grid.Column="0" VerticalOptions="Center"/>
                         <Button Text="Remove" 
         Command="{Binding Source={RelativeSource AncestorType={x:Type viewmodel:UserProfileMauiViewModel}}, Path=RemoveFileCommand}" 
         CommandParameter="{Binding .}" 
         Grid.Column="1" />
                     </Grid>
                 </DataTemplate>
             </CollectionView.ItemTemplate>
         </CollectionView>
     </Grid>
 </VerticalStackLayout>

Issue The FileName property is not found in the data context. I have verified that FileData has a FileName property and that the UploadedFiles collection is correctly populated.

I think the problem comes from the idea that the page has x:DataType="viewmodel:UserProfileMauiViewModel"

and the collectionView bind to public ObservableCollection UploadedFiles

FileData class inherited from FileBaseData and IFileData and this is FileBaseData -

public record class FileBaseData : FileKeyData, IEntityKeyNameDataClient, IRemovedData {
    public string FileName { get; set; } = string.Empty;
    public DateTime CreationTime { get; set; }
    public bool IsRemoved { get; set; }
    public string GetEntityName() => FileName;
    public string GetDisplayName() => FileName;
    public FileBaseData() { }
    public FileBaseData(FileBaseData item) : base(item) {
        FileName = item.FileName;
        CreationTime = item.CreationTime;
        IsRemoved = item.IsRemoved;
    }
}

while searching for a solution, I saw someone suggest adding but it didn't work.

I don't know if it matters but UserProfileViewModel is a viewModel in a Blazor project and I building a Maui project so far it didn't interrupted me.

Upvotes: 0

Views: 214

Answers (2)

Hay Abados
Hay Abados

Reputation: 53

To resolve the issue, I added the correct XML namespace for the FileBaseData class in the XAML file and updated the CollectionView to correctly bind to the FileName property.

First, add the namespace for FileBaseData:

XML:

xmlns:data="clr-namespace:webapplib.Shared.Data;assembly=webapplib.Shared"

Then, update the CollectionView in the XAML to bind to the FileName property:

XML:

<CollectionView ItemsSource="{Binding UploadedFiles}" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2">
    <CollectionView.ItemTemplate>
        <DataTemplate x:DataType="data:FileBaseData">
            <Label Text="{Binding FileName}" />
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

This ensures that the FileBaseData type is recognized and the FileName property can be bound correctly in the CollectionView.

By adding the appropriate namespace and specifying the x:DataType for the DataTemplate, the binding to the FileName property works as expected.

Upvotes: 1

Gerald Versluis
Gerald Versluis

Reputation: 33993

Add the x:DataType="models:FileData" attribute to the DataTemplate inside of the CollectionView. And make sure to add xmlns:models="webapplib.Shared.Model" (this is an assumption here that this is the correct namespace based on the code you provided, it might be different) to the root page.

Inside of a DataTemplate the scope changes from your page view model, to just the model that is 1 object inside of your collection that you use as the ItemsSource.

Upvotes: 0

Related Questions