FmgLib.MauiMarkup

Son Güncellenme: 09 Eyl 2024

FmgLib.MauiMarkup

FmgLib.MauiMakrup Tarafından Sunulan Ücretsiz .NET Kütüphaneleri

NuGet Paket Link Repo Bilgi
FmgLib.MauiMarkup NuGet NuGet Downloads GitHub Stars GitHub Forks GitHub last-commit
FmgLib.MauiMarkup.Template NuGet NuGet Downloads -

FmgLib.MauiMarkup

FmgLib.MauiMarkup, .NET MAUI için özel olarak tasarlanmış bir kütüphanedir. Bu kütüphane, XAML dilini kullanma zorunluluğu olmaksızın doğrudan C# kodu ile kodlama yapmanızı sağlar. Geliştiricilere, C# kodu kullanarak kullanıcı arayüzleri oluşturma konusunda basit ve esnek bir yaklaşım sunar. FmgLib.MauiMarkup ile artık uygulama arayüzlerini kod odaklı bir şekilde geliştirebilir, XAML dosyalarıyla ilgili karmaşıklıklarla uğraşmanın önüne geçebilirsiniz. Bu kütüphane, geliştirme sürecinizi hızlandırırken daha okunabilir ve yönetilebilir kod yazmanızı sağlar.

FmgLib.MauiMarkup, XAML tarafında bir Görünüm için sağlanan tüm özellikler için uzantı metotları sunar.

Başlarken

CLI üzerinden yeni bir FmgLib.MauiMarkup projesi oluşturma

FmgLib, FmgLib.MauiMarkup ile yeni bir proje başlatmak için bir proje şablonu sağlar.

NuGet'ten en son şablonları yükleyin:

dotnet new install FmgLib.MauiMarkup.Template

Yeni proje oluştur:

dotnet new fmglib-mauimarkup-app -o my-new-project

Var olan Projede

MAUI uygulamanıza FmgLib.MauiMarkup NuGet paketini yüklemek için:

dotnet add package FmgLib.MauiMarkup

XAML'den FmgLib.MauiMarkup'a(C#)

Eğer Image sınıfı için XAML kodu yazacak olsaydık, aşağıdaki gibi görünürdü:

<Image
    Source="dotnet_bot.png"
    HeightRequest="100"
    WidthRequest="150"
    Grid.Row="0"
    Grid.Column="1"
    Grid.RowSpan="2"
    Opacity=".8" />

FmgLib.MauiMarkup yardımı ile C# karşılığı şu şekilde olacaktır:

new Image()
.Source("dotnet_bot.png")
.Row(0)
.Column(1)
.RowSpan(2)
.SizeRequest(150,100)
.Opacity(.8)

Benzer şekilde, diğer Görünümler için de bunu görebiliriz. Örnek olarak birkaç kod yazalım:

new Label()
.Text("fmglib.mauimarkup")
.FontSize(12)
.Row(1)
.TextColor(Colors.Green)
.FontAttributes(FontAttributes.Bold)
.Margin(new Thickness(5,3,0,5))
this
.BackgroundImageSource("background.jpg")
.Content(
    new StackLayout()
    .Center()
    .Children(
        new ActivityIndicator()
        .IsRunning(true)
        .HeightRequest(70)
        .WidthRequest(70)
        .Center()
        .InvokeOnElement(ai => ai.Loaded += CheckLogin(sender, e))
    )
);

Nesne referanslarını atama

FmgLib.MauiMarkup içinde nesne atamanın iki ana yolu vardır:

  • Assign metodunu kullanarak,

İlk örnek, Assign metodunu kullanarak bir etiket nesnesini label adında bir değişkene atamaktadır. Bu, aşağıdaki kod ile yapılır:

new Label().Assign(out var label);
new Entry().Assign(out var entry);

Veya

Button btnOk;

new Button()
.Assign(out btnOk);

Attached Özellikler

Bağlı özellikler, bir tür üzerinde tanımlanan ancak diğer türlerin örnekleriyle kullanılması amaçlanan özelliklerdir. FmgLib.MauiMarkup içinde, bağlı özellikler bağlı özellik akıcı metodlarıyla eşleştirilir, böylece değerlerini daha okunabilir ve akıcı bir şekilde ayarlamanıza olanak tanır.

Örneğin, bir Border nesnesi üzerinde AbsoluteLayout.LayoutBounds bağlı özelliğini ayarlamak istiyorsanız, bir Border örneği oluşturur ve değerini ayarlamak için AbsoluteLayoutBounds akıcı metodunu kullanırsınız, şu şekilde:

new Border().AbsoluteLayoutBounds(new Rect(100, 100, 200, 200));

Bu, belirtilen dikdörtgen değerine Border nesnesi üzerinde AbsoluteLayout.LayoutBounds bağlı özelliğini ayarlar.

Bağlı özellikler listesi

Maui bağlı özelliği FmgLib.MauiMarkup metodu
FlyoutBase.ContextFlyout ContextFlyout()
Grid.Column Column()
Grid.Row Row()
Grid.ColumnSpan ColumnSpan()
Grid.RowSpan RowSpan()
Grid.ColumnSpan+Grid.RowSpan Span(column, row)
VisualStateManager.VisualStateGroups VisualStateGroups()
RadioButtonGroup.GroupName RadioButtonGroupGroupName()
RadioButtonGroup.SelectedValue RadioButtonGroupSelectedValue()
AbsoluteLayout.LayoutFlags AbsoluteLayoutFlags()
AbsoluteLayout.LayoutBounds AbsoluteLayoutBounds()
BindableLayout.EmptyView BindableLayoutEmptyView()
BindableLayout.EmptyViewTemplate BindableLayoutEmptyViewTemplate()
BindableLayout.ItemsSource BindableLayoutItemsSource()
BindableLayout.ItemTemplate BindableLayoutItemTemplate()
BindableLayout.TemplateSelector BindableItemTemplateSelector()
Shell.PresentationMode ShellPresentationMode()
Shell.BackgroundColor ShellBackgroundColor()
Shell.ForegroundColor ShellForegroundColor()
Shell.TitleColor ShellTitleColor()
Shell.DisabledColor ShellDisabledColor()
Shell.UnselectedColor ShellUnselectedColor()
Shell.NavBarHasShadow ShellNavBarHasShadow()
Shell.NavBarIsVisible ShellNavBarIsVisible()
Shell.TitleView ShellTitleView()
Shell.TabBarBackgroundColor ShellTabBarBackgroundColor()
Shell.TabBarForegroundColor ShellTabBarForegroundColor()
Shell.TabBarTitleColor ShellTabBarTitleColor()
Shell.TabBarDisabledColor ShellTabBarDisabledColor()
Shell.TabBarUnselectedColor ShellTabBarUnselectedColor()
Shell.TabBarIsVisible ShellTabBarIsVisible()
Shell.FlyoutBackdrop ShellFlyoutBackdrop()
Shell.FlyoutBehavior ShellFlyoutBehavior()
Shell.FlyoutHeight ShellFlyoutHeight()
Shell.FlyoutWidth ShellFlyoutWidth()
Shell.FlyoutItemIsVisible ShellFlyoutItemIsVisible()
Shell.BackButtonBehavior ShellBackButtonBehavior()
Shell.ItemTemplate ShellItemTemplate()
Shell.MenuItemTemplate ShellMenuItemTemplate()
Shell.SearchHandler ShellSearchHandler()
NavigationPage.HasNavigationBar NavigationPageHasNavigationBar()
NavigationPage.BackButtonTitle NavigationPageBackButtonTitle()
NavigationPage.HasBackButton NavigationPageHasBackButton()
NavigationPage.IconColor NavigationPageIconColor()
NavigationPage.TitleIconImageSource NavigationPageTitleIconImageSource()
NavigationPage.TitleView NavigationPageTitleView()
SemanticProperties.Hint SemanticHint()
SemanticProperties.Description SemanticDescription()
SemanticProperties.HeadingLevel SemanticHeadingLevel()
AutomationProperties.ExcludedWithChildren AutomationExcludedWithChildren()
AutomationProperties.IsInAccessibleTree AutomationIsInAccessibleTree()
AutomationProperties.Name AutomationName()
AutomationProperties.HelpText AutomationHelpText()
AutomationProperties.LabeledBy AutomationLabeledBy()
ToolTipProperties.Text ToolTipPropertiesText()

Behaviors

FmgLib.MauiMarkup içinde, kullanıcı arayüzü kontrollerine davranışlar ekleyerek işlevsellik kazandırabilirsiniz. Davranışlar, kontrolleri alt sınıfa ayırmadan onlara işlevsellik eklemenizi sağlar.

Bir kontrolün üzerine bir davranış eklemek için Behaviors metodunu kullanabilir ve davranış sınıfının bir örneğini geçirebilirsiniz. Örneğin:

new Entry().Text("Click Item")
  .Behaviors(new YourCustomBehaviors());

Binding Converters

Bu kod, FmgLib.MauiMarkup içinde BindingConverters'ları nasıl kullanılacağına dair bir örnektir.

Bir CollectionView tanımlanmış ve MyNumbers listesindeki her bir öğe için, öğenin değerine eşit metinle bir etiket oluşturulmuştur. Etiketin BackgroundColor özelliği, öğeye bağlı olarak Convert metodunu kullanarak bağlanmıştır, bu metod bir fonksiyon alır ve öğenin değerini (bir tamsayı) bir renge dönüştürür. Bu durumda, fonksiyon numaranın çift mi yoksa tek mi olduğunu kontrol eder ve sonuca göre ya Colors.Green veya Colors.Yellow döndürür.


public class CustomPage : ContentPage
{
   public List<int> MyNumbers = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

   public CustomPage()
   {
       this
       .Content(
           new VerticalStackLayout()

           Children(
               new CollectionView()
               .ItemsSource(MyNumbers)
               .ItemTemplate(() =>
                   new Label()
                       .FontSize(30)
                       .Text(e => e.Path("."))
                       .TextColor(Colors.Gray)
                       .BackgroundColor(e => e
                           .Path(".")
                           .Convert((int n) => n % 2 == 0 ? Colors.Green : Colors.Yellow)
                       )
               )
           )
       );
   }
}

Event handlers

Maui'de, kullanıcı arayüzü kontrollerine olayları işleyerek işlevsellik ekleyebilirsiniz. FmgLib.MauiMarkup sınıfındaki her EventHandler için, bir kontrolde olay işleyicisini eklemeyi kolaylaştıran akıcı bir yardımcı metod üretilir.

Örneğin, Button sınıfındaki Clicked olay işleyicisi için iki akıcı metod üretilir:

  • OnClicked(Button sender)
  • OnClicked(object sender, EventArgs e)

İşte Button kontrolünde Clicked olayını işlemek için akıcı yardımcı metod OnClicked kullanma örneği:

using FmgLib.MauiMarkup;

public class ExamPage : ContentPage
{
   int count = 0;
   public ExamPage()
   {
       this
       .Content(
           new VerticalStackLayout()
           .Children(
               ...

               new Button()
               .Text("Click me")
               .OnClicked(OnCounterClicked),
               ...
            )
       );
   }

   private void OnCounterClicked(Button sender)
   {
       count++;
       sender.Text = $"Clicked {count} ";
       sender.Text += count == 1 ? "time" : "times";
   }
}

Veya, olayı işlemek için bir satır içi fonksiyon kullanabilirsiniz:

new Button()
    .Text("Click me")
    .OnClicked(button =>
    {
        count++;
        button.Text = $"Clicked {count} ";
        button.Text += count == 1 ? "time" : "times";
    })

Bu, olay işleyicilerini kontrollere açık ve okunaklı bir şekilde eklemeyi kolaylaştırır.

Gesture Recognizers

Aşağıdaki hareket tanıyıcıları mevcuttur:

  • TapGestureRecognizer
  • PanGestureRecognizer
  • PointerGestureRecognizer

Dokunma Hareket Tanıyıcısı

TapGestureRecognizer sınıfı, bir görünümde dokunma hareketlerini algılamak için kullanılır. NumberOfTapsRequired özelliği kullanılarak gerekli dokunma sayısı belirtilebilir.

İşte bir resimde çift dokunma hareketini algılamak için TapGestureRecognizer kullanımına bir örnek:

new StackLayout()
.Children(
    new Label()
    .Text("Tap 2 times on the image")
    .Assign(out var label),
    new Image()
    .Source("dotnet_bot.png")
    .Assign(out var image)
    .SizeRequest(100, 100)
    .GestureRecognizers(new GestureRecognizer[]
        {
            new TapGestureRecognizer()
                .NumberOfTapsRequired(2)
                .OnTapped((e, args) =>
                {
                    label.Text = "You tapped 2 times";
                })
        })
)

Sürükleme Hareket Tanıyıcısı

PanGestureRecognizer sınıfı, bir görünümde sürükleme hareketlerini algılamak için kullanılır. OnPanUpdated metodu ile sürükleme hareketi olayını işleyebilir ve görünümün konumunu güncelleyebilirsiniz.

İşte PanGestureRecognizer kullanarak ekranda bir resmi hareket ettirme örneği:

public class PanGesturePage : ContentPage
{
   double x, y;

   public PanGesturePage()
   {
       this
       .Content(
           new Grid()
           .Children(
               new Image()
               .Source("dotnet_bot.png")
               .Assign(out var image)
               .SizeRequest(100, 100)
               .GestureRecognizers(new GestureRecognizer[]
               {
                    new PanGestureRecognizer()
                        .OnPanUpdated((e, args) =>
                        {
                            switch (args.StatusType)
                            {
                                case GestureStatus.Running:
                                    image.TranslationX = x + args.TotalX;
                                    image.TranslationY = y + args.TotalY;
                                    break;

                                case GestureStatus.Completed:
                                    x = image.TranslationX;
                                    y = image.TranslationY;
                                    break;
                            }
                        })
               })
           )
       );
   }
}

İşaretçi Hareket Tanıyıcısı

PointerGestureRecognizer sınıfı, bir görünüm üzerinde giriş, çıkış ve hareket gibi işaretçi olaylarını algılamak için kullanılır. Bu olayları işlemek ve görünümü buna göre güncellemek için OnPointerEntered, OnPointerExited ve OnPointerMoved metodlarını kullanabilirsiniz.

İşte PointerGestureRecognizer kullanarak bir resim üzerinde işaretçi konumunu gösterme örneği:

public class PointerGesturePage : ContentPage
{
   public PointerGesturePage()
   {
       this
       .Content(
           new StackLayout()
           .Center()
           .Children(
               new Label().Assign(out var label).FontSize(20),
               new Label().Assign(out var enterExitLabel).FontSize(20).TextColor(Colors.Blue),
               new Image()
                   .Source("dotnet_bot.png")
                   .Assign(out var image)
                   .SizeRequest(300, 300)
                   .GestureRecognizers(new GestureRecognizer[]
                   {
                        new PointerGestureRecognizer()
                            .OnPointerEntered((e, args) =>
                            {
                                enterExitLabel.Text = "Entered";
                            })
                            .OnPointerExited((e, args) =>
                            {
                                enterExitLabel.Text = "Exited";
                            })
                            .OnPointerMoved((e, args) =>
                            {
                                var pos = args.GetPosition(relativeTo: image).Value;
                                label.Text = $"point: {pos.X}, {pos.Y}";
                            })
                   })
             )
        };
	}
}

Gradient Nesneler

FmgLib.MauiMarkup, süslü parantezler içinde gradyan fırçalarını kullanarak görsel efektler oluşturmanın bir yolunu sağlar. İki tanımlı gradyan fırça türü vardır:

-LinearGradientBrush

  • RadialGradientBrush.

Örnek

İşte arka planı LinearGradientBrush olan bir Border öğesi örneği. Gradyan efekti sol üst köşeden sağ alt köşeye doğru gider.

new Border()
.Background(
    new LinearGradientBrush()
    .StartPoint(new Point(0, 0))
    .EndPoint(new Point(1, 1))
    .GradientStops(
        new List<GradientStop>(){
            new GradientStop(Colors.Yellow, 0.0),
            new GradientStop(Colors.Red, 0.25),
            new GradientStop(Colors.Blue, 0.75),
            new GradientStop(Colors.LimeGreen, 1.0)
        }
    )
)

Grid Definition

Grid öğesi, Satır ve Sütun tanımlarını kullanarak karmaşık, çok satırlı ve çok sütunlu düzenler oluşturmanıza olanak tanır. Satır ve sütunların sayısını ve boyutunu sırasıyla RowDefinitions ve ColumnDefinitions metodları ile tanımlayabilirsiniz.

Bir çocuk öğenin grid içindeki pozisyonunu Row(), Column(), ColumnSpan() ve RowSpan() metodları ile ayarlayabilirsiniz. Bu metodlar sırasıyla Grid.Row, Grid.Column, Grid.ColumnSpan ve Grid.RowSpan bağlı özelliklerine karşılık gelir.

Satır ve sütun tanımı

Satır ve sütunların sayısı ve boyutunu tanımlamak, sırasıyla RowDefinitions ve ColumnDefinitions metodları ile yapılır. Bu metodlar, satır veya sütunun özelliklerini tanımlayan bir lambda fonksiyonu alır.

Aşağıdaki örnekte, dört satır ve iki sütunlu bir Grid öğesi tanımlanıyor:

new Grid()
.RowDefinitions(e => e.Star(2).Star(0.5, count: 3)))
.ColumnDefinitions(e => e.Absolute(100).Star())
.Children(
    ...
)

Kodun yaptığı şey şudur:

RowDefinitions metodu, farklı boyutlarda dört satır tanımlıyor. İlk satır 2 yıldız alıyor, bu da Grid'deki diğer herhangi bir satırdan iki kat daha fazla dikey alan kaplayacağı anlamına geliyor. İkinci, üçüncü ve dördüncü satırlar her biri 0.5 yıldız alır. count parametresi isteğe bağlıdır ve Grid'e eklenecek aynı boyutta kaç satır olduğunu belirtir. Bu durumda, 0.5 yıldız boyutunda 3 satır ekler.

ColumnDefinitions metodu iki sütun tanımlıyor. İlk sütun, Absolute metodu kullanılarak sabit 100 piksel genişliğe ayarlanmış, ikinci sütun ise kalan alanı Star metodu ile kaplıyor.

Örnek

İşte bir grid tanımının tam bir örneği:

new Grid()
.RowDefinitions(e => e.Star(2).Star())
.ColumnDefinitions(e => e.Absolute(200).Star()))
.Children(
    new BoxView().Color(Colors.Green),
    new Label().Text("Column 0, Row 0"),

    new BoxView().Color(Colors.Blue).Column(1).Row(0),
    new Label().Text("Column 1, Row 0").Column(1).Row(0),

    new BoxView().Color(Colors.Teal).Column(0).Row(1),
    new Label().Text("Column 0, Row 1").Column(0).Row(1),

    new BoxView().Color(Colors.Purple).Column(1).Row(1),
    new Label().Text("Column 1, Row 1").Column(1).Row(1),
)

ITextAlignment Interface Extension Metodları

FmgLib.MauiMarkup içinde, ITextAlignment arayüzünü uygulayan tüm sınıflar aşağıdaki genişletme metodlarına sahip olur:

  • TextCenterHorizontal
  • TextCenterVertical
  • TextCenter
  • TextLeft
  • TextRight
  • TextBottom
  • TextBottomLeft
  • TextBottomRight
  • TextTop
  • TextTopLeft
  • TextTopRight
  • TextTopCenter
  • TextBottomCenter
  • TextCenterLeft
  • TextCenterRight
  • AlignText

Kullanım

Genişletme metodlarını kullanmak için, bir Label nesnesi oluşturun (veya ITextAlignment uygulayan herhangi bir nesne) ve istediğiniz metodu çağırın:

new Label().TextCenter()

Bu örnek, metni etiketin içerdiği öğe içinde hem yatay hem de dikey olarak merkezler.

Layout options

FmgLib.MauiMarkup içinde, her görünümü kendi içinde aşağıdaki genişletme metodlarını kullanarak düzenleyebilirsiniz:

  • CenterHorizontal
  • CenterVertical
  • Center
  • AlignLeft
  • AlignRight
  • AlignTop
  • AlignTopLeft
  • AlignTopRight
  • AlignBottom
  • AlignBottomLeft
  • AlignBottomRight
  • FillHorizontally
  • FillVertically
  • FillBothDirections
  • AlignTopCenter
  • AlignTopFill
  • AlignBottomCenter
  • AlignBottomFill
  • AlignCenterLeft
  • AlignCenterRight
  • AlignCenterFill
  • AlignFillLeft
  • AlignFillRight
  • AlignFillCenter
  • AlignLayout

Kullanım

Düzen seçeneklerini kullanmak için, bir konteyner görünümü oluşturun, düzenlemek istediğiniz görünümü konteynere ekleyin ve istediğiniz metodu çağırın:

new StackLayout()
.Children(
    new Label().Text("Hello, World!").Center()
)

Bu örnekte, bir StackLayout konteyneri içinde bir Label merkezlenmiştir. Aynı metodu diğer konteyner görünümleriyle ve içinde düzenlemek istediğiniz herhangi bir görünümle kullanabilirsiniz.

Özellikler ve Fluent Metodlar

FmgLib.MauiMarkup, UI öğeleri için özellikleri ayarlamak üzere özellikleri akıcı yardımcı metodlarla eşleştirerek kullanışlı bir yol sunar. Bu, uygulamanızın arayüzünü tanımlamayı daha kolay ve daha okunaklı hale getirir.

İşte bir Label üzerinde özellikleri ayarlamak için akıcı metodların kullanımına bir örnek:

new Label()
    .Text("This is a test")
    .Padding(20)
    .FontSize(30)
    .Center())

FmgLib.MauiMarkup, ayrıca cihaz kalıbına, platforma veya uygulama temasına göre özellik değerlerini ayarlama yolunu da sağlar. İşte bir Labelın font boyutunu ve metin rengini mevcut cihaz veya temaya göre ayarlama örneği:

new Label()
    .Text("Hello")
    .FontSize(e => e.OnDesktop(80).OnPhone(30).Default(50))
    .TextColor(e => e.OnLight(Colors.Black).OnDark(Colors.Teal))

Property Bindings

FmgLib.MauiMarkup, bir öğenin özelliklerini kaynağa bağlamanın basit bir yolunu sağlar, böylece kaynak değiştiğinde özellik de değişir. Bir özelliği bağlamak için akıcı metodu kullanabilirsiniz, örneğin Text(), TextColor() vb. ve sonra lambda ile Path() metodunu çağırarak bağlamak istediğiniz özelliği belirtebilirsiniz.

public class SimpleBindings : ContentPage
{
   public SimpleBindings()
   {
       this.Content(
           new StackLayout()

           Children(
               new Slider()
               .Assign(out var slider)
               .Minimum(1)
               .Maximum(20),

               new Label()
               .Text(e => e.Path("Value").Source(slider).StringFormat("Slider value: {0}"))
               .FontSize(28)
           )
       );
   }
}

Bu örnekte, bir Slider öğesinin Value özelliğine bağlı bir etiketin metin özelliği bulunmaktadır. Kaydırıcı değeri değiştiğinde, etiketin metni otomatik olarak yeni değeri yansıtacak şekilde güncellenir.

Bir özelliği görsel ağacın bir parçası olmayan bir nesneye de bağlayabilirsiniz. Bu, görsel bir öğeye bağlamak istediğiniz ayrı bir veri kaynağına, örneğin bir modele veya bir görünüm modeline sahip olduğunuzda faydalıdır.

Shell Uygulaması

İşte basit bir shell tabanlı uygulama örneği:

using FmgLib.MauiMarkup;

public partial class App : Application
{
   public App()
   {
       this.MainPage(
           new Shell()
           .ItemTemplate(() => new ShellItemTemplate())
           .Resources(AppResources.Default)
           .Items(
               new FlyoutItem()
               .FlyoutDisplayOptions(FlyoutDisplayOptions.AsMultipleItems)
               .Items(
                   new Tab()
                   .Title("Main")
                   .Items(
                       new ShellContent()
                       .Title("Hello Page")
                       .ContentTemplate(new HelloWorldPage()),
                       new ShellContent()
                       .Title("ExamplePage")
                       .ContentTemplate(new ExamplePage()),

                   ),

                   new ShellContent()
                   .Title("Grid")
                   .ContentTemplate(new GridPage()),
                   ...
               )
           )
       );
   }
}

FlyoutItem'ın görünümünü özelleştirebilirsiniz, özel bir içerik görünümü tanımlayarak ve Shell öğesinde ItemTemplate özelliğini ayarlayarak.

İşte bir FlyoutItem'ın görünümünü tanımlama örneği:

public class ShellItemTemplate : ContentView
{
   public ShellItemTemplate()
   {
       this
       .Content(
           new Grid()
           .ColumnDefinitions(e => e.Star(0.2).Star(0.8))
           .Children(
               new Image()
                   .Source(e => e.Path("FlyoutIcon"))
                   .Margin(5)
                   .HeightRequest(45),

               new Label()
                   .GridColumn(1)
                   .Text(e => e.Path("Title"))
                   .FontSize(20)
                   .FontAttributes(FontAttributes.Italic)
                   .CenterVertically()
           )
       );
   }
}

Uygulama Stilleme(Style)

FmgLib.MauiMarkup, öğelerin stillerini Style<T> sınıfını kullanarak tanımlamanın bir yolunu sunar. İşte bir butonun stilini nasıl tanımlayacağınıza dair bir örnek:

new Style<Button>(e => e
    .TextColor(e => e.OnLight(Colors.White).OnDark(AppColors.Primary))
    .BackgroundColor(e => e.OnLight(AppColors.Primary).OnDark(Colors.White))
    .FontFamily("OpenSansRegular")
    .FontSize(14)
    .CornerRadius(8)
    .Padding(new Thickness(14, 10))
    .MinimumHeightRequest(44)
    .MinimumWidthRequest(44))

Örnekte, bir butonun özellikleri akıcı uzantı metotları kullanılarak ayarlanmaktadır.

Ayrıca, uygulama temasına, cihaz kalıbına veya platforma bağlı olarak farklı değerler de kullanabilirsiniz:

Ek olarak, öğeler için görsel durumlar tanımlayabilirsiniz:

new Style<Button>(e => e...)
{
    ...
    new VisualState<Button> (VisualStateEnum.VisualElement.Normal, e => e
        .TextColor(e => e.OnLight(Colors.White).OnDark(AppColors.Primary))
        .BackgroundColor(e => e.OnLight(AppColors.Primary).OnDark(Colors.White))),

    new VisualState<Button> (VisualStateEnum.VisualElement.Disabled, e => e
        .TextColor(e => e.OnLight(AppColors.Gray950).OnDark(AppColors.Gray200))
        .BackgroundColor(e => e.OnLight(AppColors.Gray200).OnDark(AppColors.Gray600))),
},

Ayrıca, görsel durumları animasyon tanımlamak için kullanabilirsiniz:

new Style<Button>(e => e...)
{
    ...


    new VisualState<Button>(VisualStates.Button.Normal, e => e
        .FontSize(33)
        .TextColor(AppColors.Gray200))
    {
        async button => {
            await button.RotateTo(0);   // create animation inside VisualState
        }
    },

    new VisualState<Button>(VisualStates.Button.Disabled, e => e
        .FontSize(20)
        .TextColor(AppColors.Gray600))
    {
        async button => {
            await button.RotateTo(180);
        }
    },
}

Son olarak, tanımlanan tüm stiller bir ResourceDictionary içine yerleştirilebilir:

new ResourceDictionary
{
    ...


    new Style<Button>(e => e
        .TextColor(e => e.OnLight(Colors.White).OnDark(AppColors.Primary))
        .BackgroundColor(e => e.OnLight(AppColors.Primary).OnDark(Colors.White))
        .FontFamily("OpenSansRegular")
        .FontSize(14)
        .CornerRadius(8)
        .Padding(new Thickness(14,10))
        .MinimumHeightRequest(44)
        .MinimumWidthRequest(44))
    {
        new VisualState<Button> (VisualStateEnum.VisualElement.Normal, e => e
            .TextColor(e => e.OnLight(Colors.White).OnDark(AppColors.Primary))
            .BackgroundColor(e => e.OnLight(AppColors.Primary).OnDark(Colors.White))),

        new VisualState<Button> (VisualStateEnum.VisualElement.Disabled, e => e
            .TextColor(e => e.OnLight(AppColors.Gray950).OnDark(AppColors.Gray200))
            .BackgroundColor(e => e.OnLight(AppColors.Gray200).OnDark(AppColors.Gray600)))
    },

    new Style<Frame>(e => e
        .HasShadow(false)
        .BorderColor(e => e.OnLight(AppColors.Gray200).OnDark(AppColors.Gray950))
        .CornerRadius(8)),
    ...
};

Kullanıcı Tanımlı Extension Metodları

FmgLib: mauiMarkup içinde, kendi genişletme metodlarınızı statik bir sınıf içinde statik bir metot tanımlayarak oluşturabilirsiniz.

İşte bir Label nesnesinin yazı tipi boyutunu ayarlayan genişletme metodlarına bir örnek:

public static T FontSize<T>(this T self,
    double fontSize)
    where T : Microsoft.Maui.Controls.Label
{
   self.SetValue(Microsoft.Maui.Controls.Label.FontSizeProperty, fontSize);
   return self;
}

public static T FontSize<T>(this T self, Func<PropertyContext<double>, IPropertyBuilder<double>> configure)
   where T : Microsoft.Maui.Controls.Label
{
   var context = new PropertyContext<double>(self, Microsoft.Maui.Controls.Label.FontSizeProperty);
   configure(context).Build();
   return self;
}

public static SettersContext<T> FontSize<T>(this SettersContext<T> self,
   double fontSize)
   where T : Microsoft.Maui.Controls.Label
{
   self.XamlSetters.Add(new Setter { Property = Microsoft.Maui.Controls.Label.FontSizeProperty, Value = fontSize });
   return self;
}

public static SettersContext<T> FontSize<T>(this SettersContext<T> self, Func<PropertySettersContext<double>, IPropertySettersBuilder<double>> configure)
   where T : Microsoft.Maui.Controls.Label
{
   var context = new PropertySettersContext<double>(self.XamlSetters, Microsoft.Maui.Controls.Label.FontSizeProperty);
   configure(context).Build();
   return self;
}

public static Task<bool> AnimateFontSizeTo<T>(this T self, double value, uint length = 250, Easing? easing = null)
   where T : Microsoft.Maui.Controls.Label
{
   double fromValue = self.FontSize;
   var transform = (double t) => Transformations.DoubleTransform(fromValue, value, t);
   var callback = (double actValue) => { self.FontSize = actValue; };
   return Transformations.AnimateAsync<double>(self, "AnimateFontSizeTo", transform, callback, length, easing);
}

Bu, aşağıdaki kullanımı sağlar:

new Label().FontSize(28)

veya:

new Label().FontSize(e => e.Path("MyFontSize").Source(myModel))
new Label().FontSize(e => e.OnPhone(30).OnTablet(50).Default(40))

veya bir stil bağlamında kullanım:

new Style<Label>(e => e
    .FontSize(20)
    .CenterVertically()
    .CenterHorizontally())

veya bir animasyon bağlamında kullanım.

Triggers

Tetikleyiciler, belirli koşullar veya olaylara yanıt olarak özellikleri ayarlamanıza olanak tanır.

Özellik Tetikleyicileri

Bir özellik tetikleyicisi, başka bir özelliğin değerindeki değişikliğe yanıt olarak bir özelliği ayarlar.

İşte bir Entry öğesinin odaklandığında arka plan rengini ve metin rengini değiştirmek için bir özellik tetikleyicisini kullanma örneği:

using FmgLib:mauiMarkup;

public class PropertyTriggerPage : ContentPage
{
   public PropertyTriggerPage()
   {
       this
       .Resources(
           new ResourceDictionary
           {
                new Style<Entry>
                {
                    Entry.BackgroundColorProperty.Set(Colors.Black),
                    Entry.TextColorProperty.Set(Colors.White),

                    new Trigger(typeof(Entry))
                        .Property(Entry.IsFocusedProperty)
                        .Value(true)
                        .Setters(
                            new Setters<Entry>(e => e
                                .BackgroundColor(Colors.Yellow)
                                .TextColor(Colors.Black))
                        ),
                }
           }
       )
       .Content(
           new StackLayout()
           .Children(
               new Entry().Text("Enter name"),
               new Entry().Text("Enter password"),
               new Entry().Text("Enter address")
           )
       );
   }
}

Veri Tetikleyicileri

Bir veri tetikleyicisi, bir veri kaynağının değerindeki bir değişikliğe yanıt olarak bir özelliği ayarlar.

İşte bir Entry öğesinin metin uzunluğu sıfır olduğunda bir düğmeyi devre dışı bırakmak için bir veri tetikleyicisini kullanma örneği:

this.Content(new StackLayout()
    .Children(
        new Entry().Assign(out var entry).Text("Enter text..."),
        new Button()
            .Text("Save")
            .Triggers(
                new DataTrigger(typeof(Button))
                    .Binding(e => e.Path("Text.Length").Source(entry))
                    .Value(0)
                    .Setters(new Setters<Entry>(e => e.IsEnabled(false)))
            ),

    )
)

Olay Tetikleyicileri

Bir olay tetikleyicisi, bir olaya yanıt olarak bir özelliği ayarlar.

İşte bir Entry öğesinin girdisini bir sayı olarak doğrulamak için bir olay tetikleyicisini kullanma örneği:

this
.Content(new StackLayout()
    .Children(
        new Entry()
            .Assign(out var entry)
            .Text("Enter text...")
            .Triggers(
                new EventTrigger()
                    .Event("TextChanged")
                    .Actions(new NumericValidationTriggerAction())
            )
    )
)

Ve işte NumericValidationTriggerAction sınıfının tanımı:

public class NumericValidationTriggerAction : TriggerAction<Entry>
{
   protected override void Invoke(Entry entry)
   {
       double result;
       bool isValid = Double.TryParse(entry.Text, out result);
       entry.TextColor = isValid ? Colors.Black : Colors.Red;
   }
}

3. Taraf Kontroller İçin Uzantılar

FmgLib.MauiMarkup kütüphanesi, üçüncü taraf kütüphanelerinden kontrol tipleri için de uzantı metotları üretebilir. Bunu başarmak için, FmgLib.MauiMarkup tarafından sağlanan MauiMarkupAttribute kullanılmalıdır.

Uzantı metotları oluşturmak istediğiniz kontrolü [MauiMarkup(typeof(YourControl))] şeklinde belirtin.

MauiMarkup() özniteliğinin yapıcı metodu, argüman olarak verilen tip içinde bulunan BindableProperties ve Eventler için otomatik olarak uzantı metotları üretir. Yapıcı metod içinde minimum 1 tür sağlayabilirsiniz ve maksimum sınır yoktur. Tek bir sınıfa birden fazla MauiMarkup özniteliği eklenebilir.

Örneklere bir göz atalım:

using FmgLib.MauiMarkup;

namespace GeneratedExam;

[MauiMarkup(typeof(ZXing.Net.Maui.Controls.BarcodeGeneratorView))]
public class MyBarcodeGeneratorView { }

[MauiMarkup(typeof(ZXing.Net.Maui.Controls.CameraView))]
public class MyCameraView { }


[MauiMarkup(typeof(ZXing.Net.Maui.Controls.CameraBarcodeReaderView))]
public class MyCameraBarcodeReaderView { }

[MauiMarkup(typeof(SkiaSharp.Extended.UI.Controls.SKLottieView))]
public class MySkLottieView { }

Veya bunun yerine şöyle kullanılabilir:

using Microsoft.Extensions.Logging;
using FmgLib.MauiMarkup;
using SkiaSharp.Extended.UI.Controls;
using ZXing.Net.Maui.Controls;
using UraniumUI.Material.Controls;
namespace MauiApp1
{
    [MauiMarkup(typeof(CameraView))]
    [MauiMarkup(typeof(SKLottieView), typeof(SKFileLottieImageSource), typeof(DataGrid))]
    [MauiMarkup(typeof(SKConfettiView), typeof(BarcodeGeneratorView),typeof(InputField),typeof(EditorField),typeof(TextField))]
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                });

#if DEBUG
    		builder.Logging.AddDebug();
#endif
            return builder.Build();
        }
    }
}

ZXing.Net.Maui.Controls kütüphanesinden CameraView sınıfı:

using System;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;
using System;

namespace ZXing.Net.Maui.Controls
{
	public partial class CameraView : View, ICameraView
	{
		public event EventHandler<CameraFrameBufferEventArgs> FrameReady;

		void ICameraFrameAnalyzer.FrameReady(CameraFrameBufferEventArgs e)
			=> FrameReady?.Invoke(this, e);

		public static readonly BindableProperty IsTorchOnProperty =
			BindableProperty.Create(nameof(IsTorchOn), typeof(bool), typeof(CameraView), defaultValue: true);

		public bool IsTorchOn
		{
			get => (bool)GetValue(IsTorchOnProperty);
			set => SetValue(IsTorchOnProperty, value);
		}

		public static readonly BindableProperty CameraLocationProperty =
			BindableProperty.Create(nameof(CameraLocation), typeof(CameraLocation), typeof(CameraView), defaultValue: CameraLocation.Rear);

		public CameraLocation CameraLocation
		{
			get => (CameraLocation)GetValue(CameraLocationProperty);
			set => SetValue(CameraLocationProperty, value);
		}

		public void AutoFocus()
			=> StrongHandler?.Invoke(nameof(AutoFocus), null);

		public void Focus(Point point)
			=> StrongHandler?.Invoke(nameof(Focus), point);

		CameraViewHandler StrongHandler 
			=> Handler as CameraViewHandler;
	}
}

MauiMarkup özniteliği kullanılarak oluşturulan CameraViewExtension sınıfı aşağıdaki gibi olacaktır:

//
// <auto-generated-fmglib-mauimarkup-generator />
//


namespace FmgLib.MauiMarkup;

public static partial class CameraViewExtension
{
        public static T CameraLocation<T>(this T self,
            ZXing.Net.Maui.CameraLocation cameraLocation)
            where T : ZXing.Net.Maui.Controls.CameraView
        {
            self.SetValue(ZXing.Net.Maui.Controls.CameraView.CameraLocationProperty, cameraLocation);
            return self;
        }
        
        public static T CameraLocation<T>(this T self, Func<PropertyContext<ZXing.Net.Maui.CameraLocation>, IPropertyBuilder<ZXing.Net.Maui.CameraLocation>> configure)
            where T : ZXing.Net.Maui.Controls.CameraView
        {
            var context = new PropertyContext<ZXing.Net.Maui.CameraLocation>(self, ZXing.Net.Maui.Controls.CameraView.CameraLocationProperty);
            configure(context).Build();
            return self;
        }
        
        public static SettersContext<T> CameraLocation<T>(this SettersContext<T> self,
            ZXing.Net.Maui.CameraLocation cameraLocation)
            where T : ZXing.Net.Maui.Controls.CameraView
        {
            self.XamlSetters.Add(new Setter { Property = ZXing.Net.Maui.Controls.CameraView.CameraLocationProperty, Value = cameraLocation });
            return self;
        }
        
        public static SettersContext<T> CameraLocation<T>(this SettersContext<T> self, Func<PropertySettersContext<ZXing.Net.Maui.CameraLocation>, IPropertySettersBuilder<ZXing.Net.Maui.CameraLocation>> configure)
            where T : ZXing.Net.Maui.Controls.CameraView
        {
            var context = new PropertySettersContext<ZXing.Net.Maui.CameraLocation>(self.XamlSetters, ZXing.Net.Maui.Controls.CameraView.CameraLocationProperty);
            configure(context).Build();
            return self;
        }
        
        public static T IsTorchOn<T>(this T self,
            bool isTorchOn)
            where T : ZXing.Net.Maui.Controls.CameraView
        {
            self.SetValue(ZXing.Net.Maui.Controls.CameraView.IsTorchOnProperty, isTorchOn);
            return self;
        }
        
        public static T IsTorchOn<T>(this T self, Func<PropertyContext<bool>, IPropertyBuilder<bool>> configure)
            where T : ZXing.Net.Maui.Controls.CameraView
        {
            var context = new PropertyContext<bool>(self, ZXing.Net.Maui.Controls.CameraView.IsTorchOnProperty);
            configure(context).Build();
            return self;
        }
        
        public static SettersContext<T> IsTorchOn<T>(this SettersContext<T> self,
            bool isTorchOn)
            where T : ZXing.Net.Maui.Controls.CameraView
        {
            self.XamlSetters.Add(new Setter { Property = ZXing.Net.Maui.Controls.CameraView.IsTorchOnProperty, Value = isTorchOn });
            return self;
        }
        
        public static SettersContext<T> IsTorchOn<T>(this SettersContext<T> self, Func<PropertySettersContext<bool>, IPropertySettersBuilder<bool>> configure)
            where T : ZXing.Net.Maui.Controls.CameraView
        {
            var context = new PropertySettersContext<bool>(self.XamlSetters, ZXing.Net.Maui.Controls.CameraView.IsTorchOnProperty);
            configure(context).Build();
            return self;
        }
        
        public static T OnFrameReady<T>(this T self, System.EventHandler<ZXing.Net.Maui.CameraFrameBufferEventArgs> handler)
            where T : ZXing.Net.Maui.Controls.CameraView
        {
            self.FrameReady += handler;
            return self;
        }
        
        public static T OnFrameReady<T>(this T self, System.Action<T> action)
            where T : ZXing.Net.Maui.Controls.CameraView
        {
            self.FrameReady += (o, arg) => action(self);
            return self;
        }
        
}

Örneğin, TextField ve SKLottieView Kontrolleri için örnek kodları yazalım:

new TextField()
.Title("Password")
.TitleColor(Colors.LightGray)
.AccentColor(Colors.CadetBlue)
.TextColor(Colors.White)
.IsPassword(true),

new SKLottieView()
.Source(new SKFileLottieImageSource().File("iconapp.json"))
.RepeatCount(-1)
.HeightRequest(250)
.WidthRequest(250)

MauiMarkupAttachedPropAttribute sayesinde üçüncü taraf kütüphanelerdeki Control sınıfları içinde AttachedProperty'lerin rahatlıkla extension methodlarını oluşturabilirsiniz. Yapıcı Methodu içerisindeki ilk parametre Control sınıfı tipini, ikinci parametre AttachedProperty adını, üçüncü parametre ttachedProperty'in alabileceği değer tipini ve dördüncü parametre ise AttachedProperty'in hangi türe uygulanacağını alır.

Örnek kullanım:

[MauiMarkupAttachedProp(typeof(InputKit.Shared.Controls.FormView), nameof(InputKit.Shared.Controls.FormView.IsSubmitButtonProperty), typeof(bool), typeof(Button))]
[MauiMarkup(typeof(InputKit.Shared.Controls.FormView))]
public class MyFormView { }

Veya


using Microsoft.Extensions.Logging;
using FmgLib.MauiMarkup;

namespace MauiApp1
{
    [MauiMarkupAttachedProp(typeof(InputKit.Shared.Controls.FormView), nameof(InputKit.Shared.Controls.FormView.IsSubmitButtonProperty), typeof(bool), typeof(Button))]
    [MauiMarkup(typeof(InputKit.Shared.Controls.FormView))]
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                });

#if DEBUG
    		builder.Logging.AddDebug();
#endif
            return builder.Build();
        }
    }
}

Button sınıfında AttachedProperty'in kullanımı ise şu şekilde olacaktır:

new Button()
.Text("Login")
.FontAttributes(Bold)
.FormViewIsSubmitButton(true)

Yerelleştirme (Çoklu Dil Desteği)

MauiProgram.cs dosyasına aşağıdaki kod eklenmelidir:

builder
    .UseMauiApp<App>()
    .UseMauiMarkupLocalization();

Ana projenizde json türünde bir dil dosyasına sahip olmalısınız. Çeviri bu dosyadan okunarak içe aktarılacaktır. Eğer dosya(lar) için parametrelerde bir yol belirtmezseniz (

builder
    .UseMauiApp<App>()
    .UseMauiMarkupLocalization();
  //.UseMauiMarkupLocalization(defaultLang:"en-US"); // veya
  //.UseMauiMarkupLocalization(defaultLang:"en-US", "Loc1.json", "Loc2.json"); // veya

), ana dizinde Localization.json adlı bir json dosyası arayacaktır.

Eğer şu şekilde bir veya daha fazla parametre verirseniz:

builder
    .UseMauiApp<App>()
    .UseMauiMarkupLocalization("Localization1.json", "Localization2.json", "/Languages/Temp1.json");

Belirtilen dosya yollarındaki json dosyalarını okuyacaktır.

Burada kritik nokta, json dosyaları için Build Action: MauiAsset seçeneğini belirlemektir.

Doğru json formatı şu şekildedir:

{
  "Hello": {
    "tr-TR": "Merhaba Dünya!",
    "en-US": "Hello World!"
  },
  "Msg": {
    "tr-TR": "Deneme amaçlı yapılmıştır.",
    "en-US": "It was made for testing purposes."
  }
}

'Msg' anahtar kelimeleri yerine istediğiniz kelimeyi veya kelime öbeğini kullanabilirsiniz. Herhangi bir Regex sınırlamanız yoktur.

Ayrıca, 'tr-TR' ve 'en-US' dil anahtarlarını istediğiniz kelimeler veya cümlelerle değiştirebilirsiniz. Ancak 'en-US', 'tr-TR', 'fr-FR' gibi ifadelerin kullanılması tavsiye edilir.

Kodu şu şekilde basitçe kullanabilirsiniz:

new Label()
.Text(e => e.Translate("Hello"))
.FontSize(32)
.CenterHorizontal()
.SemanticHeadingLevel(SemanticHeadingLevel.Level1),

new Label()
.Text(e => e.Translate("Msg"))
.FontSize(18)
.CenterHorizontal()
.SemanticDescription(e => e.Translate("Msg"))
.SemanticHeadingLevel(SemanticHeadingLevel.Level1)

Mevcut dili değiştirmek için şu adımları takip edebilirsiniz:

Translator.Instance.ChangeCulture(CultureInfo.GetCultureInfo("en-US"));
//OR
Translator.Instance.ChangeCulture(CultureInfo.GetCultureInfo("tr-TR"));

Genel Örnek Kod


using Microsoft.Maui.Layouts;
using FmgLib.MauiMarkup;

namespace MyOrderApp.Views;

public partial class HomePage : BasePage<HomePageViewModel>
{
    public HomePage(HomePageViewModel viewModel) : base(viewModel, "Home Page")
    {
    }

    public override void Build()
    {
        this
        .Content(
            new VerticalStackLayout()
            .Padding(10)
            .Children(
                new SearchBar()
                .Placeholder("Ürünlerde Ara.")
                .Margin(10)
                .Assign(out var search)
                .SearchCommand(BindingContext.SearchCommand)
                .Bind(SearchBar.SearchCommandParameterProperty, "Text", source: search),

                new Frame()
                .CornerRadius(15)
                .BackgroundColor(Colors.Blue)
                .BorderColor(Colors.Blue)
                .HeightRequest(150)
                .Margin(15,7)
                .Padding(0)
                .Content(
                    new Grid()
                    .ColumnDefinitions(e => e.Star(5).Star(5))
                    .RowDefinitions(e => e.Star(5).Star(5))
                    .Children(
                        new Frame()
                        .Row(0)
                        .Column(0)
                        .Margin(0,20,0,0)
                        .Padding(0)
                        .CornerRadius(0)
                        .BackgroundColor(Colors.DarkBlue)
                        .BorderColor(Colors.DarkBlue)
                        .Content(
                            new Label()
                            .Text("%50 İndirim")
                            .TextColor(Colors.White)
                            .FontAttributes(FontAttributes.Bold)
                            .FontSize(20)
                            .Center()
                        ),

                        new Label()
                        .Text("Tüm Unlu Mamüllerde her gün saat 21:00'dan sonra!")
                        .FontSize(12)
                        .Row(1)
                        .Column(0)
                        .TextColor(Colors.White)
                        .FontAttributes(FontAttributes.Italic)
                        .Margin(10,3,0,0),

                        new Image()
                        .Source("white_board.png")
                        .Row(0)
                        .Column(1)
                        .RowSpan(2)
                        .SizeRequest(150,100)
                        .Opacity(.8)
                    )
                ),

                new Grid()
                .ColumnDefinitions(e => e.Star(7).Star(3))
                .FillHorizontal()
                .Padding(10)
                .Children(
                    new Label()
                    .Text("Son Ürünler")
                    .FontAttributes(FontAttributes.Bold)
                    .FontSize(18)
                    .CenterVertical()
                    .Column(0)
                    .AlignLeft(),


                    new Label()
                    .Text("Tümünü Gör")
                    .FontSize(15)
                    .CenterVertical()
                    .Column(1)
                    .AlignRight()
                    .TextDecorations(TextDecorations.Underline)
                    .GestureRecognizers(
                        new TapGestureRecognizer()
                        .Command(BindingContext.GotoAllProductsCommand)
                    )
                ),

                new CollectionView()
                .SelectionMode(SelectionMode.None)
                .Bind(CollectionView.ItemsSourceProperty, "Products")
                .ItemsLayout(new LinearItemsLayout(ItemsLayoutOrientation.Horizontal).ItemSpacing(10))
                .EmptyView(
                    new VerticalStackLayout()
                    .Children(
                        new Label()
                        .Text("Kayıt Yoktur.")
                        .TextColor(Colors.Red)
                        .FontAttributes(FontAttributes.Bold)
                        .FontSize(18)
                    )
                    .Center()
                )
                .ItemTemplate(() =>
                    new Frame()
                    .CornerRadius(15)
                    .BorderColor(Colors.LightGray)
                    .BackgroundColor(Colors.LightGray)
                    .MinimumHeightRequest(200)
                    .MaximumWidthRequest(200)
                    .Padding(5)
                    .Content(
                        new Grid()
                        .RowDefinitions(e => e.Star(1).Star(6).Star(2).Star(1))
                        .Padding(5)
                        .Children(
                            new Grid()
                            .Row(0)
                            .ColumnDefinitions(e => e.Star(6).Star(4))
                            .Children(
                                new ImageButton()
                                .Bind(ImageButton.SourceProperty, nameof(ProductVM.IsFavorite), converter: new BoolToFavoriteImageConverter())
                                .BackgroundColor(Colors.Transparent)
                                .AlignLeft()
                                .SizeRequest(30, 30)
                                .Command(BindingContext.ChangeFavoriteCommand)
                                .Bind(ImageButton.CommandParameterProperty, "."),

                                new Frame()
                                .CornerRadius(20)
                                .HeightRequest(25)
                                .WidthRequest(50)
                                .Padding(0)
                                .BackgroundColor(Colors.Red)
                                .BorderColor(Colors.Red)
                                .Column(1)
                                .Bind(Microsoft.Maui.Controls.Frame.IsVisibleProperty, nameof(ProductVM.IsDiscount))
                                .Content(
                                    new Label()
                                    .Bind(Label.TextProperty, nameof(ProductVM.DiscountRate))
                                    .FontSize(11)
                                    .FontAttributes(FontAttributes.Bold)
                                    .TextColor(Colors.White)
                                    .Center()
                                )
                            ),

                            new Image()
                            .Bind(Image.SourceProperty, nameof(ProductVM.Image))
                            .SizeRequest(80,80)
                            .Row(1)
                            .CenterHorizontal(),

                            new VerticalStackLayout()
                            .Row(2)
                            .Children(
                                new Label()
                                .Bind(Label.TextProperty, nameof(ProductVM.Name))
                                .FontAttributes(FontAttributes.Bold)
                                .FontSize(11)
                                .AlignLeft()
                                .LineBreakMode(LineBreakMode.TailTruncation)
                                .FontAutoScalingEnabled(true),

                                new HorizontalStackLayout()
                                .Spacing(2)
                                .Children(
                                    new Label()
                                    .Bind(Label.TextProperty, nameof(ProductVM.Price))
                                    .Bind(Label.TextDecorationsProperty, nameof(ProductVM.IsDiscount), converter: new BoolToTextDecorationConverter())
                                    .Bind(Label.FontSizeProperty, nameof(ProductVM.IsDiscount), converter: new BoolToFontSizeConverter())
                                    .FontAttributes(FontAttributes.Bold)
                                    .CenterVertical(),

                                    new Label()
                                    .TextColor(Colors.Red)
                                    .FontAttributes(FontAttributes.Bold)
                                    .CenterVertical()
                                    .Bind(Label.IsVisibleProperty, nameof(ProductVM.IsDiscount))
                                    .Bind(Label.TextProperty, nameof(ProductVM.DiscountPrice)),

                                    new Label()
                                    .Text("/")
                                    .FontSize(10)
                                    .CenterVertical()
                                    .TextColor(Colors.DarkSlateGray),

                                    new Label()
                                    .FontSize(10)
                                    .CenterVertical()
                                    .TextColor(Colors.DarkSlateGray)
                                    .Bind(Label.TextProperty, nameof(ProductVM.Unit))
                                )
                            ),

                            new Button()
                            .Row(3)
                            .Margin(new Thickness(0,5,0,0))
                            .Padding(0)
                            .Text("Sepete Ekle")
                            .BackgroundColor(Colors.Green)
                            .FontSize(12)
                            .FontAttributes(FontAttributes.Bold)
                            .CenterHorizontal()
                            .HeightRequest(35)
                            .WidthRequest(100)
                            .Command(BindingContext.AddProductBasketCommand)
                            .Bind(Button.CommandParameterProperty, ".")
                        )
                    )
                ),

                new Grid()
                .ColumnDefinitions(e => e.Star(7).Star(3))
                .FillHorizontal()
                .Padding(10)
                .Children(
                    new Label()
                    .Text("Kategoriler")
                    .FontAttributes(FontAttributes.Bold)
                    .FontSize(18)
                    .CenterVertical()
                    .Column(0)
                    .AlignLeft(),


                    new Label()
                    .Text("Tümünü Gör")
                    .FontSize(15)
                    .CenterVertical()
                    .Column(1)
                    .AlignRight()
                    .TextDecorations(TextDecorations.Underline)
                    .GestureRecognizers(
                        new TapGestureRecognizer()
                        .Command(BindingContext.GotoAllCategoriesCommand)
                    )
                ),

                new FlexLayout()
                .ItemsSources(BindingContext.Categories)
                .Assign(out var flex)
                .Wrap(FlexWrap.Wrap)
                .FlexBasis(FlexBasis.Auto)
                .ItemTemplates(new DataTemplate(() => 
                    new Frame()
                    .CornerRadius(15)
                    .BorderColor(Colors.LightGray)
                    .BackgroundColor(Colors.LightGray)
                    .MinimumHeightRequest(30)
                    .WidthRequest(180)
                    .Padding(0)
                    .Margin(new Thickness(1,0,5,5))
                    .FlexBasis(FlexBasis.Auto)
                    .Content(
                        new Grid()
                        .ColumnDefinitions(e => e.Star(3).Star(7))
                        .Padding(5)
                        .Children(
                            new Image()
                            .Bind(Image.SourceProperty, nameof(SubCategoryVM.Icon))
                            .SizeRequest(30,30)
                            .Column(0)
                            .CenterVertical(),

                            new Label()
                            .Bind(Label.TextProperty, nameof(SubCategoryVM.Name))
                            .TextColor(Colors.CornflowerBlue)
                            .FontAttributes(FontAttributes.Bold)
                            .FontSize(12)
                            .Column(1)
                            .FontAutoScalingEnabled(true)
                            .CenterVertical()
                        )
                    )
                ))
            )
            .FillHorizontal()
        );
    }
}