简体   繁体   中英

Reusable XAML control in Xamarin.Forms

I have written a nice Grid with some other controls like: Entry and Image and now I would like to reuse it the simplest way.

This is my control for Email property:

<Grid
                Style="{StaticResource gridEntryStyle}">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="9*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="20" />
                    <RowDefinition Height="7" />
                    <RowDefinition Height="20" />
                </Grid.RowDefinitions>

                <controls:ExtendedEntry
                    Grid.Row="0"
                    Grid.Column="0"
                    Text="{Binding UserEmail, Mode=TwoWay}"
                    Placeholder="{i18n:Translate UserEmailPlaceholder}"
                    Style="{StaticResource entryStyle}">

                    <controls:ExtendedEntry.Behaviors>
                        <behavior:EventToCommandBehavior 
                            EventName="Focused" 
                            Command="{Binding ControlFocusCommand}"
                            CommandParameter="UserEmail"/>
                        <behavior:EventToCommandBehavior 
                            EventName="Unfocused" 
                            Command="{Binding ControlUnfocusedCommand}"
                            CommandParameter="UserEmail"/>
                    </controls:ExtendedEntry.Behaviors>

                </controls:ExtendedEntry>

                <Image
                    Grid.Row="0"
                    Grid.Column="1"
                    Source="clear.png"
                    IsVisible="{Binding IsEntryFocused}"
                    Style="{StaticResource imageClearStyle}">

                    <Image.GestureRecognizers>
                        <TapGestureRecognizer
                            Command="{Binding ClearCommand}"
                            CommandParameter="UserEmail"/>
                    </Image.GestureRecognizers>

                </Image>

                <Image
                    Grid.Row="1"
                    Grid.Column="0"
                    Grid.ColumnSpan="2"
                    Source="lineWhite.png"
                    Style="{StaticResource imageLineStyle}"/>

                <Image
                    Grid.Row="1"
                    Grid.Column="0"
                    Grid.ColumnSpan="2"
                    Source="linePure.png"
                    Style="{StaticResource imageLineStyle}"
                    IsVisible="{Binding IsError}"/>

                <Image
                    Grid.Row="1"
                    Grid.Column="0"
                    Grid.ColumnSpan="2"
                    Source="lineGradient.png"
                    Style="{StaticResource imageLineStyle}"
                    IsVisible="{Binding IsEntryFocused}"/>

                <Label
                    Grid.Row="2"
                    Grid.Column="0"
                    Text="{Binding ErrorMessage}"
                    Style="{StaticResource labelErrorStyle}"
                    IsVisible="{Binding IsError}"/>

                <Image
                    Grid.Row="2"
                    Grid.Column="1"
                    Source="error.png"
                    Style="{StaticResource imageErrorStyle}"
                    IsVisible="{Binding IsError}"/>

            </Grid>

I would like to reuse it for example as follows:

<usercontrols:EntryControl 
                MainText="{Binding UserEmail}"
                MainTextPlaceholder="{i18n:Translate UserEmailPlaceholder}" />

For now even this simple example is not working and I have no idea how to define Command in this control. For now I have:

public partial class EntryControl : ContentView
{
    public EntryControl()
    {
        InitializeComponent();
    }

    public static readonly BindableProperty MainTextProperty =
        BindableProperty.Create(
            propertyName: "MainText",
            returnType: typeof(string),
            declaringType: typeof(string),
            defaultValue: string.Empty,
            defaultBindingMode: BindingMode.TwoWay);

    public string MainText
    {
        get { return (string)this.GetValue(MainTextProperty); }
        set { this.SetValue(MainTextProperty, value); }
    }

    public static readonly BindableProperty MainTextPlaceholderProperty =
        BindableProperty.Create(
            propertyName: "MainTextPlaceholder", 
            returnType: typeof(string), 
            declaringType: typeof(string), 
            defaultValue: string.Empty, 
            defaultBindingMode: BindingMode.TwoWay);

    public string MainTextPlaceholder
    {
        get { return (string)this.GetValue(MainTextPlaceholderProperty); }
        set { this.SetValue(MainTextPlaceholderProperty, value);}
    }
}

Is this the right way? or is this even possible in Xamarin.Forms?

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="ApplicationName.Controls.EntryControl"
      Style="{StaticResource gridEntryStyle}">
    </Grid>

xaml.cs:

namespace ApplicationName.Controls
{
    public partial class EntryControl : Grid
    {
        public static readonly BindableProperty CommandProperty =
        BindableProperty.Create(
            propertyName: nameof(Command),
            returnType: typeof(ICommand),
            declaringType: typeof(EntryControl),
            defaultValue: null,
            defaultBindingMode: BindingMode.TwoWay);

        public string Command
        {
            get { return (string)this.GetValue(CommandProperty); }
            set { this.SetValue(CommandProperty, value); }
        }

        public EntryControl()
        {
            InitializeComponent();
        }
    }
}

using:

xmlns:controls="clr-namespace:ApplicationName.Controls;assembly=ApplicationName"

<controls:EntryLabel/>

Your issue with BindingContext

In short, you have to write down the bindings inside your control like this {Binding UserEmail, Mode=TwoWay, Source={x:Reference myControlTHIS}} , where 'myControlTHIS' is x:Name="TheCategoryHeader".


More info:

BindingContext is everything when it comes to getting things to bind and work right in an MVVM app – WPF or Xamarin. Controls inherit context from the parent control unless a different context is explicitly assigned. That's what we need to do here. We need to tell each UI element (label, entry, button, etc.) to explicitly look at this control for its context in order to find those BindingProperties we just made. This is one of the rare occasions when we actually give a XAML element a name: When it is going to be referenced by another XAML element within the XAML itself. To the ContentView, add a tag naming the control 'this'. That's right. We're going to keep to the Microsoft naming and have this item refer to itself as 'myControlTHIS'. It makes all of us comfortable and the code and markup easy to read and follow.

We can now use 'myControlTHIS' as a reference source telling the rest of our XAML where to look for properties to bind to.

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