Bongo
Bongo

Reputation: 3153

.net Maui binding values multiple levels deep

How can I Pass a Binding from a Page to a View?

I have this Page(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:views="clr-namespace:DataBindingTests.Views"
             xmlns:model="clr-namespace:DataBindingTests.ViewModels"
             x:Class="DataBindingTests.Pages.CoolePage"
             Title="CoolePage"
             x:DataType="model:CoolesModel">
    <VerticalStackLayout>
        <Label Text="{Binding YourName}"></Label>
        <views:MainView YourName="{Binding YourName}"></views:MainView>
        <Button Command="{Binding ChangeNameCommand}"></Button>
    </VerticalStackLayout>
</ContentPage>

And its CodeBehind:

using DataBindingTests.ViewModels;

namespace DataBindingTests.Pages;

public partial class CoolePage : ContentPage
{
    public CoolePage()
    {
        this.BindingContext = new CoolesModel();
        InitializeComponent();
    }
}

If I pass a String into my MainView it works and all events are fired. When I use the binding it doesn't. In this simple test, the app should display two times the same name, but only the Label of the ContentPage has the YourName property printed

<views:MainView YourName="Lars"></views:MainView> <-- Works

<views:MainView YourName="{Binding YourName}"></views:MainView> <-- doesn't work

This is the Xaml of the MainView

<?xml version="1.0" encoding="utf-8" ?>
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:views="clr-namespace:DataBindingTests.Views"
             x:Class="DataBindingTests.Views.MainView">
    <VerticalStackLayout>
        <Label Text="{Binding YourName}"
            VerticalOptions="Center" 
            HorizontalOptions="Center" />
    </VerticalStackLayout>
</ContentView>

This is the CodeBehind of the MainView

namespace DataBindingTests.Views;

public partial class MainView : ContentView
{
    public String YourName
    {
        get
        {
            String value = (String)GetValue(MainView.YourNameProperty);
            return value;
        }
        set
        {
            SetValue(MainView.YourNameProperty, value);
        }
    }

    public static readonly BindableProperty YourNameProperty = BindableProperty.Create(nameof(YourName)
    , typeof(String)
    , typeof(MainView), defaultBindingMode:BindingMode.TwoWay, propertyChanged: OnYourNameChanged);

    static void OnYourNameChanged(BindableObject bindable, object oldValue, object newValue)
    {
        Console.WriteLine(newValue);
    }

    public MainView()
    {
        this.BindingContext = this; // Ignore ParentContext
        InitializeComponent();
    }
}

Upvotes: 0

Views: 2499

Answers (2)

Jessie Zhang -MSFT
Jessie Zhang -MSFT

Reputation: 13889

You can just remove code this.BindingContext = this; from the constructor of MainView.xaml.cs:

 public MainView()
    {
        //this.BindingContext = this;
        InitializeComponent();
    }

Update:

the above code would only work because the Property in the View and the Page have the same name.

In this condition, you can modify the code of MainView.xaml as follows:

 <?xml version="1.0" encoding="utf-8" ?> 
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiApp929.MainView"
             x:Name="TestControlView"
             >
    <VerticalStackLayout>
        <Label   Text="{Binding Source={x:Reference TestControlView}, Path=YourName}"
            VerticalOptions="Center" 
            HorizontalOptions="Center" />
    </VerticalStackLayout>
</ContentView>

MainView.xaml.cs

public partial class MainView : ContentView 
{

    public String YourName
    {
        get
        {
            String value = (String)GetValue(YourNameProperty);
            return value;
        }
        set
        {
            SetValue(YourNameProperty, value);
        }
    }

    public static readonly BindableProperty YourNameProperty = BindableProperty.Create(nameof(YourName)
    , typeof(String)
    , typeof(MainView), defaultBindingMode: BindingMode.TwoWay, propertyChanged: OnYourNameChanged);

    static void OnYourNameChanged(BindableObject bindable, object oldValue, object newValue)
    {
        Console.WriteLine("-----------------> "+newValue);
    }
    public MainView()
      {
            InitializeComponent();

       // this.BindingContext = this;
    }
}

CoolesModel.cs

public class CoolesModel 
{
    //  public string YourName { get; set; }
    public string Name { get; set; }

    public string TestName { get; set; }

    public ICommand ChangeNameCommand => new Command(changeMethod);

    private void changeMethod()
    {

    }

    public CoolesModel() {
        //YourName = "abc";
        Name = "abc";

        TestName = "test123...";
    }
}

MainPage.xaml.cs

<?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:mauiapp929="clr-namespace:MauiApp929"
             x:Class="MauiApp929.MainPage">
  
    <ScrollView>
        <VerticalStackLayout>
            <Label Text="{Binding Name}"></Label>
            <mauiapp929:MainView YourName="{Binding TestName}"></mauiapp929:MainView>
            <Button Command="{Binding ChangeNameCommand}"></Button>
        </VerticalStackLayout>
    </ScrollView>

</ContentPage>

Upvotes: 1

Blindy
Blindy

Reputation: 67447

In this simple test, the app should display two times the same name, but only the Label of the ContentPage has the YourName property printed

You're overwriting your binding context half way through for some reason, and the context your page binding resolves (the normal way of using it, the parent context) is different than what you actually see on the screen (which is your this.BindingContext = this). And you never set your second context's property.

Upvotes: 0

Related Questions