mvvmbindingmauisyncfusion

Binding data to a ContentView in Syncfusion Popup (MAUI)


I'm trying to have a Syncfusion Popup in my app. Because of the size and for reusability I put the Popup's content into a separate ContentView. However this makes the Binding to the content fail. I made a minimal (not) working example for this with two Popups, one has a Label and the other has the Label moved to a ContentView. This is part of a new Maui App Project in VS, so I will only post the relevant code.

MainPage.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:sfPopup="clr-namespace:Syncfusion.Maui.Popup;assembly=Syncfusion.Maui.Popup"
             xmlns:local="clr-namespace:MinimalNotWorkingExample"
             x:Class="MinimalNotWorkingExample.MainPage">

    <ScrollView>
        <VerticalStackLayout>
            <Button
                x:Name="CounterBtn"
                Text="Click me" 
                SemanticProperties.Hint="Counts the number of times you click"
                Clicked="OnCounterClicked"
                HorizontalOptions="Fill" />
            <Button
                x:Name="CounterBtn2"
                Text="Click me" 
                SemanticProperties.Hint="Counts the number of times you click"
                Clicked="CounterBtn_Clicked"
                HorizontalOptions="Fill" />

            <sfPopup:SfPopup x:Name="popup" HeaderTitle="{Binding MyString}" AppearanceMode="OneButton" ShowFooter="True" AutoSizeMode="Both">
                <sfPopup:SfPopup.ContentTemplate>
                    <DataTemplate>
                        <local:MyView Data="{Binding MyString}"/>
                    </DataTemplate>
                </sfPopup:SfPopup.ContentTemplate>
            </sfPopup:SfPopup>
            
            <sfPopup:SfPopup x:Name="popup2" HeaderTitle="{Binding MyString}" AppearanceMode="OneButton" ShowFooter="True" AutoSizeMode="Both">
                <sfPopup:SfPopup.ContentTemplate>
                    <DataTemplate>
                        <VerticalStackLayout>
                            <Label 
                                Text="If you can Read this, the Content was loaded directly. IF you can see the same text below as in the header, the Binding is working."
                                VerticalOptions="Center" 
                                HorizontalOptions="Center"
                                TextColor="Black"/>
                            <Label Text="{Binding MyString}" TextColor="Black"/>
                        </VerticalStackLayout>
                    </DataTemplate>
                </sfPopup:SfPopup.ContentTemplate>
            </sfPopup:SfPopup>
        </VerticalStackLayout>
    </ScrollView>

</ContentPage>

MainPage.cs

namespace MinimalNotWorkingExample
{
    public partial class MainPage : ContentPage
    {
        int count = 0;

        MyViewModel vm = new MyViewModel();

        public MainPage()
        {
            BindingContext = vm;
            InitializeComponent();
        }

        private void OnCounterClicked(object? sender, EventArgs e)
        {
            count++;

            if (count == 1)
                CounterBtn.Text = $"Clicked {count} time";
            else
                CounterBtn.Text = $"Clicked {count} times";

            SemanticScreenReader.Announce(CounterBtn.Text);
            vm.MyString = CounterBtn.Text;
            popup.Show();
        }

        private void CounterBtn_Clicked(object sender, EventArgs e)
        {
            count++;

            if (count == 1)
                CounterBtn2.Text = $"Clicked {count} time";
            else
                CounterBtn2.Text = $"Clicked {count} times";

            SemanticScreenReader.Announce(CounterBtn2.Text);
            vm.MyString = CounterBtn2.Text;
            popup2.Show();
        }
    }
}

MyView.xaml

<?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="MinimalNotWorkingExample.MyView">
    <VerticalStackLayout>
        <Label 
            Text="If you can Read this, the ContentView was loaded. IF you can see the same text below as in the header, the Binding is working."
            VerticalOptions="Center" 
            HorizontalOptions="Center"
            TextColor="Black"/>
        <Label 
            Text="{Binding Data}"
            VerticalOptions="Center" 
            HorizontalOptions="Center"
            TextColor="Black"/>
    </VerticalStackLayout>
</ContentView>

MyView.cs

namespace MinimalNotWorkingExample;

public partial class MyView : ContentView
{
    public static readonly BindableProperty DataProperty =
    BindableProperty.Create("Data", typeof(string), typeof(MyView));
    public string Data
    {
        get => (string)GetValue(DataProperty);
        set => SetValue(DataProperty, value);
    }
    public MyView()
    {
        BindingContext = this;
        InitializeComponent();
    }
}

MyViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using System;
using System.Collections.Generic;
using System.Text;

namespace MinimalNotWorkingExample
{
    public partial class MyViewModel : ObservableObject
    {
        [ObservableProperty]
        public partial string MyString {  get; set; }
    }
}

if you run this code you can see that the binding is only working when the content is set directly and not through a separete ContentView.


Solution

  • Update after comment

    You have to tell MyView.xaml where to search for Data binding. In case you place your view in a namespace named controls

    namespace MinimalNotWorkingExample;
    
    public partial class MyView : ContentView
    {
       public class CustomClass
       {
          public String Id { get; set; }
          public String Name { get; set; }
       }
    
       public static readonly BindableProperty DataProperty =
          BindableProperty.Create(
             propertyName: nameof(Data),
             returnType: typeof(CustomClass),
             declaringType: typeof(MyView),
             defaultValue: default(CustomClass));
    
       public CustomClass Data
       {
          get { return (CustomClass)GetValue(DataProperty); }
          set { SetValue(DataProperty, value); }
       }
       
       public MyView()
       {
          InitializeComponent();
       }
    }
    
    <?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:controls="clr-namespace:MinimalNotWorkingExample.Controls;assembly=MinimalNotWorkingExample"
                 x:Class="MinimalNotWorkingExample.MyView">
        <VerticalStackLayout>
            <Label 
                Text="If you can Read this, the ContentView was loaded. IF you can see the same text below as in the header, the Binding is working."
                VerticalOptions="Center" 
                HorizontalOptions="Center"
                TextColor="Black"/>
            <Label 
                Text="{Binding Source={RelativeSource AncestorType={x:Type controls:MyView}}, Path=Data.Name, x:DataType=controls:MyView}"
                VerticalOptions="Center" 
                HorizontalOptions="Center"
                TextColor="Black"/>
        </VerticalStackLayout>
    </ContentView>