[Xamarin Forms Series] – Xamarin Community Toolkit

Apa itu Xamarin Community Toolkit

Ketika bekerja dengan beberapa proyek Xamarin Forms, pengembang biasanya akan mencoba untuk menggunakan kembali elemen yang digunakan pada proyek sebelumnya. Elemen yang biasa digunakan kembali seperti value converter, custom view, dan behaviour. Untuk menyederhanakan kebutuhan tersebut Microsoft menawarkan library bernama Xamarin Community Toolkit.

Menggunakan Xamarin Community Toolkit

Anda dapat memperoleh Xamarin Community Toolkit melalui NuGet Package Manager. Berikut adalah cara untuk menambahkan Xamarin Conmmunity Toolkit menggunakan Visual Studio 2022.

Anda dapat mengecek file .csproj untuk memastikan bahwa Xamarin Community Toolkit sudah ditambahkan.

Anda juga dapat melihat dokumentasi dan contoh implementasi dari Xamarin Commonity Toolkit pada tautan berikut ini.

Pada bab ini akan dibahas beberapa contoh elemen dari Xamarin Community Toolkit yang sering digunakan dalam membuat aplikasi.

Bekerja dengan View

Xamarin Community Toolkit menyediakan beberapa view yang dapat melengkapi view bawaan dari Xamarin Forms. Berikut ini adalah beberapa view yang ada pada Xamarin Community Toolkit

  • AvatarView
  • BadgeView
  • CameraView
  • DockLayout
  • Expander
  • LazyView
  • MediaElement
  • Popup
  • RangeSlider
  • Shield
  • StateLayout
  • TabView
  • UniformGrid
  • Uniform Grid

Pada contoh dibawah ini dijelaskan beberapa layout baru yang didukung di Xamarin Community Toolkit. Untuk lebih lengkapnya anda dapat melihat dokumentasinya.

Menggunakan DockingLayout

Pada contoh berikut akan ditunjukan cara untuk menggunakan docking layout.

        <xct:DockLayout LastChildFill="False">
            <Button xct:DockLayout.Dock="Top" Text="Top" HeightRequest="50"/>
            <Button xct:DockLayout.Dock="Bottom" Text="Bottom" HeightRequest="50"/>
            <Button xct:DockLayout.Dock="Left" Text="Left" WidthRequest="60"/>
            <Button xct:DockLayout.Dock="Left" Text="Left" WidthRequest="60"/>
            <Button xct:DockLayout.Dock="Right" Text="Right" WidthRequest="80"/>
            <Button xct:DockLayout.Dock="Right" Text="Right" WidthRequest="80"/>
        </xct:DockLayout>

Hasilnya dapat dilihat sebagai berikut:

Menggunakan UniformGrid

UniformGrid dapat digunakan untuk mengatur layout dalam bentuk Grid yang ukuran kolom dan barisnya sama persis.

        <xct:UniformGrid>
            <BoxView Color="Red" />
            <BoxView Color="Yellow" />
            <BoxView Color="Orange" />
            <BoxView Color="Purple" />
            <BoxView Color="Blue" />
            <BoxView Color="Green" />
            <BoxView Color="LightGreen" />
            <BoxView Color="Gray" />
            <BoxView Color="Pink" />
        </xct:UniformGrid>

Hasilnya dapat dilihat sebagai berikut:

Menggunakan Camera View

CameraView kontrol memungkinkan pengguna untuk menampilkan preview  keluaran kamera. Selain itu CameraView juga dapat mengambil foto atau merekam video. CameraView juga menawarkan opsi yang anda harapkan untuk mendukung pengambilan foto dan perekaman video seperti menyalakan atau mematikan flash, menyimpan media yang diambil ke file. Berikut adalah kontrol yang dapat digunakan.

Pada contoh dibawah ini kita dapat mengcapture gambar, dan merekam video melalui kamera.

        <ScrollView>
            <Grid RowDefinitions="300, Auto, *">
                <Grid ColumnDefinitions="*, *" Grid.Row="0">
                    <xct:CameraView
                    Grid.Column="0"
                    x:Name="cameraView"
                    CaptureMode="Photo"
                    FlashMode="Off"
                    HorizontalOptions="FillAndExpand"
                    MediaCaptured="CameraView_MediaCaptured"
                    OnAvailable="CameraView_OnAvailable"
                    VerticalOptions="FillAndExpand" />
                    <Label
                    Grid.Column="0"
                    Text="Camera"
                    HorizontalTextAlignment="Center"
                    HorizontalOptions="FillAndExpand"
                    VerticalOptions="End" />

                    <Image
                    Grid.Column="1"
                    x:Name="previewPicture"
                    Aspect="AspectFit"
                    BackgroundColor="LightGray" />

                    <xct:MediaElement
                    Grid.Column="1"
                    x:Name="previewVideo"
                    Aspect="AspectFit"
                    BackgroundColor="LightGray"
                    IsVisible="false"/>
                    <Label
                    Grid.Column="1"
                    Text="Result"
                    HorizontalTextAlignment="Center"
                    HorizontalOptions="FillAndExpand"
                    VerticalOptions="End" />
                </Grid>

                <StackLayout Grid.Row="1" Orientation="Horizontal">
                    <Label x:Name="zoomLabel" />
                    <Slider
                    x:Name="zoomSlider"
                    Margin="5,0"
                    IsEnabled="False"
                    Maximum="10"
                    Minimum="1"
                    HorizontalOptions="FillAndExpand"
                    ValueChanged="ZoomSlider_ValueChanged"
                    Value="1" />
                </StackLayout>

                <StackLayout Grid.Row="2">
                    <Grid ColumnDefinitions="*, *" RowDefinitions="*,*">
                        <StackLayout
                        Grid.Row="0"
                        Grid.Column="0"
                        Margin="5"
                        Orientation="Horizontal">
                            <Switch
                            Margin="0,0,5,0"
                            IsToggled="False"
                            Toggled="VideoSwitch_Toggled" />
                            <Label Text="Video" />
                        </StackLayout>
                        <StackLayout
                        Grid.Row="1"
                        Grid.Column="0"
                        Margin="5"
                        Orientation="Horizontal">
                            <Switch
                            Margin="0,0,5,0"
                            IsToggled="False"
                            Toggled="FrontCameraSwitch_Toggled" />
                            <Label Text="Front camera" />
                        </StackLayout>
                        <StackLayout
                        Grid.Row="0"
                        Grid.Column="1"
                        Margin="5"
                        Orientation="Horizontal">
                            <Switch
                            Margin="0,0,5,0"
                            IsToggled="False"
                            Toggled="FlashSwitch_Toggled" />
                            <Label Text="Flash" />
                        </StackLayout>
                    </Grid>

                    <Button
                    x:Name="doCameraThings"
                    Clicked="DoCameraThings_Clicked"
                    IsEnabled="False"
                    Text="Snap picture" />

                </StackLayout>
            </Grid>
        </ScrollView>

Tambahkan juga kode c# berikut:

public partial class SampleCameraView : ContentPage
    {
        public SampleCameraView()
        {
            InitializeComponent();
			zoomLabel.Text = string.Format("Zoom: {0}", zoomSlider.Value);

		}

	void ZoomSlider_ValueChanged(object? sender, ValueChangedEventArgs e)
		{
			cameraView.Zoom = (float)zoomSlider.Value;
			zoomLabel.Text = string.Format("Zoom: {0}", Math.Round(zoomSlider.Value));
		}

		void VideoSwitch_Toggled(object? sender, ToggledEventArgs e)
		{
			var captureVideo = e.Value;

			if (captureVideo)
				cameraView.CaptureMode = CameraCaptureMode.Video;
			else
				cameraView.CaptureMode = CameraCaptureMode.Photo;

			previewPicture.IsVisible = !captureVideo;

			doCameraThings.Text = e.Value ? "Start Recording"
				: "Snap Picture";
		}

		
		void FrontCameraSwitch_Toggled(object? sender, ToggledEventArgs e)
			=> cameraView.CameraOptions = e.Value ? CameraOptions.Front : CameraOptions.Back;

		
		void FlashSwitch_Toggled(object? sender, ToggledEventArgs e)
			=> cameraView.FlashMode = e.Value ? CameraFlashMode.On : CameraFlashMode.Off;

		void DoCameraThings_Clicked(object? sender, EventArgs e)
		{
			cameraView.Shutter();
			doCameraThings.Text = cameraView.CaptureMode == CameraCaptureMode.Video
				? "Stop Recording"
				: "Snap Picture";
		}

		void CameraView_OnAvailable(object? sender, bool e)
		{
			if (e)
			{
				zoomSlider.Value = cameraView.Zoom;
				var max = cameraView.MaxZoom;
				if (max > zoomSlider.Minimum && max > zoomSlider.Value)
					zoomSlider.Maximum = max;
				else
					zoomSlider.Maximum = zoomSlider.Minimum + 1; // if max == min throws exception
			}

			doCameraThings.IsEnabled = e;
			zoomSlider.IsEnabled = e;
		}

	void CameraView_MediaCaptured(object? sender, MediaCapturedEventArgs e)
		{
			switch (cameraView.CaptureMode)
			{
				default:
				case CameraCaptureMode.Default:
				case CameraCaptureMode.Photo:
					previewVideo.IsVisible = false;
					previewPicture.IsVisible = true;
					previewPicture.Rotation = e.Rotation;
					previewPicture.Source = e.Image;
					doCameraThings.Text = "Snap Picture";
					break;
				case CameraCaptureMode.Video:
					previewPicture.IsVisible = false;
					previewVideo.IsVisible = true;
					previewVideo.Source = e.Video;
				doCameraThings.Text = "Start Recording";
					break;
			}
		}
	} 

Hasil dari program diatas dapat dilihat pada gambar dibawah ini.

Komponen Reusable Converter

Pada Xamarin.Forms terutama pada platform UI berbasis XAML, terjadi pengikatan data antara properti sumber dan properti target. Jika keduanya bertipe sama, data-binding dapat dilakukan. Jika properti memiliki tipe yang berbeda, misalnya saat perlu mengikat/binding Image.Source properti ke gambar yang diwakili oleh string Base64, mekanisme pengikatan data memerlukan konverter nilai yang sesuai yang mengubah string Base64 menjadi tipe ImageSource. Solusinya adalah menggunakan konverter nilai, yang merupakan objek yang mengimplementasikan IvalueConverter.

Adapun Reusable Converter yang dapat digunakan pada Xamarin Community Toolkit adalah sebagai berikut:

  • BoolToObjectConverter
  • ByteArrayToImageSourceConverter
  • CompareConverter
  • DateTimeOffsetConverter
  • DoubleToIntConverter
  • EnumToBoolConverter
  • EnumToIntConverter
  • EqualConverter
  • IndexToArrayItemConverter
  • IntToBoolConverter
  • InvertedBoolConverter
  • IsInRangeConverter
  • IsNotNullOrEmptyConverter
  • IsNullOrEmptyConverter
  • ItemSelectedEventArgsConverter
  • ItemTappedEventArgsConverter
  • ListIsNotNullOrEmptyConverter
  • ListIsNullOrEmptyConverter
  • ListToStringConverter
  • MathExpressionConverter
  • MultiConverter
  • NotEqualConverter
  • StringToListConverter
  • TextCaseConverter
  • VariableMultiValueConverter

Validasi Data dengan Reusable Behaviour

Pada Xamarin Forms, kita dapat menambahkan behaviour yang bertujuan untuk menambahkan fungsionalitas ke tampilan antarmuka pengguna tanpa harus membuat subclass-nya (turunan dari class).  Beberapa behaviour yang ada pada Xamarin Community Toolkit adalah sebagai berikut:

  • AnimationBehavior
  • CharactersValidationBehavior
  • EmailValidationBehavior
  • EventToCommandBehavior
  • ImpliedOrderGridBehavior
  • MaskedBehavior
  • MaxLengthReachedBehavior
  • MultiValidationBehavior
  • NumericValidationBehavior
  • RequiredStringValidationBehavior
  • SetFocusOnEntryCompletedBehavior
  • TextValidationBehavior
  • UriValidationBehavior
  • UserStoppedTypingBehavior
  • ValidationBehavior

EventToCommand Behaviour Pada ListView

Contoh berikut menunjukan bagaimana cara penggunaan EventToCommand behaviour yang biasa dilakukan untuk membinding event ItemTapped dari ListView ke command.

Pada contoh di artikel sebelumnya yang membahas tentang MVVM kita sudah melihat bagaimana cara untuk membinding ViewModel kedalam ListView. Namun ada satu hal yang belum kita lakukan yaitu memambahkan event hadler ItemTapped yang dibinding kedalam command yang ada di ViewModel. Untuk tujuan tersebut kita perlu menggunakan EventToCommand behaviour.

Hal yang pertama kita perlu menambahkan resources converter yang akan digunakan untuk mengkonversi  event ke command.

    <ContentPage.Resources>
        <ResourceDictionary>
            <xct:ItemSelectedEventArgsConverter x:Key="ItemSelectedEventArgsConverter" />
        </ResourceDictionary>
    </ContentPage.Resources>

Kemudian pada kontrol ListView kita dapat menambahkan EventToCommand behaviour.

            <ListView.Behaviors>
                <xct:EventToCommandBehavior EventName="ItemSelected"
                    Command="{Binding SelectedCommand}" 
                    EventArgsConverter="{StaticResource ItemSelectedEventArgsConverter}"/>
            </ListView.Behaviors>

Dengan menggunakan kode diatas maka kita dapat mengkonversi event ItemSelected pada ListView menjadi command SelectedCommand yang ada pada ViewModel.

SelectedCommand = new AsyncCommand<object>(SelectedCoffee);
        private async Task SelectedCoffee(object arg)
        {
            var coffee = arg as Coffee;
            if (coffee == null) return;
            await Application.Current.MainPage.DisplayAlert("Selected", coffee.Name, "OK");
        }

Behaviour yang sudah disediakan oleh Xamarin Community Toolkit Ini akan sangat membantu kita dalam proses development menggunakan pattern MVVM.

Advertisement

Mentoring Session: Program Studi Independen Kampus Merdeka – Cloud Fundamental

Program Studi Independen adalah bagian dari program Kampus Merdeka yang bertujuan untuk memberikan kesempatan kepada mahasiswa untuk belajar dan mengembangkan melalui aktifitas di luar kelas perkuliahan. Program ini diperuntukan bagi mahasiswa yang ingin memperlengkapi dirinya dengan menguasai kompetensi spesifik dan praktis yang juga dicari oleh dunia usaha.

Program Kampus Merdeka sendiri adalah program resmi dari Kemendikbud Indonesia untuk meningkatkan kempuan skill praktis yang sesuai dengan kebutuhan industri melalui kerja sama langsung dengan industri.

Microsoft sebagai salah satu vendor terkemuka didunia yang mempunyai berbagai jenis produk dan layanan juga bekerja sama dengan kemendkbud untuk melaksanakan program ini. Pada batch pertama Microsoft Indonesia menawarkan tiga kelas yang dapat diikuti yaitu:

  • Cloud Fundamental
  • Data and Artificial Intelligence
  • Microsoft Productivity – The Modern Workspace

Saya sendiri berkesempatan untuk menjadi mentor pada salah satu program tersebut yaitu Cloud Fundamental. Program ini bertujuan untuk memberikan wawasan tentang produk cloud dari Microsoft yaitu layanan Azure. Program ini dilakukan selama empat bulan dan setiap bulan peserta difasilitasi untuk mengambil fundamental certification. Tautan dari program tersebut dapat dilihat pada link berikut ini.

Program tersebut dilangsungkan selama empat bulan dari Agustus 2021 sampai dengan Desember 2021. Peserta dari program ini total ada kurang lebih 1000 orang. Untuk kelas saya sendiri ada sekitar 55 peserta yang terdaftar. Untuk sesi mentoring yang dilakukan adalah 6 kali sesi sinkron (tatap muka) setiap bulannya, dan kelas asinkron (juga 6 kali untuk satu bulan). Jadi total waktu mentoring total 24 kali pertemuan dalam 4 bulan program berlangsung.

Diakhir program saya berkesempatan untuk mendapatkan penghargaan sebagai best mentor untuk track Cloud Fundamental.

Mentoring Session: Indonesia Belajar Microsoft Cloud

Program Indonesia Belajar Microsoft Cloud (https://belajarmscloud.com/) adalah program inisiasi dari Microsoft Indonesia. Tujuan dari program ini adalah agar mahasiswa dan pengembang mampu mengembangkan keahlian digital yang dibutuhkan untuk kesiapan karir di masa depan. Untuk itu Microsoft MVP dan Fokus Target menggagas Microsoft Campus, sebuah wadah untuk memperoleh keahlian digital melalui Indonesia Belajar Microsoft Cloud.

Dengan inisiatif ini, Pengembang dapat mempelajari berbagai keahlian baru yang dapat digunakan untuk tumbuh dan sukses di dunia digital di online learning platform, Microsoft Learn. untuk mempelajari keahlian untuk menjadi pengembang aplikasi, cloud hingga menjadi solutions architect.

Salah satu program dari Indonesia Belajar Microsoft Cloud adalah mentoring dan sertifikasi. Pada program ini saya berkesempatan untuk berperan sebagai mentor yang memberikan bimbingan dan berbagi pengalaman dalam mengambil sertifikasi di bidang Microsoft Azure. Bimbingan mentor tersebut dilaksanakan pada tanggal 25 November 2021 melalui Microsoft Teams. Program mentoring ini dilaksanakan selama kurang lebih 2 bulan, dari bulan November 2021.

[Xamarin Forms Series] – Menggunakan Library MVVM Helper – Part 2

Tutorial berikut adalah lanjutan dari tutorial MVVM dengan Xamarin Forms 5 yang sudah ditulis sebelumnya.

Untuk mempermudah penggunaan MVVM di Xamarin, anda dapat menggunakan library MVVMHelper. Library ini mendukung beberapa fitur yang sangat membantu kita dalam mengimplementasikan pola MVVM. Fitur-fitur tersebut diantarannya ViewModelBase, AsyncCommand dan ObservableRangeCollection.

Untuk menambahkan library MVVMHelper pada solution, tambahkan package manager berikut ini. Klik kanan pada solution, kemudian cari library MVVMHelpers

Setelah itu buat class dengan nama ViewModelBase yang diturunkan dari class BaseViewModel yang ada pada MVVMHelper.

    public class ViewModelBase : BaseViewModel
    {
    }

Kemudian buat class dengan nama CoffeeEquipmentViewModel.cs pada folder ViewModel. Disini kita akan menambahkan o ObservableRange yang akan digunakan sebagai sumber data pada ListView.

namespace MyXamarinApps.ViewModels
{
    public class CoffeeEquipmentViewModel : ViewModelBase
    {
        public ObservableRangeCollection<string> Coffee { get; set; }

        public ICommand IncreaseCount { get; }

        public ICommand CallServerCommand { get;}

        public CoffeeEquipmentViewModel()
        {
            IncreaseCount = new Command(OnIncrease);
            CallServerCommand = new AsyncCommand(CallServer);
            Coffee = new ObservableRangeCollection<string>();
            Title = "Coffee Equipment";
        }

        async Task CallServer()
        {
            var items = new List<string> { "Luwak Coffee", "Aceh Gayo", "Toraja Coffee" };
            Coffee.AddRange(items);
        }

        int count = 0;
        private string countDisplay = "Click Me";
        public string CountDisplay
        {
            get => countDisplay;
            set => SetProperty(ref countDisplay, value);
        }

        private void OnIncrease()
        {
            count++;
            CountDisplay = $"You clicked {count} time";
        }
    }
}

Tambahkan class Coffee di folder Model.

    public class Coffee
    {
        public string Roaster { get; set; }
        public string Name { get; set; }
        public string Image { get; set; }
    }

Kemudian modifikasi class CoffeeEquipmentViewModel

namespace MyXamarinApps.ViewModels
{
    public class CoffeeEquipmentViewModel:ViewModelBase
    {
        public ObservableRangeCollection<Coffee> Coffee { get; set; }
        public ObservableRangeCollection<Grouping<string,Coffee>> CoffeeGroup { get; set; }
        public AsyncCommand RefreshCommand { get; }
        public AsyncCommand<Coffee> FavoriteCommand { get; set; }
        public AsyncCommand<object> SelectedCommand { get; set; }

        public CoffeeEquipmentViewModel()
        {
            Title = "Coffee Equipment";
            Coffee = new ObservableRangeCollection<Coffee>();
            CoffeeGroup = new ObservableRangeCollection<Grouping<string, Coffee>>();
            var image = "luwak.png";

            Coffee.Add(new Coffee { Roaster = "Otten Coffee", Name = "Italian Roasted", Image = image });
            Coffee.Add(new Coffee { Roaster = "Blue Bottle", Name = "Aceh Gayo", Image = image });
            Coffee.Add(new Coffee { Roaster = "Blue Bottle", Name = "Darked Roasted", Image = image });
            Coffee.Add(new Coffee { Roaster = "Otten Coffee", Name = "Kenya Kiambu", Image = image });
            Coffee.Add(new Coffee { Roaster = "Yes Plz", Name = "Pike Market", Image = image });
            Coffee.Add(new Coffee { Roaster = "Otten Coffee", Name = "Toraja", Image = image });

            CoffeeGroup.Add(new Grouping<string, Coffee>("Otten Coffee",Coffee.Where(c=>c.Roaster== "Otten Coffee")));
            CoffeeGroup.Add(new Grouping<string, Coffee>("Blue Bottle", Coffee.Where(c => c.Roaster == "Blue Bottle")));
            
            RefreshCommand = new AsyncCommand(Refresh);
            FavoriteCommand = new AsyncCommand<Coffee>(Favorite);
            SelectedCommand = new AsyncCommand<object>(SelectedCoffee);
        }

        private async Task SelectedCoffee(object arg)
        {
            var coffee = arg as Coffee;
            if (coffee == null) return;
            await Application.Current.MainPage.DisplayAlert("Selected", coffee.Name, "OK");
        }

        private async Task Favorite(Coffee coffee)
        {
            if (coffee == null)
                return;
            await Application.Current.MainPage.DisplayAlert("Favorite", $"{coffee.Roaster} - {coffee.Name}", "OK");
        }

        private async Task Refresh()
        {
            IsBusy = true;
            await Task.Delay(2000);
            IsBusy = false;
        }
    }
}

Pada XAML tambahkan kode berikut untuk membinding objek ViewModel yang sudah kita buat sebelumnya.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:viewmodels="clr-namespace:SampleMVVM.ViewModels"
             x:Class="MyXamarinApps.ViewModels">

    <ContentPage.BindingContext>
        <viewmodels:CoffeeEquipmentViewModel />
    </ContentPage.BindingContext>

    <ListView BackgroundColor="Transparent" 
              ItemsSource="{Binding Coffee}"
              HasUnevenRows="True"
              SeparatorVisibility="None">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <ViewCell.ContextActions>
                        <MenuItem Text="Favorite" />
                        <MenuItem IsDestructive="True" Text="Delete" />
                    </ViewCell.ContextActions>
                    <Grid Padding="10">
                        <Frame CornerRadius="20" HasShadow="True">
                            <StackLayout Orientation="Horizontal">
                                <Image Source="{Binding Image}" WidthRequest="66" />
                                <StackLayout VerticalOptions="Center">
                                    <Label
                                        FontSize="Large"
                                        Text="{Binding Name}"
                                        VerticalOptions="Center" />
                                    <Label
                                        FontSize="Large"
                                        Text="{Binding Roaster}"
                                        VerticalOptions="Center" />
                                </StackLayout>
                            </StackLayout>
                        </Frame>
                    </Grid>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
        <ListView.Header>
            <StackLayout Orientation="Horizontal">
                <Label HorizontalOptions="Center" Text="Coffee of the World" />
            </StackLayout>
        </ListView.Header>
        <ListView.Footer>
            <StackLayout HorizontalOptions="Center" Orientation="Horizontal">
                <Button Text="Load More" />
            </StackLayout>
        </ListView.Footer>
    </ListView>
</ContentPage>

Jalankan aplikasinya untuk melihat tampilan List yang mengambil data contoh dari ViewModel yang sudah dibuat sebelumnya. Dari contoh ini dapat dilihat bahwa dengan menggunakan MVVMHelper kita dapat menggunakan objek ObservableRange yang fleksible untuk digunakan sebagai tempat menampung objek bertipe List. Selain itu MVVMHelper juga mendukung AsyncCommand untuk membuat command yang mendukung async method.