From a9ec9f526c019caa94962f8c8c4230405d793c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20G=C3=B6n=C3=BClta=C5=9F?= Date: Sun, 18 Aug 2024 23:59:34 +0300 Subject: [PATCH 1/5] fix --- .../FmgLib.MauiMarkup.csproj | 5 ++- .../Handlers/HotReloadAppExtensions.cs | 36 +++++++++++++++++++ .../Handlers/HotReloadExtensions.cs | 24 +++++++++++++ 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 libs/FmgLib.MauiMarkup/Handlers/HotReloadAppExtensions.cs create mode 100644 libs/FmgLib.MauiMarkup/Handlers/HotReloadExtensions.cs diff --git a/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj b/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj index 5e53ce7..22a5ec5 100644 --- a/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj +++ b/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj @@ -11,7 +11,7 @@ FmgLib.MauiMarkup FmgLib.MauiMarkup with C# Markup classes and fluent helper methods FmgLib.MauiMarkup - 8.6.7 + 100.8.7.0 FmgYazılım Fmg Yazılım ©2024 @@ -36,4 +36,7 @@ + + + diff --git a/libs/FmgLib.MauiMarkup/Handlers/HotReloadAppExtensions.cs b/libs/FmgLib.MauiMarkup/Handlers/HotReloadAppExtensions.cs new file mode 100644 index 0000000..24a7ccf --- /dev/null +++ b/libs/FmgLib.MauiMarkup/Handlers/HotReloadAppExtensions.cs @@ -0,0 +1,36 @@ +using Microsoft.Maui.LifecycleEvents; +using System.Reflection; + +namespace FmgLib.MauiMarkup; + +public static class HotReloadAppExtensions +{ + public static MauiAppBuilder UseFmgLibHotReload(this MauiAppBuilder builder) + { + builder.ConfigureLifecycleEvents(events => + { + events.AddEvent>(nameof(Application.MainPage), _ => + { + RegisterHotReloadForPages(); + }); + }); + + return builder; + } + + private static void RegisterHotReloadForPages() + { + var reloadablePages = Assembly.GetExecutingAssembly() + .GetTypes() + .Where(t => typeof(IFmgLibHotReload).IsAssignableFrom(t) && !t.IsAbstract && t.IsSubclassOf(typeof(Page))) + .ToList(); + + foreach (var pageType in reloadablePages) + { + if (Activator.CreateInstance(pageType) is IFmgLibHotReload reloadablePage) + { + reloadablePage.InitializeHotReload(); + } + } + } +} diff --git a/libs/FmgLib.MauiMarkup/Handlers/HotReloadExtensions.cs b/libs/FmgLib.MauiMarkup/Handlers/HotReloadExtensions.cs new file mode 100644 index 0000000..e464503 --- /dev/null +++ b/libs/FmgLib.MauiMarkup/Handlers/HotReloadExtensions.cs @@ -0,0 +1,24 @@ +using System.Diagnostics; + +namespace FmgLib.MauiMarkup; + +public static class HotReloadExtensions +{ + public static TPage InitializeHotReload(this TPage page) where TPage : IFmgLibHotReload + { + page.Build(); + + if (Debugger.IsAttached) + { + FmgLibHotReloadHandler.UpdateApplicationEvent += (types) => + { + if (types == null || types.Contains(page.GetType())) + { + MainThread.BeginInvokeOnMainThread(page.Build); + } + }; + } + + return page; + } +} \ No newline at end of file From ea672e528ecc010de66ace47764d2d8113ea0e7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20G=C3=B6n=C3=BClta=C5=9F?= Date: Sun, 18 Aug 2024 23:59:47 +0300 Subject: [PATCH 2/5] fix. --- libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj | 3 --- 1 file changed, 3 deletions(-) diff --git a/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj b/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj index 22a5ec5..edd9482 100644 --- a/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj +++ b/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj @@ -36,7 +36,4 @@ - - - From dcbc1ad80461181d0b51f66a8e411cf117acf7bb Mon Sep 17 00:00:00 2001 From: Mustafa Gonultas Date: Mon, 19 Aug 2024 12:48:42 +0300 Subject: [PATCH 3/5] finished. --- .../FmgLib.MauiMarkup.csproj | 2 +- .../Handlers/HotReloadAppExtensions.cs | 36 ------------------- 2 files changed, 1 insertion(+), 37 deletions(-) delete mode 100644 libs/FmgLib.MauiMarkup/Handlers/HotReloadAppExtensions.cs diff --git a/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj b/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj index edd9482..f4b97ea 100644 --- a/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj +++ b/libs/FmgLib.MauiMarkup/FmgLib.MauiMarkup.csproj @@ -11,7 +11,7 @@ FmgLib.MauiMarkup FmgLib.MauiMarkup with C# Markup classes and fluent helper methods FmgLib.MauiMarkup - 100.8.7.0 + 8.7.0 FmgYazılım Fmg Yazılım ©2024 diff --git a/libs/FmgLib.MauiMarkup/Handlers/HotReloadAppExtensions.cs b/libs/FmgLib.MauiMarkup/Handlers/HotReloadAppExtensions.cs deleted file mode 100644 index 24a7ccf..0000000 --- a/libs/FmgLib.MauiMarkup/Handlers/HotReloadAppExtensions.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.Maui.LifecycleEvents; -using System.Reflection; - -namespace FmgLib.MauiMarkup; - -public static class HotReloadAppExtensions -{ - public static MauiAppBuilder UseFmgLibHotReload(this MauiAppBuilder builder) - { - builder.ConfigureLifecycleEvents(events => - { - events.AddEvent>(nameof(Application.MainPage), _ => - { - RegisterHotReloadForPages(); - }); - }); - - return builder; - } - - private static void RegisterHotReloadForPages() - { - var reloadablePages = Assembly.GetExecutingAssembly() - .GetTypes() - .Where(t => typeof(IFmgLibHotReload).IsAssignableFrom(t) && !t.IsAbstract && t.IsSubclassOf(typeof(Page))) - .ToList(); - - foreach (var pageType in reloadablePages) - { - if (Activator.CreateInstance(pageType) is IFmgLibHotReload reloadablePage) - { - reloadablePage.InitializeHotReload(); - } - } - } -} From b498cfa87ac0d07ee4b0e4c92ff73b5450461006 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mustafa=20G=C3=B6n=C3=BClta=C5=9F?= Date: Mon, 19 Aug 2024 21:36:44 +0300 Subject: [PATCH 4/5] published (8.7.0) --- README.md | 47 ++ libs/FmgLib.MauiMarkup/README.md | 1204 +++++++++++++++++++++++++++++- 2 files changed, 1221 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 773e7c6..b70ff83 100644 --- a/README.md +++ b/README.md @@ -1035,6 +1035,53 @@ public class NumericValidationTriggerAction : TriggerAction } ``` +### .NET built-in Hot-Reload + +Additionally, the FmgLib.MauiMarkup library includes hot reload support to make the development process faster and more efficient. + +If you want to enhance your page with fast reload, your page needs to implement the IFmgLibHotReload interface. Then, you should trigger the this.InitializeHotReload(); function within the constructor. + +And you can write your design code inside the `Build()` method to enhance it with fast reload. + +Example usage is as follows: + +```csharp +public partial class ExamplePage : ContentPage, IFmgLibHotReload +{ + public ExamplePage() + { + this.InitializeHotReload(); + } + public void Build() + { + this + .Content( + new Label() + .Text("FmgLib.MauiMarkup") + .CharacterSpacing(2) + .FontSize(30) + .FontAttributes(Italic) + .TextColor(Green) + .TextCenter() + ); + } +} +``` + +And here is the definition of the `NumericValidationTriggerAction` class: + +```csharp +public class NumericValidationTriggerAction : TriggerAction +{ + protected override void Invoke(Entry entry) + { + double result; + bool isValid = Double.TryParse(entry.Text, out result); + entry.TextColor = isValid ? Colors.Black : Colors.Red; + } +} +``` + # Extensions for 3rd Party Controls FmgLib.MauiMarkup library can also generate extension methods for controls from third-party libraries. To achieve this, you should utilize the `MauiMarkupAttribute` provided by FmgLib.MauiMarkup. diff --git a/libs/FmgLib.MauiMarkup/README.md b/libs/FmgLib.MauiMarkup/README.md index 3c69666..ad4f8c0 100644 --- a/libs/FmgLib.MauiMarkup/README.md +++ b/libs/FmgLib.MauiMarkup/README.md @@ -1,10 +1,31 @@ -# **FmgLib.MauiMarkup** +# Getting Started -FmgLib.MauiMarkup is a specialized library crafted for .NET MAUI. This library allows you to code directly in C# without the necessity of employing XAML language. It provides developers with a straightforward and flexible approach to building user interfaces using C# code. With FmgLib.MauiMarkup, you can now develop application interfaces in a code-focused manner, avoiding the complexities of dealing with XAML files. This library accelerates your development process while enabling you to write more readable and manageable code. +### Creating a new FmgLib.MauiMarkup project from CLI -FmgLib.MauiMarkup provides extension methods for all properties provided for a View on the XAML side. +FmgLib provides a project template to start a new project with FmgLib.MauiMarkup. -For example: +**Install latest templates from NuGet:** +```bash +dotnet new install FmgLib.MauiMarkup.Template +``` + + +**Create a new project:** + +```bash +dotnet new fmglib-mauimarkup-app -o my-new-project +``` + +
+ +### Existing Projects + +Install the [FmgLib.MauiMarkup](https://www.nuget.org/packages/FmgLib.MauiMarkup/) NuGet package to your MAUI application. +```bash +dotnet add package FmgLib.MauiMarkup +``` + +# XAML to FmgLib.MauiMarkup(C#) If we were to write XAML code for the Image class, it would look like this: ```xaml @@ -57,13 +78,1000 @@ this ); ``` -## Extensions for 3rd Party Controls +# How to assign object references + +There are two main ways to assign objects in `FmgLib.MauiMarkup`: + +- using the `Assign` method, + +The first example uses the `Assign` method to assign a label object to a variable named label. This is done using the following code: + +```csharp +new Label().Assign(out var label); +new Entry().Assign(out var entry); +``` +Or + +```csharp +Button btnOk; + +new Button() +.Assign(out btnOk); +``` + +# Attached properties + +Attached properties are properties that are defined on a type but are intended to be used with instances of other types. In `FmgLib.MauiMarkup`, attached properties are matched with attached property fluent methods, allowing you to set their values in a more readable and fluent manner. + +For example, if you want to set the `AbsoluteLayout.LayoutBounds` attached property on a Border object, you would create an instance of Border and then use the `AbsoluteLayoutBounds` fluent method to set its value, like this: + +```csharp +new Border().AbsoluteLayoutBounds(new Rect(100, 100, 200, 200)); +``` + +This would set the `AbsoluteLayout.LayoutBounds` attached property to the specified rectangle value on the `Border` object. + +## Attached properties list + + | Maui bağlı özelliği | FmgLib.MauiMarkup metodu | + |-|-| + |`Shell.ItemTemplate`|`ShellItemTemplate()`| + |`FlyoutBase.ContextFlyout`|`ContextFlyout()`| + |`Grid.Column`|`Column()`| + |`Grid.Row`|`Row()`| + |`Grid.ColumnSpan`|`ColumnSpan()`| + |`Grid.RowSpan`|`RowSpan()`| + ||`Span(column, row)`| + |`VisualStateManager.VisualStateGroups`|`VisualStateGroups()`| + |`AbsoluteLayout.LayoutFlags`|`AbsoluteLayoutFlags()`| + |`AbsoluteLayout.LayoutBounds`|`AbsoluteLayoutBounds()`| + |`BindableLayout.EmptyView`|`BindableLayoutEmptyView()`| + |`BindableLayout.EmptyViewTemplate`|`BindableLayoutEmptyViewTemplate()`| + |`BindableLayout.ItemsSource`|`BindableLayoutItemsSource()`| + |`BindableLayout.ItemTemplate`|`BindableLayoutItemTemplate()`| + |`BindableLayout.TemplateSelector`|`BindableItemTemplateSelector()`| + |`Shell.PresentationMode`|`ShellPresentationMode()`| + |`ShellBackgroundColor`|`ShellBackgroundColor()`| + |`Shell.ForegroundColor`|`ShellForegroundColor()`| + |`Shell.TitleColor`|`ShellTitleColor()`| + |`Shell.DisabledColor`|`ShellDisabledColor()`| + |`Shell.UnselectedColor`|`ShellUnselectedColor()`| + |`Shell.NavBarHasShadow`|`ShellNavBarHasShadow()`| + |`Shell.NavBarIsVisible`|`ShellNavBarIsVisible()`| + |`Shell.TitleView`|`ShellTitleView()`| + |`NavigationPage.HasNavigationBar`|`NavigationPageHasNavigationBar()`| + |`NavigationPage.BackButtonTitle`|`NavigationPageBackButtonTitle()`| + |`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 + +In `FmgLib.MauiMarkup`, you can add functionality to user interface controls using behaviors. Behaviors allow you to add functionality to controls without having to subclass them. + +You can add a behavior to a control by using the `Behaviors` method and passing in an instance of the behavior class. For example: + +```csharp +new Entry().Text("Click Item") + .Behaviors(new YourCustomBehaviors()); +``` + +# Binding Converters + +This code is an example of how to use binding converters in `FmgLib.MauiMarkup`. + +A `CollectionView` is defined and for each item in the `MyNumbers` list, a label is created with text equal to the value of the item. The `BackgroundColor` property of the label is bound to the item using the `Convert` method, which takes in a function that converts the value of the item (an integer) to a color. In this case, the function checks if the number is even or odd, and returns either `Colors.Green` or `Colors.Yellow` based on the result. + +```csharp +public class CustomPage : ContentPage +{ + public List MyNumbers = new List { 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 + +In Maui, you can add functionality to user interface controls by handling events. For each `EventHandler` in a `FmgLib.MauiMarkup` class, a fluent helper method is generated to make it easier to attach an event handler to the control. + +For example, in the case of the `Clicked` event handler in the `Button` class, two fluent methods are generated: + +- `OnClicked(Button sender)` +- `OnClicked(object sender, EventArgs e)` + +Here's an example of how you can use the fluent helper method `OnClicked` to handle the `Clicked` event on a `Button` control: + +```csharp +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"; + } +} +``` +Or, you can use an inline function to handle the event: + +```csharp +new Button() + .Text("Click me") + .OnClicked(button => + { + count++; + button.Text = $"Clicked {count} "; + button.Text += count == 1 ? "time" : "times"; + }) +``` + +This makes it easy to attach event handlers to controls in a concise and readable way. + +# Gesture Recognizers + +The following gesture recognizers are available: + +- `TapGestureRecognizer` +- `PanGestureRecognizer` +- `PointerGestureRecognizer` + +### Tap Gesture Recognizer + +The `TapGestureRecognizer` class is used to detect tap gestures on a view. You can specify the number of taps required using the `NumberOfTapsRequired` property. + +Here's an example of using the TapGestureRecognizer to detect a double-tap gesture on an image: + +```csharp +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"; + }) + }) +) +``` + +### Pan Gesture Recognizer + +The `PanGestureRecognizer` class is used to detect pan gestures on a view. You can use the `OnPanUpdated` method to handle the pan gesture event and update the position of the view. + +Here's an example of using the `PanGestureRecognizer` to move an image on the screen: + +```csharp +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; + } + }) + }) + ) + ); + } +} +``` + +### Pointer Gesture Recognizer + +The `PointerGestureRecognizer` class is used to detect pointer events such as entering, exiting, and moving on a view. You can use the `OnPointerEntered`, `OnPointerExited`, and `OnPointerMoved` methods to handle these events and update the view accordingly. + +Here's an example of using the `PointerGestureRecognizer` to display the position of a pointer on an image: + +```csharp +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}"; + }) + }) + ) + }; + } +} +``` + +# Gradients + +`FmgLib.MauiMarkup` provides a way to create visual effects using gradient brushes in curly braces. There are two defined types of gradient brushes: + +- LinearGradientBrush +- RadialGradientBrush. + +### Example + +Here is an example of a `Border` element with a `LinearGradientBrush` as its background. The gradient effect goes from the top-left corner to the bottom-right corner. + +```csharp +new Border() +.Background( + new LinearGradientBrush() + .StartPoint(new Point(0,0)) + .EndPoint(new Point(1,1)) + .GradientStops( + new List(){ + 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 + +The `Grid` element allows you to create complex, multi-row and multi-column layout using Row and Column definitions. You can define the number and size of the rows and columns using the `RowDefinitions` and `ColumnDefinitions` methods, respectively. + +You can set the position of a child element within the grid using the `Row()`, `Column()`, `ColumnSpan()`, and `RowSpan()` methods. These methods match the attached properties `Grid.Row`, `Grid.Column`, `Grid.ColumnSpan`, and `Grid.RowSpan`, respectively. + +### Row and column definition + +Defining the number and size of rows and columns is done using the `RowDefinitions` and `ColumnDefinitions` methods, respectively. These methods take a lambda function that defines the properties for the row or column. + +In the following example, you're defining a `Grid` element with four rows and two columns: + +```csharp +new Grid() +.RowDefinitions(e => e.Star(2).Star(0.5, count: 3))) +.ColumnDefinitions(e => e.Absolute(100).Star()) +.Children( + ... +) +``` + +Here's what the code is doing: + +The `RowDefinitions` method is defining four rows with different sizes. The first row takes up 2 stars, which means it will take up twice as much vertical space as any other row in the Grid. The second, third, and fourth rows each take up 0.5 stars. The count parameter is optional and specifies how many rows of the same size should be added to the Grid. In this case, it adds 3 rows of size 0.5 stars. + +The `ColumnDefinitions` method is defining two columns. The first column is set to a fixed width of 100 pixels using the `Absolute` method, and the second column takes up the remaining space using the `Star` method. + + +### Example + +Here is a full example of a grid definition: + +```csharp +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 methods + +In `FmgLib.MauiMarkup`, all classes that implement the `ITextAlignment` interface get the following extension methods: + + - `TextCenterHorizontal` + - `TextCenterVertical` + - `TextCenter` + - `TextLeft` + - `TextRight` + - `TextBottom` + - `TextBottomLeft` + - `TextBottomRight` + - `TextTop` + - `TextTopLeft` + - `TextTopRight` + - `TextTopCenter` + - `TextBottomCenter` + - `TextCenterLeft` + - `TextCenterRight` + - `AlignText` + +## Usage + +To use the extension methods, create a `Label` object (or any object that implements `ITextAlignment`), and call the desired method: + +```csharp +new Label().TextCenter() +``` + +This example centers the text both horizontally and vertically within the label's containing element. + +# Layout options + +In `FmgLib.MauiMarkup`, you can layout every view in their container using the following extension methods: + + - `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` + +## Usage + +To use the layout options, create a container view , add the view you want to layout to the container, and call the desired method: + +```csharp +new StackLayout() +.Children( + new Label().Text("Hello, World!").Center() +) +``` + +This example centers a Label inside a `StackLayout` container. You can use the same method with other container views, and with any view that you want to lay out within its containing element. + +# Menus in FmgLib.MauiMarkup + +### Context menu + +Here is an example of creating a context menu for an image. The context menu has options for copying and pasting, and also for changing the background color of a grid. + +```csharp +new Grid() +.Assign(out var grid) +.Children( + new Image() + .Source("dotnet_bot.png") + .ContextFlyout(new MenuFlyout() + { + new MenuFlyoutItem() + .Text("Copy") + .OnClicked(e => Console.WriteLine("Copy")), + new MenuFlyoutItem() + .Text("Paste") + .OnClicked(e => Console.WriteLine("Paste")), + new MenuFlyoutSubItem() + .Text("Background color") + { + new MenuFlyoutItem() + .Text("Blue") + .OnClicked(e => grid.BackgroundColor = Colors.Blue), + new MenuFlyoutItem() + .Text("Red") + .OnClicked(e => grid.BackgroundColor = Colors.Red), + new MenuFlyoutItem() + .Text("Black") + .OnClicked(e => grid.BackgroundColor = Colors.Black) + } + } + ) +) +``` + +### Menu bar + +Here is an example of creating a menu bar for a `ContentPage`. The menu bar has three options: My Menu, Edit, and Theme. + +```csharp +public class MenuPage : ContentPage +{ + public MenuPage() + { + this.MenuBarItems(new MenuBarItem[] + { + new MenuBarItem() + .Text("My Menu") + { + new MenuFlyoutItem() + .Text("Exit") + .OnClicked(e => Application.Current.Quit()), + }, + new MenuBarItem() + .Text("Edit") + { + new MenuFlyoutItem() + .Text("Copy") + .OnClicked(e => Console.WriteLine("Copy")), + new MenuFlyoutItem() + .Text("Paste") + .OnClicked(e => Console.WriteLine("Paste")), + }, + new MenuBarItem() + .Text("Theme") + { + new MenuFlyoutItem() + .Text("Blue") + .OnClicked(e => this.BackgroundColor = Colors.Blue), + ... + } + }); + + ... + } +} +``` + +# Properties and Fluent Methods + +`FmgLib.MauiMarkup` provides a convenient way to set properties for UI elements by matching properties with fluent helper methods. This makes it easier and more readable to define the interface of your application. + +Here is an example of using fluent methods to set properties on a `Label`: + +```csharp +new Label() + .Text("This is a test") + .Padding(20) + .FontSize(30) + .Center()) +``` + + +`FmgLib.MauiMarkup` also provides a way to set property values based on device idiom, platform, or app theme. Here is an example of setting the font size and text color of a `Label` based on the current device or theme: + +```csharp +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` provides a simple way to bind properties of an element to a source, so that when the source changes, the property changes as well. You can bind a property by using the fluent method e.g. `Text()`, `TextColor()` etc. and then using lambda call the method `Path()` to specify the property you want to bind to. + + +```csharp +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) + ) + ); + } +} +``` + +In this example, the text property of the label is bound to the `Value` property of a `Slider` element named `slider`. When the value of the slider changes, the text of the label will automatically update to reflect the new value. + +You can also bind a property to an object that is not part of the visual tree. This is useful when you have a separate data source, such as a model or a view model, that you want to bind to a visual element. + +# Shell Application + +Here's an example of a simple shell-based application: + +```csharp +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()), + ... + ) + ) + ); + } +} +``` + +You can customize the appearance of the `FlyoutItem` by defining a custom content view and setting the `ItemTemplate` property on the `Shell` element. + +Here's an example of defining the appearance of a `FlyoutItem`: + +```csharp +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() + ) + ); + } +} +``` + +# Application Styling + +`FmgLib.MauiMarkup` provides a way to define the styles of elements using the `Style` class. Here's an example of how to define the style of a button: + +```csharp +new Style