I'm creating a custom control for an entry that can be validated.
I did this by creating a ContentView that has a Grid as it's child that contains the entry, error label, etc.
I'd like this to be flexible when it comes to validation, so ideally it would be nice to expose the Entry's MultiValidationBehavior's Children property, or set that property as my control's content property.
As it stands now, I haven't figured out a way to add behaviors to my custom control.
Is this possible?
<ContentView x:Class="MPK.UI.Views.Components.FormEntry"
x:Name="FormEntryControl"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit"
xmlns:yummy="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"
xmlns:converters="clr-namespace:MPK.UI.Converters;assembly=MPK.UI">
<ContentView.Resources>
<ResourceDictionary>
<converters:IsValidToEntryBorderConverter x:Key="IsValidToEntryBorderConverter"/>
<converters:ErrorsToLabelTextConverter x:Key="ErrorsToLabelTextConverter"/>
<xct:InvertedBoolConverter x:Key="InvertedBoolConverter" />
</ResourceDictionary>
</ContentView.Resources>
<Grid>
<yummy:PancakeView CornerRadius="10"
HeightRequest="50"
HorizontalOptions="FillAndExpand"
BackgroundColor="{StaticResource EntryBackgroundColor}"
Padding="16,0,16,0">
<yummy:PancakeView.Behaviors>
<xct:AnimationBehavior AnimateCommand="{Binding Source={x:Reference FormEntryControl}, Path=ShakeCommand}">
<xct:AnimationBehavior.AnimationType>
<xct:ShakeAnimation />
</xct:AnimationBehavior.AnimationType>
</xct:AnimationBehavior>
</yummy:PancakeView.Behaviors>
<yummy:PancakeView.Border>
<yummy:Border
Color="{Binding IsValid, Source={x:Reference MultiValidationBehavior}, Converter={StaticResource IsValidToEntryBorderConverter}}"
Thickness="1" />
</yummy:PancakeView.Border>
<Entry x:Name="Entry"
Text="{Binding Text, Source={x:Reference FormEntryControl}}"
Placeholder="{Binding Placeholder, Source={x:Reference FormEntryControl}}"
ReturnType="{Binding ReturnType, Source={x:Reference FormEntryControl}}"
ReturnCommand="{Binding ReturnCommand, Source={x:Reference FormEntryControl}}"
PlaceholderColor="{StaticResource EntryPlaceholderTextColor}"
BackgroundColor="Transparent"
IsPassword="{Binding IsPassword, Source={x:Reference FormEntryControl}}"
ClearButtonVisibility="{Binding ClearButtonVisibility, Source={x:Reference FormEntryControl}}">
<Entry.Effects>
<xct:RemoveBorderEffect />
</Entry.Effects>
<Entry.Behaviors>
<xct:MultiValidationBehavior x:Name="MultiValidationBehavior"
IsValid="{Binding IsValid, Source={x:Reference FormEntryControl}, Mode=TwoWay}"
Children="{Binding ValidationBehaviors, Source={x:Reference FormEntryControl}}"/>
<!-- Binding children doesn't work here -->
</Entry.Behaviors>
</Entry>
</yummy:PancakeView>
<xct:Expander Margin="8,4,0,0"
AnimationLength="100"
IsExpanded="{Binding IsValid, Source={x:Reference FormEntryControl}, Mode=OneWay, Converter={StaticResource InvertedBoolConverter}}">
<Label Text="{Binding Errors, Source={x:Reference MultiValidationBehavior}, Converter={StaticResource ErrorsToLabelTextConverter}}"
TextColor="{StaticResource ErrorColor}" />
</xct:Expander>
</Grid>
</ContentView>
The solution was much simpler than I expected.
In my control's code behind I needed to add a property that points to the multivalidationbehavior's children property.
public IList<ValidationBehavior> ValidationBehaviors => TheMultiValidationBehavior.Children;
Using my custom control looks something like this:
<components:FormEntry Placeholder="Name">
<components:FormEntry.ValidationBehaviors>
<xct:TextValidationBehavior MinimumLength="1" xct:MultiValidationBehavior.Error="Min: 1"/>
</components:FormEntry.ValidationBehaviors>
</components:FormEntry>
Related
I created a customer entry which contain a label for error message
<Grid xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ExampleApp.Contols.CustomerEntry"
Title="Customer"
x:Name="AppEntry">
<Entry x:Name="ControlEntry"
BackgroundColor="{Binding BackgroundColor, Source={x:Reference AppEntry}}"/>
<Label x:Name="ConrolErrorMessage"
Text="{Binding ErrorMessage, Source={x:Reference AppEntry}}"/>
</Grid>
Now using the control, in any page.
<CustomerEntry
x:Name="Password"
BackgroundColor="Blue">
<controls:EntryControl.Behaviors>
<toolkit:TextValidationBehavior
InvalidStyle="{StaticResource InvalidEntry}"
MinimumLength="10"
MaximumLength="100" />
</controls:EntryControl.Behaviors>
</CustomerEntry>
The InvalidEntry style is simple:
<Style
x:Key="InvalidEntry"
TargetType="controls:CustomerEntry">
<Setter Property="BackgroundColor" Value="Red" />
</Style>
Now the behavior is not working. Is it possible to bind pass the behavior to the Entry of the customer control?
I try to make the entry of the customer control as follow:
<Entry x:Name="ControlEntry"
BackgroundColor="{Binding BackgroundColor, Source={x:Reference AppEntry}}"
Behaviors="{Binding Behaviors, Source={x:Reference AppEntry}}"/>
The above code didn't work.
I have to opacify (make semi-transparent) the background image with a colored veil, I tried adding a frame but I don't get the desired effect`
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewmodels="clr-namespace:GET_SOUND_V2.ViewModels" x:DataType="viewmodels:LoginViewModel"
mc:Ignorable="d"
x:Class="GET_SOUND_V2.Views.LoginPage"
BackgroundImage="maco.jpg">
<ContentPage.Content>
<Frame BorderColor="Transparent" Opacity="0.7">
<Frame.Background>
<LinearGradientBrush StartPoint="0,0"
EndPoint="1,0">
<GradientStop Color="#1d57a8"
Offset="0.1"/>
<GradientStop Color="#1d57a8"
Offset="1.0"/>
</LinearGradientBrush>
</Frame.Background>
<StackLayout Orientation="Vertical" Padding="30" Spacing="40">
<BoxView HeightRequest="30"/>
<StackLayout Orientation="Vertical">
<Entry x:Name="txtUserName" Text="{Binding UserName}" HorizontalTextAlignment="Center" Placeholder="UserName"
PlaceholderColor="White" HeightRequest="40"
Keyboard="Email"
TextColor="White"
Focused="BiometricCredentials" />
<Entry x:Name="txtPassword" Text="{Binding Password}" HorizontalTextAlignment="Center" Placeholder="Password"
PlaceholderColor="White" HeightRequest="40"
IsPassword="True"
TextColor="White"/>
</StackLayout>
<Button Command="{Binding SubmitCommand}" Text="Accedi" TextColor="White" CornerRadius="50"
FontAttributes="Bold" FontSize="Large" HorizontalOptions="FillAndExpand" VerticalOptions="EndAndExpand"
Margin="0,0,0,150" BackgroundColor="#EF7F1A" />
</StackLayout>
</Frame>
</ContentPage.Content>
</ContentPage>
I would like the elements, however, not to be affected by the opacity value (still be fully opaque), any advice on how I could do?
One approach is to "overlay" two elements over each other, using a single-cell Grid.
The "underneath" item is the image (with opacity applied).
The "top" item is the rest of your layout.
.
<Grid>
<Frame ...>
...
</Frame>
<StackLayout ...>
...
</StackLayout>
</Grid>
Notice that StackLayout is no longer inside Frame. Because no Grid.Row or Grid.Column is specified on these two elements, they both are at Row=0, Column=0. Hence, overlaid one on top of the other.
May need to add properties to <Grid> element, to get desired size, etc. But start with what I show, see what happens.
I'm new to Xamarin forms.
I'm trying to implement a switch in a registration page, that when toggled, it should show 3 more entries on the same page, if not, it just shows the first 3 entries (email, password and confirmPassword).
I'm using MVVM so I already have the RegistraPageViewModel created and would love to keep using this architecture.
Attached images are what I want to accomplish with the registration page before and after toggling the switch. Code below of the RegistrationPage.xaml (only the section pertinent to the question)
<StackLayout Orientation="Horizontal" Spacing="10">
<Label Text="Are you a service provider?"/>
<Label Text="Yes/No"/>
<Switch x:Name="SwitchIsToggled" HorizontalOptions="CenterAndExpand"
OnColor="Orange"
IsToggled="False"
ThumbColor="{StaticResource MainButtonColor}"
/>
</StackLayout>
<StackLayout x:Name="IsProvider" IsVisible="{Binding SwitchIsToggled}">
<StackLayout.Triggers>
<DataTrigger TargetType="StackLayout"
Binding="{Binding Source={x:Reference IsProvider}}">
<Setter Property="IsVisible" Value="false"/>
</DataTrigger>
</StackLayout.Triggers>
<Entry x:Name="providerCompanyEntry"
Placeholder="Company Name"
Keyboard="Default"/>
<Entry x:Name="timEntry"
Placeholder="TIN"
Keyboard="Numeric"/>
<Entry x:Name="addressEntry"
Placeholder="Address"
Keyboard="Default"/>
</StackLayout>
You just need to bind the IsVisible propery of Entry to the IsToggled property of the Switch.
somethig like :
<StackLayout>
<Switch x:Name="SwitchIsToggled" HorizontalOptions="CenterAndExpand"
OnColor="Orange"
IsToggled="False"
ThumbColor="{StaticResource MainButtonColor}"
/>
<Entry Placeholder="email"></Entry>
<Entry Placeholder="password"></Entry>
<Entry Placeholder="confirm password"></Entry>
<Entry Placeholder="Company Name" IsVisible="{Binding IsToggled}" BindingContext="{x:Reference SwitchIsToggled}"></Entry>
<Entry Placeholder="TIN" IsVisible="{Binding IsToggled}" BindingContext="{x:Reference SwitchIsToggled}"></Entry>
<Entry Placeholder="Address" IsVisible="{Binding IsToggled}" BindingContext="{x:Reference SwitchIsToggled}"></Entry>
</StackLayout>
Update :
public Page2()
{
InitializeComponent();
cname.BindingContext = SwitchIsToggled;
cname.SetBinding(Entry.IsVisibleProperty, "IsToggled");
tin.BindingContext = SwitchIsToggled;
tin.SetBinding(Entry.IsVisibleProperty, "IsToggled");
address.BindingContext = SwitchIsToggled;
address.SetBinding(Entry.IsVisibleProperty, "IsToggled");
}
xaml:
<StackLayout>
<Switch x:Name="SwitchIsToggled" HorizontalOptions="CenterAndExpand"
OnColor="Orange"
IsToggled="False"
ThumbColor="{StaticResource MainButtonColor}"
/>
<Entry Placeholder="email"></Entry>
<Entry Placeholder="password"></Entry>
<Entry Placeholder="confirm password"></Entry>
<Entry x:Name="cname" Placeholder="Company Name"></Entry>
<Entry x:Name="tin" Placeholder="TIN" ></Entry>
<Entry x:Name="address" Placeholder="Address" ></Entry>
</StackLayout>
I've got following view:
<ContentPage.Content>
<AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
<StackLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1">
<StackLayout VerticalOptions="CenterAndExpand" Margin="20, 0, 20, 0">
<StackLayout>
<Label Text="Login" HorizontalOptions="CenterAndExpand" />
<Entry Text="{Binding Login, Mode=OneWayToSource}"></Entry>
</StackLayout>
<StackLayout>
<Label Text="Hasło" HorizontalOptions="CenterAndExpand" />
<Entry IsPassword="True" Text="{Binding Password, Mode=TwoWay}"></Entry>
</StackLayout>
<Button Text="Zaloguj się" Command="{Binding SignInCommand}"></Button>
</StackLayout>
</StackLayout>
<BoxView AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1" BackgroundColor="Gray" Opacity="0.5" InputTransparent="false" IsVisible="{Binding IsBusy}" />
<ActivityIndicator IsRunning="{Binding IsBusy}" AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1" VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" />
</AbsoluteLayout>
</ContentPage.Content>
I want to move BoxView and ActivityIndicator to external control to make reusable component. The problem is that to achieve this I need to "group" these controls to obtain one child element.
The question is if is there any element I can use to group my controls, but which will not affect the way how controls are displayed? Alternatevlly which element I can use and how to still have effect of overlay over whole page and loading indicator?
I was trying to use AbsoluteLayout, StackLayout etc. but I couldn't position it to persist initial effect (overlay on whole page and loading indicator in the center with opacity = 1).
You can use DependencyService and reuse it in any page.
Check this tutorial.
There is also a working sample.
I have 2 different values for the Left Margin and the Right Margin for my application. (Let say 27 and 64)
Is it possible to get these values from the ResourcesDictionaty and set them in my XAML.
For example
<StackLayout Margin="{Left1},0,{Right1},0">
</StackLayout>
You can specify them as double values in ResourceDictionary and refer to them in other resources/styles or at element level itself.
Sample resource-dictionary
<ResourceDictionary>
<!-- namespace declaration
xmlns:sys="clr-namespace:System;assembly=System.Runtime" -->
<sys:Double x:Key="leftMargin">20</sys:Double>
<sys:Double x:Key="rightMargin">30</sys:Double>
<Thickness x:Key="MyMargin1"
Left="{StaticResource leftMargin}"
Right="{StaticResource rightMargin}"
Top="20"
Bottom="30" />
<Thickness x:Key="MyMargin2"
Left="{StaticResource leftMargin}"
Right="{StaticResource rightMargin}"
Top="5"
Bottom="15" />
</ResourceDictionary>
Sample usage
<StackLayout>
<BoxView BackgroundColor="Blue" Margin="{StaticResource MyMargin1}" />
<BoxView HeightRequest="1" BackgroundColor="Black" />
<BoxView BackgroundColor="Red" Margin="{StaticResource MyMargin2}" />
<BoxView HeightRequest="1" BackgroundColor="Black" />
<BoxView BackgroundColor="Yellow">
<BoxView.Margin>
<Thickness
Left="{StaticResource leftMargin}"
Right="{StaticResource rightMargin}"
Top="30"
Bottom="10" />
</BoxView.Margin>
</BoxView>
<BoxView HeightRequest="1" BackgroundColor="Black" />
</StackLayout>
I don't think doing this for individual values will work. You can however bind the entire Margin to a value in a resource dictionary as follows:
<ResourceDictionary>
<Thickness x:Key="MyMargin">27,0,64,0</Thickness>
</ResourceDictionary>
<StackLayout Margin="{StaticResource MyMargin}">
</StackLayout>