Home > Back-end >  Binding Property not found on ViewModel even though it exists
Binding Property not found on ViewModel even though it exists

Time:01-13

I created a model class called Project and also created a ViewModel class called MainPageViewModel.

What I want to implement is a simple table that has multiple columns. There should be one column named "Action" and that column should have one button.

Here is the 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"
             x:Class="SharpTool.UI.MainPage"
             xmlns:viewmodel="clr-namespace:SharpTool.UI.ViewModels"
             xmlns:models ="clr-namespace:SharpTool.UI.Models"
             x:DataType="viewmodel:MainPageViewModel">
    
    <ScrollView>
        <VerticalStackLayout
            Padding="10, 10">
            <Border MinimumHeightRequest="50" StrokeThickness ="0.5">
    
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="*"/>
                    </Grid.ColumnDefinitions>
    
                    <Border StrokeThickness="0.1" Grid.Column="0">
                        <Label HorizontalOptions="Center" VerticalOptions="Center" FontFamily="icon" FontSize="15" Text="Id" TextColor="Blue"/>
                    </Border>
    
                    <Border StrokeThickness="0.1" Grid.Column="1">
                        <Label HorizontalOptions="Center" VerticalOptions="Center" FontFamily="icon" FontSize="15" Text="Name" TextColor="Blue"/>
                    </Border>
    
                    <Border StrokeThickness="0.1"  Grid.Column="2">
                        <Label HorizontalOptions="Center" VerticalOptions="Center" FontFamily="icon" FontSize="15" Text="Discription" TextColor="Blue"/>
                    </Border>
    
                    <Border StrokeThickness="0.1" Grid.Column="3">
                        <Label HorizontalOptions="Center" VerticalOptions="Center" FontFamily="icon" FontSize="15" Text="Version" TextColor="Blue"/>
                    </Border>
                        
                    <Border StrokeThickness="0.1" Grid.Column="4">
                        <Label HorizontalOptions="Center" VerticalOptions="Center" FontFamily="icon" FontSize="15" Text="Action" TextColor="Blue"/>
                    </Border>
    
                </Grid>
    
            </Border>
    
            <CollectionView ItemsSource="{Binding Projects}" >
    
                <CollectionView.ItemsLayout>
                    <LinearItemsLayout Orientation="Vertical"/>
                </CollectionView.ItemsLayout>
    
                <CollectionView.ItemTemplate>
                    <DataTemplate x:DataType="models:Project">
    
                        <Border MinimumHeightRequest="50" StrokeThickness ="0.5" >
    
                            <Grid>
                                <Grid.ColumnDefinitions>
                                    <ColumnDefinition Width="*" />
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="*"/>
                                    <ColumnDefinition Width="*"/>
                                </Grid.ColumnDefinitions>
    
                                <Border StrokeThickness="0.1" Grid.Column="0">
                                    <Label HorizontalOptions="Center" VerticalOptions="Center" FontFamily="icon" FontSize="15" Text="{Binding Id}" TextColor="Blue"/>
                                </Border>
    
                                <Border StrokeThickness="0.1" Grid.Column="1">
                                    <Label HorizontalOptions="Center" VerticalOptions="Center" FontFamily="icon" FontSize="15" Text="{Binding Name}" TextColor="Blue"/>
                                </Border>
    
                                <Border StrokeThickness="0.1"  Grid.Column="2">
                                    <Label HorizontalOptions="Center" VerticalOptions="Center" FontFamily="icon" FontSize="15" Text="{Binding Description}"  TextColor="Blue"/>
                                </Border>
    
                                <Border StrokeThickness="0.1" Grid.Column="3">
                                    <Label HorizontalOptions="Center" VerticalOptions="Center" FontFamily="icon" FontSize="15" Text="{Binding Version}" TextColor="Blue"/>
                                </Border>
    
                                <Border StrokeThickness="0.1" Grid.Column="4">
    
                                    <Button
                                        Text="Click me"
                                        SemanticProperties.Hint="Counts the number of times you click"
                                        Command="{Binding GetProjectByCommand}"
                                        CommandParameter="2"
                                        HorizontalOptions="Center" />
                                </Border>
    
                            </Grid>
    
                        </Border>
    
                    </DataTemplate>
    
                </CollectionView.ItemTemplate>
            </CollectionView>
    
        </VerticalStackLayout>
    </ScrollView>
    
</ContentPage>

I managed to implement the UI code but I have a problem. When I set the x:DataType="models:Project" in the DataTemplate I can't bind the ViewModel methods to buttons or any other components.

It gives me an error:

Binding: Property "GetProjectByCommand" not found on "SharpTool.UI.Models.Project".

If I remove this part: Command="{Binding GetProjectByCommand}" in the button inside the Grid, then the project builds and runs without any issues.

This is the ViewModel code:

[INotifyPropertyChanged]
public partial class MainPageViewModel
{
    [ObservableProperty]
    ObservableCollection<Project> projects = new();
    
    [ObservableProperty]
    public bool isBusy = false;
    
    public ICommand GetProjectByCommand { private set; get; }
    
    public MainPageViewModel()
    {
        projects.Add(new Project { Id = 1, Name = "Project 1 Name", Description = "Project 1 Description", Version = "1.0" });
        projects.Add(new Project { Id = 2, Name = "Project 2 Name", Description = "Project 2 Description", Version = "1.0" });
        projects.Add(new Project { Id = 3, Name = "Project 3 Name", Description = "Project 3 Description", Version = "1.0" });
        projects.Add(new Project { Id = 4, Name = "Project 1 Name", Description = "Project 1 Description", Version = "1.0" });
        projects.Add(new Project { Id = 5, Name = "Project 2 Name", Description = "Project 2 Description", Version = "1.0" });
        projects.Add(new Project { Id = 6, Name = "Project 3 Name", Description = "Project 3 Description", Version = "1.0" });
        projects.Add(new Project { Id = 7, Name = "Project 1 Name", Description = "Project 1 Description", Version = "1.0" });
        projects.Add(new Project { Id = 8, Name = "Project 2 Name", Description = "Project 2 Description", Version = "1.0" });
        projects.Add(new Project { Id = 9, Name = "Project 3 Name", Description = "Project 3 Description", Version = "1.0" });
        projects.Add(new Project { Id = 10, Name = "Project 3 Name", Description = "Project 3 Description", Version = "1.0" });
    
        GetProjectByCommand = new Command<string>(GetProjectById);
    }
    
    private void GetProjectById(string id)
    {
        //query project list
    }
}

So, how do I bind that GetProjectById method to that button inside the Grid?

CodePudding user response:

This is something that happens a lot. The issue here is that whenever you are in a control that shows items, the data-binding scope will change.

For you full page the view model is your MainPageViewModel. You then have a CollectionView that reads from the Projects property of your MainPageViewModel. So far, so good.

However, inside of your CollectionView, especially inside of your ItemTemplate you are no longer scoped to the rest of your page, but the binding context is now the type of whatever item you put in there, in your case Project. You can also see this because you have to specify <DataTemplate x:DataType="models:Project"> which should indicate to you that you're now looking at Project objects and not the MainPageViewModel anymore.

There is multiple options to solve this. The most obvious, but not necessarily best, is to add the GetProjectByCommand to your Project object. However, technically this is probably a responsibility of your MainPageViewModel.

The other option is to change your binding to make sure that it looks at your MainPageViewModel again. In order to do this, change this line:

<Button
    Text="Click me"
    SemanticProperties.Hint="Counts the number of times you click"
    Command="{Binding GetProjectByCommand}"
    CommandParameter="2"
    HorizontalOptions="Center" />

to this:

<Button
    Text="Click me"
    SemanticProperties.Hint="Counts the number of times you click"
    Command="{Binding GetProjectByCommand, Source={x:Reference mainPage.BindingContext}}"
    CommandParameter="2"
    HorizontalOptions="Center" />

In order for this to work make sure that you add the x:Name="mainPage" attribute to your ContentPage node.

What this does is set the source of the binding to the mainPage.BindingContext which is your MainPageViewModel.

More information about this can also be found in my video about it: https://www.youtube.com/watch?v=Or_qn8i8jVM

I just noticed that in my video I use a different syntax:

<Button
    Text="Click me"
    SemanticProperties.Hint="Counts the number of times you click"
    Command="{Binding Path=BindingContext.GetProjectByCommand, Source={Reference mainPage}}"
    CommandParameter="2"
    HorizontalOptions="Center" />

Which is essentially the same but written different.

  • Related