Forum Xamarin.Forms
We are excited to announce that the Xamarin Forums are moving to the new Microsoft Q&A experience. Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

We encourage you to head over to Microsoft Q&A for .NET for posting new questions and get involved today.

How to create a bindable view property inside a custom control

GaetanFGaetanF USMember ✭✭✭

Hi guys,

I am trying to create a view that can contain another view but for unknown reason, I get weird behaviours!
This is what I have so far:

CustomView.xaml:

<?xml version="1.0" encoding="UTF-8"?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms" 
      xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
      x:Class="AppTest.CustomView"
      HorizontalOptions="FillAndExpand"
      VerticalOptions="FillAndExpand"
      BackgroundColor="Aqua">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="1*" />
        <ColumnDefinition Width="1*" />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="1*" />
        <RowDefinition Height="1*" />
        <RowDefinition Height="1*" />
    </Grid.RowDefinitions>

    <Label Grid.Column="0"
           Grid.Row="0"
           Grid.ColumnSpan="3"
           BackgroundColor="Chartreuse"
           Text="fdtyu7osrtjsrdytj"/>
    <ContentView x:Name="ViewContent"
                 Grid.Column="1"
                 Grid.Row="1"
                 HorizontalOptions="FillAndExpand"
                 VerticalOptions="FillAndExpand"
                 BackgroundColor="Coral"/>
</Grid>

CustomView.xaml.cs:

using System.Diagnostics;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace AppTest
{
    [ContentProperty(nameof(Content))]
    [XamlCompilation(XamlCompilationOptions.Skip)]
    public partial class CustomView : Grid
    {
        public CustomView()
        {
            Debug.WriteLine(nameof(InitializeComponent) + " started!");
            InitializeComponent();
            Debug.WriteLine(nameof(InitializeComponent) + " ended");
        }

        #region Content (Bindable Xamarin.Forms.View)

        /// <summary>
        /// Manages the binding of the <see cref="Content"/> property
        /// </summary>
        public static readonly BindableProperty ContentProperty
            = BindableProperty.Create(propertyName: nameof(Content)
                                    , returnType: typeof(Xamarin.Forms.View)
                                    , declaringType: typeof(CustomView)
                                    , defaultBindingMode: BindingMode.OneWay
                                    , propertyChanged: Content_PropertyChanged
                                    );

        public Xamarin.Forms.View Content { get => (Xamarin.Forms.View)GetValue(ContentProperty); set => SetValue(ContentProperty, value); }

        private static void Content_PropertyChanged(BindableObject bindable, object oldValue, object newValue)
        {
            var control = (CustomView)bindable;
            var value = (View)newValue;

            if (control.ViewContent == null) Debug.WriteLine("ViewContent null!");
            if (ReferenceEquals(newValue, control)) Debug.WriteLine("New value is myself!!!!");
            if (newValue is Label label)
            {
                Debug.WriteLine("Added label with text: " + label.Text);
                if (label.Text.Equals("abc")) control.ViewContent.Content = (View)newValue;
            }
        }

        #endregion Content (Bindable Xamarin.Forms.View)
    }
}

CustomPage.xaml:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="AppTest.CustomPage"
             xmlns:local="clr-namespace:AppTest">
    <local:CustomView x:Name="view">
        <Label Text="abc"/>
    </local:CustomView>
</ContentPage>

That's it. The app just launches a CustomPage.
I have a bunch of weird behavior when launching the windows emulator API 19.
Depending on the XamlCompilationOptions, I get different behaviours but always an Aqua coloured page with no labels as a final result :

  • For Skip:

    [0:] InitializeComponent started!
    [0:] ViewContent null!
    [0:] Added label with text: fdtyu7osrtjsrdytj
    [0:] ViewContent null!
    [0:] InitializeComponent ended
    [0:] Added label with text: abc

  • For Compile:

    [0:] InitializeComponent started!
    [0:] Added label with text: fdtyu7osrtjsrdytj
    [0:] InitializeComponent ended
    [0:] Added label with text: abc

I can't get my mind around it! It should basically say InitializeComponent started=>ended then Added label with text: abc.
The project settings are: .Net Standard 1.4 core library, Xamarin.Forms nuget v2.5.0.280555, Xamarin.Android.Support nuget v25.4.0.2, Windows emulator for android.
Can anyone reproduce this behaviour and explain this to me or have an idea, please?

Cheers,

G.

Answers

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    Tutorial on re-usable controls with bindable properties
    https://redpillxamarin.com/2017/01/28/206-reusable-controls/

  • JohnHardmanJohnHardman GBUniversity admin

    @ClintStLaurent - Just in case you're not seeing it, Chrome is reporting the following when I click on that link:

    Your connection is not private
    Attackers might be trying to steal your information from redpillxamarin.com (for example, passwords, messages, or credit cards). Learn more
    NET::ERR_CERT_COMMON_NAME_INVALID

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    @JohnHardman

    Huh? Thanks. I turned off the GeoIPblock temporarily because it was blocking me when trying to admin from work over VPN. I wouldn't think that should affect it.. but.. eh... who knows when it comes to Wordpress? I'll dig into it over the weekend.

  • GaetanFGaetanF USMember ✭✭✭
    edited March 2018

    Thanks @ClintStLaurent for the tutorial link.
    Although I found it interesting, I could not find anything that could solve my issue.
    I tried to trim the CustomView control by removing ContentView control at the root because it's just a layout class with a content property and I was thinking a Grid could do the job as good as a ContentView, am I right? Still, it does not explain the weird behaviour...

  • GaetanFGaetanF USMember ✭✭✭

    Bump!

  • ghasanghasan Member ✭✭

    @ClintStLaurent said:
    Tutorial on re-usable controls with bindable properties

    Thank you for the tutorial. I have been using custom controls for sometime now, but I was in doubt of referencing 'this' for each binding inside the same view. You tutorial assured me. Thanks.

  • ClintStLaurentClintStLaurent USUniversity ✭✭✭✭✭

    @ghasan Cheers mate! Glad it helped.

  • SteveShaw.5557SteveShaw.5557 USMember ✭✭✭
    edited September 2019

    @GaetanF - I see that label "fdtyu7osrtjsrdytj" was sent to Content_PropertyChanged.
    I suspect this is because you have overridden the usual ContentPropertyAttribute of Grid base class, which is Children.
    It looks like each child of your custom grid is sent to Content property, but DOESN'T become a child of the Grid.
    And therefore, is never shown.

    To fix:

    1. remove [ContentProperty(nameof(Content))],
    2. Explicitly indicate the property you are setting value of, each time you set it.

    Details for (2.):

    To avoid confusion, lets give your property a different name, Content2 (and Content2Property). Change those declarations in your source.

    Then replace this line:

    <Label Text="abc"/>
    

    With

    <local:CustomView.Content2>
        <Label Text="abc"/>
    </local:CustomView.Content2>
    

    There might be some other detail I'm forgetting. If so, Google elsewhere for examples of setting custom properties in XAML.
    Once you've gotten that working, you can probably name Content2 back to Content. I recommended changing it during this debugging, to be sure there wasn't any conflict with some existing meaning of Content. (I don't see any in Xamarin.Forms source - the property named Content is added in ContentView class, not in some base class that Grid uses. But I like to be cautious - and to make it clear that we are not using the ContentPropertyAttribute.)


    FWIW, an alternative fix might be to make it explicit in CustomView.xaml, that the children of CustomView are supposed to be children!
    Something like:

    <Grid.Children>
        .. your definitions of label "fdtyu7osrtjsrdytj" and ContentView "ViewContent" ..
    </Grid.Children>
    

    That might allow you to keep your override of ContentPropertyAttribute. So the fix might be as simple as adding those two lines.

Sign In or Register to comment.