简体   繁体   中英

Content in a complex Xamarin Forms custom control

I'm creating a custom control by deriving from ContentView . My goal is to have a user consume the class as you would expect to consume any control with a single piece of content:

<local:NewControl>
  <Image source="..." />
</local:NewControl>

However, I would like the control to contain a grid and then have the content placed in the grid. Conceptually (ie ignoring the fact that ContentPresenter cannot be used in this way), I'd like NewControl.xaml to look like this:

<ContentView ...>
  <Grid x:Name="HiddenGrid">
    <ContentPresenter />
  </Grid>
</ContentView>

Doing what I detailed above results in HiddenGrid being replaced by the Image , rather than the image being inserted in the visual tree as a child of the ContentPresenter .

At the moment, I'm achieving my goal by deriving from Grid instead of ContentView , but I'd like to hide this implementation detail so consumers cannot interfere with the Grid and have a control to which they can assign only a single piece of content.

This seems like it should be straightforward (and widely applicable to all sorts of scenarios where content needs to be embellished in some way), but I can't see a way of doing it. Am I missing something obvious, is it not possible, or is it just tricky to do and I need to read up on something (like control templates)?

We can use ControlTemplate. Xamarin.Forms control templates enable to define the visual structure of ContentView derived custom controls, and ContentPage derived pages.

<ContentView.ControlTemplate>
        <ControlTemplate>
            <Grid>
                <Grid.RowDefinitions>
                    <RowDefinition Height="*"></RowDefinition>
                    <RowDefinition Height="*"></RowDefinition>
                </Grid.RowDefinitions>
                <Label Text="Test" Grid.Row="0"></Label>
                <ContentPresenter Grid.Row="1"></ContentPresenter>

            </Grid>
        </ControlTemplate>
        
    </ContentView.ControlTemplate>

Xaml

<local:NewControl>
  <Label Text= "Test"/>
</local:NewControl>

One way i can think of is overriding the ContentProperty of your NewControl : by doing this you can control exactly what happens with the "Content" of the NewControl .

To do this follow the next steps:

  • Define your control's appearance in Xaml
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="HiddenGridTest.NewControl">
    <ContentView.Content>
        <Grid x:Name="HiddenGrid" Padding="20">
            <Grid.RowDefinitions>
                <RowDefinition Height="2*" />
                <RowDefinition Height="*" />
                <RowDefinition Height="100" />
            </Grid.RowDefinitions>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="*" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>

        </Grid>
    </ContentView.Content>
</ContentView>

Very important here is that you put your Grid inside the Content property explicitly!

<ContentView.Content>
</ContentView.Content>
  • Override the ContentProperty of your control and link it to the Children of the HiddenGrid
using System.Collections.Generic;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace HiddenGridTest
{
    [ContentProperty("Contents")]
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class NewControl : ContentView
    {

        public IList<View> Contents { get => HiddenGrid.Children; }

        public NewControl()
        {
            InitializeComponent();
        }
    }
}
  • Consume your NewControl
<?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:local="clr-namespace:HiddenGridTest"
             x:Class="HiddenGridTest.MainPage">

    <local:NewControl>
        <Label  Text="Inside grid"
               BackgroundColor="Red"/>
    </local:NewControl>
    
</ContentPage>

The result is shown in the image below. Note that the image is set as content of the Grid (occupying the first row and first column:):

在此处输入图像描述


Interestingly enough, i also answered a couple of questions this week where also overriding the ContentProperty was useful. Since they could complete the information posted in this answer, i leave the links here:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM