# Handler Customization / Tùy Biến Handler

## Overview / Tổng Quan

.NET MAUI sử dụng **Handlers** thay vì Custom Renderers (Xamarin.Forms legacy). Handlers cung cấp cách tiếp cận hiện đại, hiệu quả hơn để tùy chỉnh native controls.

```
┌─────────────────────────────────────────────────────────────┐
│                    HANDLER ARCHITECTURE                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌──────────────┐                  ┌──────────────────┐   │
│   │  MAUI View   │ ─── Handler ───► │   Native View    │   │
│   │   (Entry)    │                  │ (EditText/UITxt) │   │
│   └──────────────┘                  └──────────────────┘   │
│          │                                   │              │
│          │    PropertyMapper                 │              │
│          └──────────────────────────────────►│              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
```

---

## Customization Levels / Các Cấp Độ Tùy Biến

### Level 1: XAML Styles (Ưu tiên cao nhất)

Dùng cho thay đổi properties cơ bản:

```xml
<Style TargetType="Entry" x:Key="BrandEntry">
    <Setter Property="BackgroundColor" Value="{StaticResource SurfaceCard}" />
    <Setter Property="TextColor" Value="{StaticResource TextPrimary}" />
    <Setter Property="PlaceholderColor" Value="{StaticResource TextSecondary}" />
</Style>
```

### Level 2: ControlTemplate

Dùng khi cần thay đổi cấu trúc visual:

```xml
<ControlTemplate x:Key="RoundedEntryTemplate">
    <Border StrokeThickness="1"
            Stroke="{StaticResource BorderDefault}"
            StrokeShape="RoundRectangle 8">
        <ContentPresenter Padding="12,8" />
    </Border>
</ControlTemplate>

<!-- Usage -->
<Entry ControlTemplate="{StaticResource RoundedEntryTemplate}" />
```

### Level 3: Handlers (Khi cần Native API)

Dùng khi XAML không đủ (bỏ underline, đổi cursor color, etc.):

```csharp
// ⚠️ CHỈ dùng khi cần thiết
EntryHandler.Mapper.AppendToMapping("NoBorder", (handler, view) =>
{
#if ANDROID
    handler.PlatformView.SetBackgroundColor(Android.Graphics.Color.Transparent);
#elif IOS
    handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
#endif
});
```

---

## Handler Mapping Methods

### AppendToMapping (Recommended)

Thêm customization **sau** default mapping:

```csharp
public static class EntryHandlerExtensions
{
    public static void ConfigureBrandEntry(this MauiAppBuilder builder)
    {
        EntryHandler.Mapper.AppendToMapping("BrandEntry", (handler, view) =>
        {
#if ANDROID
            // Bỏ underline mặc định
            handler.PlatformView.SetBackgroundColor(Android.Graphics.Color.Transparent);
            
            // Đổi màu cursor
            if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Q)
            {
                handler.PlatformView.SetTextCursorDrawable(null);
            }
#elif IOS
            // Bỏ border
            handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
#endif
        });
    }
}

// MauiProgram.cs
public static MauiApp CreateMauiApp()
{
    var builder = MauiApp.CreateBuilder();
    builder.ConfigureBrandEntry();
    return builder.Build();
}
```

### PrependToMapping

Thêm customization **trước** default mapping:

```csharp
// Ít dùng - chỉ khi cần override trước
EntryHandler.Mapper.PrependToMapping("BeforeDefault", (handler, view) =>
{
    // Code chạy TRƯỚC default mapping
});
```

### ModifyMapping (Nguy hiểm)

Thay thế hoàn toàn một property mapping:

```csharp
// ⚠️ NGUY HIỂM - có thể break default behavior
EntryHandler.Mapper.ModifyMapping(nameof(Entry.Text), (handler, view, action) =>
{
    // Thay thế hoàn toàn cách xử lý Text property
    action?.Invoke(handler, view);
});
```

---

## Complete Handler Examples

### BrandEntry Handler (Bỏ Underline)

```csharp
// Handlers/BrandEntryHandler.cs
namespace MyApp.Handlers;

public static class BrandEntryHandler
{
    /// <summary>
    /// Configure Entry để bỏ underline mặc định trên Android
    /// và bỏ border trên iOS
    /// </summary>
    public static void Configure()
    {
        Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(
            "BrandEntry", 
            (handler, view) =>
        {
#if ANDROID
            ConfigureAndroid(handler);
#elif IOS || MACCATALYST
            ConfigureIOS(handler);
#endif
        });
    }

#if ANDROID
    private static void ConfigureAndroid(
        Microsoft.Maui.Handlers.EntryHandler handler)
    {
        // Bỏ background/underline mặc định
        handler.PlatformView.SetBackgroundColor(
            Android.Graphics.Color.Transparent);
        
        // Optional: Select all on focus
        handler.PlatformView.SetSelectAllOnFocus(true);
    }
#endif

#if IOS || MACCATALYST
    private static void ConfigureIOS(
        Microsoft.Maui.Handlers.EntryHandler handler)
    {
        // Bỏ border
        handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
        
        // Optional: Clear button
        handler.PlatformView.ClearButtonMode = 
            UIKit.UITextFieldViewMode.WhileEditing;
    }
#endif
}
```

### BrandPicker Handler (Custom Arrow)

```csharp
namespace MyApp.Handlers;

public static class BrandPickerHandler
{
    public static void Configure()
    {
        Microsoft.Maui.Handlers.PickerHandler.Mapper.AppendToMapping(
            "BrandPicker",
            (handler, view) =>
        {
#if ANDROID
            // Bỏ underline
            handler.PlatformView.SetBackgroundColor(
                Android.Graphics.Color.Transparent);
#elif IOS
            // Custom styling
            handler.PlatformView.BorderStyle = 
                UIKit.UITextBorderStyle.None;
#endif
        });
    }
}
```

### BrandEditor Handler (Multi-line Text)

```csharp
namespace MyApp.Handlers;

public static class BrandEditorHandler
{
    public static void Configure()
    {
        Microsoft.Maui.Handlers.EditorHandler.Mapper.AppendToMapping(
            "BrandEditor",
            (handler, view) =>
        {
#if ANDROID
            // Bỏ background
            handler.PlatformView.SetBackgroundColor(
                Android.Graphics.Color.Transparent);
            
            // Set max lines
            handler.PlatformView.SetMaxLines(5);
#elif IOS
            // Customize TextView
            handler.PlatformView.Layer.BorderWidth = 0;
#endif
        });
    }
}
```

---

## Cross-Platform Partial Classes

Khi code native dài, tách thành Partial Classes:

### Shared Interface

```csharp
// Services/IBrandingService.cs
namespace MyApp.Services;

public interface IBrandingService
{
    void ConfigureEntry(object platformView);
    void ConfigurePicker(object platformView);
}
```

### Partial Implementation

```csharp
// Services/BrandingService.cs (shared)
namespace MyApp.Services;

public partial class BrandingService : IBrandingService
{
    public partial void ConfigureEntry(object platformView);
    public partial void ConfigurePicker(object platformView);
}
```

```csharp
// Platforms/Android/Services/BrandingService.Android.cs
namespace MyApp.Services;

public partial class BrandingService
{
    public partial void ConfigureEntry(object platformView)
    {
        if (platformView is AndroidX.AppCompat.Widget.AppCompatEditText editText)
        {
            editText.SetBackgroundColor(Android.Graphics.Color.Transparent);
            editText.SetSelectAllOnFocus(true);
        }
    }
    
    public partial void ConfigurePicker(object platformView)
    {
        if (platformView is AndroidX.AppCompat.Widget.AppCompatEditText editText)
        {
            editText.SetBackgroundColor(Android.Graphics.Color.Transparent);
        }
    }
}
```

```csharp
// Platforms/iOS/Services/BrandingService.iOS.cs
namespace MyApp.Services;

public partial class BrandingService
{
    public partial void ConfigureEntry(object platformView)
    {
        if (platformView is UIKit.UITextField textField)
        {
            textField.BorderStyle = UIKit.UITextBorderStyle.None;
            textField.ClearButtonMode = UIKit.UITextFieldViewMode.WhileEditing;
        }
    }
    
    public partial void ConfigurePicker(object platformView)
    {
        if (platformView is UIKit.UITextField textField)
        {
            textField.BorderStyle = UIKit.UITextBorderStyle.None;
        }
    }
}
```

---

## MauiProgram.cs Integration

```csharp
using MyApp.Handlers;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .UseMauiCommunityToolkit()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("Inter-Regular.ttf", "InterRegular");
                fonts.AddFont("Inter-Bold.ttf", "InterBold");
            });
        
        // ✅ Configure all brand handlers
        ConfigureBrandHandlers();
        
        return builder.Build();
    }
    
    private static void ConfigureBrandHandlers()
    {
        BrandEntryHandler.Configure();
        BrandPickerHandler.Configure();
        BrandEditorHandler.Configure();
    }
}
```

---

## Common Mistakes / Lỗi Thường Gặp

### ❌ DON'T: Use Custom Renderers

```csharp
// ❌ LEGACY - Không dùng nữa
[assembly: ExportRenderer(typeof(Entry), typeof(CustomEntryRenderer))]
public class CustomEntryRenderer : EntryRenderer
{
    // Old Xamarin.Forms approach
}
```

### ✅ DO: Use Handlers

```csharp
// ✅ MODERN - .NET MAUI approach
EntryHandler.Mapper.AppendToMapping("Custom", (h, v) => { });
```

### ❌ DON'T: Use `#if` everywhere

```csharp
// ❌ Code pollution
public void Configure()
{
#if ANDROID
    // 50 lines of Android code
#elif IOS
    // 50 lines of iOS code
#elif WINDOWS
    // 50 lines of Windows code
#endif
}
```

### ✅ DO: Use Partial Classes

```csharp
// ✅ Clean separation
// Services/MyService.cs
public partial class MyService
{
    public partial void DoPlatformWork();
}

// Platforms/Android/Services/MyService.Android.cs
public partial class MyService
{
    public partial void DoPlatformWork()
    {
        // Android-specific
    }
}
```

---

## Quick Reference

| Task | Method |
|------|--------|
| Thay đổi màu/font | XAML Styles |
| Thay đổi cấu trúc UI | ControlTemplate |
| Bỏ underline Entry | Handler + AppendToMapping |
| Đổi cursor color | Handler + Native API |
| Thêm clear button (iOS) | Handler + UITextField |
| Custom dropdown arrow | Handler + Native Drawable |

---

## Related Guidelines

- [Resource Architecture](./resource-architecture.md) - XAML Resources
- [Accessibility Rules](./accessibility-rules.md) - WCAG compliance
