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:
<?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>
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();
}
}
}
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.