Custom Frame Binding Not Working

Xamarin NewBie 121 Reputation points
2021-04-05T05:02:29.507+00:00

I copied/wrote a class that inherits from Frame

public class Circle : Frame
{
    //private double _radius;

    public static readonly BindableProperty RadiusProperty = BindableProperty.Create(nameof(Radius), typeof(double), typeof(Circle), 126.0, BindingMode.TwoWay);
    public double Radius
    {
        get => (double)GetValue(RadiusProperty); //_radius;
        set
        {
            SetValue(RadiusProperty, value);
            OnPropertyChanged();
            AdjustSize();
        }
    }

    private void AdjustSize()
    {
        HeightRequest = Radius;
        WidthRequest = Radius;
        Margin = new Thickness(0,0,0,0);
        Padding = new Thickness(0, 0, 0, 0);
        CornerRadius = (float) (Radius / 2);
    }

    public Circle()
    {
        HorizontalOptions = LayoutOptions.Center;
    }
}

The consuming page defines these BinadableProperties

    public static readonly BindableProperty InnerColorProperty = BindableProperty.Create("InnerColor", typeof(Color), typeof(CircleProgressView), defaultValue: Color.FromHex("#34495E"), BindingMode.TwoWay);
    public Color InnerColor
    {
        get => (Color)GetValue(InnerColorProperty);
        set => SetValue(InnerColorProperty, value);
    }

    public static readonly BindableProperty InnerRadiusProperty = BindableProperty.Create("InnerRadius", typeof(double), typeof(CircleProgressView), 126.0, BindingMode.TwoWay);
    public double InnerRadius
    {
        get => (double)GetValue(InnerRadiusProperty);
        set => SetValue(InnerRadiusProperty, value);
    }

And uses the Circle like so

<components:Circle Grid.Row="0" BackgroundColor="{Binding InnerColor}" Radius="{Binding InnerRadius}" >

Alas, the bindable's setter, and hence AdjustSize(), is never called nor is the default value used. Instead of a circle I end up with a rectangle. The BackgroundColor, which is a property of Frame, binds and works fine.

If I remove the BindableProperty and leave behind a regular INotify property

public class Circle : Frame
{
    private double _radius;

    public double Radius
    {
        get => _radius;
        set
        {
            _radius = value;
            OnPropertyChanged();
            AdjustSize();
        }
    }

    private void AdjustSize()
    {
        HeightRequest = Radius;
        WidthRequest = Radius;
        Margin = new Thickness(0,0,0,0);
        Padding = new Thickness(0, 0, 0, 0);
        CornerRadius = (float) (Radius / 2);
    }

    public Circle()
    {
        HorizontalOptions = LayoutOptions.Center;
    }
}

The compiler complains if I keep the InnerRadius binding

Severity Code Description Project File Line Suppression State

Error Position 17:92. No property, bindable property, or event found for 'Radius', or mismatching type between value and property. ...\Components\CircleProgressView.xaml 17

I can replace the Radius binding with a hardcoded value and it runs fine, a circle appears.

<components:Circle Grid.Row="0" BackgroundColor="{Binding InnerColor}" Radius="126" >

What's wrong with a BindableProperty in a regular C# class?

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,294 questions
0 comments No comments
{count} votes

Accepted answer
  1. JessieZhang-MSFT 7,706 Reputation points Microsoft Vendor
    2021-04-06T09:47:48.687+00:00

    Hello,

    Welcome to our Microsoft Q&A platform!

    We can try to handle data in the property changed event of bindable property instead of the setter method of a normal property.
    So ,we can use the following code instead :

       public class Circle: Frame  
        {  
            public static readonly BindableProperty RadiusProperty = BindableProperty.Create(nameof(Radius), typeof(double), typeof(Circle), 125.0, BindingMode.TwoWay, propertyChanged: RadiusChanged);  
            public double Radius  
            {  
                get => (double)GetValue(RadiusProperty); //_radius;  
                set => SetValue(RadiusProperty, value);  
            }  
            static void RadiusChanged(BindableObject bindableObject, object oldValue, object newValue)  
            {  
                Circle circle = bindableObject as Circle;  
                circle.HeightRequest = (double)newValue;  
                circle.WidthRequest = (double)newValue;  
                circle.CornerRadius = (float)((double)newValue / 2);  
            }  
        }  
    

    And use like this:

     <controls:Circle  Radius=" 70"  BackgroundColor="Green"/>  
    

    In addition, if you want to use parent page's bindable property as the Circle's binding context, you can do like this:

    <?xml version="1.0" encoding="utf-8" ?>  
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"  
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"  
                 xmlns:controls="clr-namespace:XamarinFormsPlayground.Controls;assembly=XamarinFormsPlayground"  
                 x:Class="XamarinFormsPlayground.MainPage"  
                 x:Name="myPage"  
                 >  
        <ContentPage.Content>  
            <StackLayout>  
                <controls:Circle  Radius=" 70"  BackgroundColor="Green"/>  
      
                <controls:Circle  BackgroundColor="{Binding InnerColor, Source={x:Reference myPage}}" Radius="{Binding InnerRadius, Source={x:Reference myPage}}"/>  
            </StackLayout>  
        </ContentPage.Content>  
    </ContentPage>  
    

    Note: myPage is property x:Name of current page.

    Best Regards,

    Jessie Zhang

    ---
    If the response is helpful, please click "Accept Answer" and upvote it.

    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.


0 additional answers

Sort by: Most helpful