MAUI component for skills using FlexLayout

MAUI component for skills using FlexLayout

In this new post, I will create a new MAUI component for skills using FlexLayout. 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!

Related posts

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.