In this new post, I will create a new MAUI component for skills using FlexLa
yout. For that, I am using the code from my previous post How to use FlexLayout with different sizes.
The full source code is available on GitHub.
What I want to do
In my previous post How to use FlexLayout with different sizes, I created a MAUI application. It displays text in pills. It also includes a button to remove the values. For that, I use a FlexLayout
to display the pills. So, it is possible to have a long list of words on multiple lines.
Now, I want to create a reusable component from this code. Here it is the list of expectations:
- gets a reusable component for displaying text in pills
- adds or removes items
- returns the list of
string
Implementation
Create EntryChoices.xaml
First, I’m going to create a new folder called Components where to add all the components for a project. That I create a new ComponentView called EntryChoices.xaml
. In the XAML, I am going to add the VerticalStackLayout
what was in the MainPage.xaml
.
<VerticalStackLayout
Padding="20,20"
Spacing="15"
VerticalOptions="Start">
<Label
Margin="5,0,0,0"
FontAttributes="Bold"
FontSize="20"
Text="Skills : " />
<FlexLayout
x:Name="FlexSkillContainer"
Margin="5,-10,5,5"
AlignContent="Start"
AlignItems="Start"
BindableLayout.ItemsSource="{Binding Skills}"
Direction="Row"
HorizontalOptions="FillAndExpand"
JustifyContent="Start"
VerticalOptions="Fill"
Wrap="Wrap">
<BindableLayout.ItemTemplate>
<DataTemplate>
<Frame
Margin="2"
Padding="4"
BackgroundColor="{StaticResource ColorFocused}"
CornerRadius="15">
<StackLayout Margin="5,0,5,0" Orientation="Horizontal">
<Label
x:Name="LabelSkill"
Margin="5,0,5,0"
FontSize="18"
Text="{Binding .}"
TextColor="{StaticResource ColorWhite}"
VerticalOptions="Center" />
<Image
x:Name="ImgCross"
HeightRequest="48"
HorizontalOptions="End"
Source="cross.png"
VerticalOptions="Center"
WidthRequest="48">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="OnDeleteSkillClicked" />
</Image.GestureRecognizers>
</Image>
</StackLayout>
</Frame>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
<FlexLayout Direction="Row" HeightRequest="50">
<Entry
x:Name="SkillEntry"
Margin="5"
FlexLayout.Basis="{Binding Source={x:Reference SkillEntry}, Path=Text, Converter={StaticResource ViewSizeConverter}}"
FontSize="20"
HeightRequest="50"
HorizontalOptions="FillAndExpand"
Keyboard="Text"
Placeholder="Add Skill"
VerticalOptions="Fill" />
<Image
x:Name="ImageCheck"
FlexLayout.AlignSelf="Center"
FlexLayout.Basis="10%"
HeightRequest="35"
IsVisible="{Binding Source={x:Reference SkillEntry}, Path=Text, Converter={StaticResource IsLableShow}}"
Source="check.png"
VerticalOptions="CenterAndExpand"
WidthRequest="35">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="OnAddSkillClicked" />
</Image.GestureRecognizers>
</Image>
</FlexLayout>
</VerticalStackLayout>
Add the Resources
After that, I have to add the converters to use in the XAML. So, I add this code
<ContentView.Resources>
<converter:StringToReverseBoolConverter x:Key="IsLableShow" />
<converter:StringToViewSizeStringConverter x:Key="ViewSizeConverter" />
</ContentView.Resources>
and then in the ContentView
, I need to add the reference to the converter
.
<ContentView
x:Class="MAUIFlexSkills.Components.EntryChoices"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converter="clr-namespace:MAUIFlexSkills.ValueConverters"
x:Name="entryChoices">
Now, the name of the ContentView
is required. This will help us in the binding reference in the rest of the code.
Add the Reference
Now, it is necessary to bind the XAML with code behind. For that, I have to add the Reference
to the VerticalStackLayout
like in the following code
<VerticalStackLayout
Padding="20,20"
BindingContext="{x:Reference entryChoices}"
Spacing="15"
VerticalOptions="Start">
Create the property
Now, the next step is to create the BindableProperty
for the list of strings. So, in the code behind in the file EntryChoices.xaml.cs
, I add the following code:
public static readonly BindableProperty ItemsProperty =
BindableProperty.Create(
propertyName: nameof(Items), // Property name
returnType: typeof(ObservableCollection<string>), // Property type
declaringType: typeof(EntryChoices), // Declaring type
defaultValue: new ObservableCollection<string>(), // Default value
propertyChanged: OnItemsChanged, // Optional: PropertyChanged callback
defaultBindingMode: BindingMode.TwoWay
);
This defines the ItemsProperty
that is an ObservableCollection<string>
. Also, I defined the properties Items
and OnItemsChanged
to handle the update of the values. The default binding mode is two ways.
Create the CLR property wrapper
Now, I have to add the wrapper for the Items
to handle the list of strings.
public ObservableCollection<string> Items
{
get => (ObservableCollection<string>)GetValue(ItemsProperty);
set => SetValue(ItemsProperty, value);
}
The ObservableCollection
gives us the support for updating the UI.
Handle property changes
So, the next step is to handle the property changes.
private static void OnItemsChanged(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is EntryChoices customView)
{
// Handle the property change logic here
customView.OnItemsUpdated((ObservableCollection<string>)newValue);
}
}
private void OnItemsUpdated(ObservableCollection<string> newValue)
{
// Example: Update UI or perform actions based on the new value
Console.WriteLine($"Items updated to: {newValue}");
}
Add and remove items
Now, the last thing to do is to handle the buttons to add or remove a text from the list. Here the code for it:
private void OnDeleteSkillClicked(object sender, TappedEventArgs e)
{
string skill = (sender as Image).BindingContext as string;
if (!string.IsNullOrEmpty(skill))
{
Items.Remove(skill);
}
}
private void OnAddSkillClicked(object sender, TappedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(ItemEntry.Text))
{
Items.Add(ItemEntry.Text);
ItemEntry.Text = "";
}
}
How to use this component
Now, in the MainPage.xaml
, I add the reference to the component like that
<ContentPage
x:Class="MAUIFlexSkills.MainPage"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:components="clr-namespace:MAUIFlexSkills.Components">
And then I can add the following code
<components:EntryChoices Items="{Binding Skills}" />
ViewModel
Now, the page uses a ViewModel defined like the following
public class MainPageViewModel
{
private ObservableCollection<string>? _skills;
public MainPageViewModel()
{
Skills = new ObservableCollection<string>() { "test1", "test2" };
}
public ObservableCollection<string>? Skills
{
get
{
return _skills;
}
set
{
_skills = value;
}
}
}
Wrap up
In conclusion, in this post, I created a MAUI component for displaying list of strings (skills) in pills using FlexLayout. Let me know your feedback and comment.
Happy coding!