Skip to content

cheatsheetz/xamarin

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 

Repository files navigation

Xamarin Cheat Sheet

Quick Reference

Xamarin is a Microsoft platform for building cross-platform mobile applications using C# and .NET, allowing code sharing between iOS, Android, and Windows platforms.

Setup and Installation

# Install Visual Studio with Xamarin workload
# Or Visual Studio for Mac

# Install Xamarin through Visual Studio Installer:
# - Mobile development with .NET workload
# - Android SDK, NDK, and Java development tools

# Create new project
# File -> New -> Project -> Mobile -> Cross-Platform -> Xamarin.Forms

# NuGet Package Manager
Install-Package Xamarin.Forms
Install-Package Xamarin.Essentials

C# Fundamentals for Xamarin

Basic Syntax

// Variables and types
string name = "Xamarin";
int count = 42;
double price = 99.99;
bool isActive = true;
var inferredType = "Hello World"; // string

// Nullable types
string? nullableString = null;
int? nullableInt = null;

// Collections
List<string> items = new List<string> { "Item1", "Item2", "Item3" };
Dictionary<string, object> data = new Dictionary<string, object>
{
    { "name", "John" },
    { "age", 30 }
};

// Arrays
string[] names = { "Alice", "Bob", "Charlie" };
int[] numbers = new int[5] { 1, 2, 3, 4, 5 };

Classes and Interfaces

// Interface
public interface IDataService
{
    Task<List<User>> GetUsersAsync();
    Task<User> GetUserAsync(int id);
}

// Class implementation
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public DateTime CreatedAt { get; set; } = DateTime.Now;
    
    // Constructor
    public User(string name, string email)
    {
        Name = name;
        Email = email;
    }
    
    // Methods
    public string GetDisplayName()
    {
        return $"{Name} ({Email})";
    }
    
    public override string ToString()
    {
        return GetDisplayName();
    }
}

// Inheritance
public class Employee : User
{
    public string Department { get; set; }
    public decimal Salary { get; set; }
    
    public Employee(string name, string email, string department) 
        : base(name, email)
    {
        Department = department;
    }
    
    public override string GetDisplayName()
    {
        return $"{Name} - {Department}";
    }
}

// Generic class
public class Repository<T> where T : class
{
    private readonly List<T> _items = new List<T>();
    
    public void Add(T item) => _items.Add(item);
    public List<T> GetAll() => _items.ToList();
    public T GetById(Func<T, bool> predicate) => _items.FirstOrDefault(predicate);
}

Async/Await

// Async method
public async Task<List<User>> GetUsersAsync()
{
    using var httpClient = new HttpClient();
    var response = await httpClient.GetStringAsync("https://api.example.com/users");
    var users = JsonConvert.DeserializeObject<List<User>>(response);
    return users;
}

// Calling async methods
public async Task LoadUsersAsync()
{
    try
    {
        var users = await GetUsersAsync();
        // Update UI
        UserList = users;
    }
    catch (Exception ex)
    {
        // Handle error
        await DisplayAlert("Error", ex.Message, "OK");
    }
}

// Parallel execution
public async Task<(List<User> users, List<Product> products)> LoadDataAsync()
{
    var usersTask = GetUsersAsync();
    var productsTask = GetProductsAsync();
    
    await Task.WhenAll(usersTask, productsTask);
    
    return (await usersTask, await productsTask);
}

Xamarin.Forms

Basic App Structure

// App.xaml.cs
public partial class App : Application
{
    public App()
    {
        InitializeComponent();
        
        // Set main page
        MainPage = new NavigationPage(new MainPage());
    }
    
    protected override void OnStart()
    {
        // Handle when your app starts
    }
    
    protected override void OnSleep()
    {
        // Handle when your app sleeps
    }
    
    protected override void OnResume()
    {
        // Handle when your app resumes
    }
}

// MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MyApp.MainPage"
             Title="Home">
    
    <StackLayout Padding="20" Spacing="20">
        <Label x:Name="WelcomeLabel" 
               Text="Welcome to Xamarin.Forms!" 
               FontSize="24"
               HorizontalOptions="Center" />
        
        <Entry x:Name="NameEntry" 
               Placeholder="Enter your name" />
        
        <Button x:Name="SubmitButton"
                Text="Submit"
                BackgroundColor="Blue"
                TextColor="White"
                Clicked="OnSubmitClicked" />
        
        <ListView x:Name="ItemsListView"
                  ItemsSource="{Binding Items}">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding Name}" 
                              Detail="{Binding Description}" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

// MainPage.xaml.cs
public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
        BindingContext = new MainPageViewModel();
    }
    
    private async void OnSubmitClicked(object sender, EventArgs e)
    {
        if (!string.IsNullOrEmpty(NameEntry.Text))
        {
            WelcomeLabel.Text = $"Hello, {NameEntry.Text}!";
            NameEntry.Text = "";
        }
        else
        {
            await DisplayAlert("Error", "Please enter your name", "OK");
        }
    }
}

Layouts

<!-- StackLayout - Sequential arrangement -->
<StackLayout Orientation="Vertical" Spacing="10" Padding="20">
    <Label Text="Item 1" />
    <Label Text="Item 2" />
    <Label Text="Item 3" />
</StackLayout>

<!-- Grid Layout - Table-like arrangement -->
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
        <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="*" />
        <ColumnDefinition Width="2*" />
    </Grid.ColumnDefinitions>
    
    <Label Grid.Row="0" Grid.Column="0" Text="Name:" />
    <Entry Grid.Row="0" Grid.Column="1" Text="{Binding Name}" />
    
    <Label Grid.Row="1" Grid.Column="0" Text="Description:" />
    <Editor Grid.Row="1" Grid.Column="1" Text="{Binding Description}" />
    
    <Button Grid.Row="2" Grid.ColumnSpan="2" Text="Save" />
</Grid>

<!-- AbsoluteLayout - Precise positioning -->
<AbsoluteLayout>
    <Label Text="Top Left" 
           AbsoluteLayout.LayoutBounds="0,0,100,50" 
           AbsoluteLayout.LayoutFlags="None" />
    <Label Text="Center" 
           AbsoluteLayout.LayoutBounds="0.5,0.5,100,50" 
           AbsoluteLayout.LayoutFlags="PositionProportional" />
</AbsoluteLayout>

<!-- RelativeLayout - Relative positioning -->
<RelativeLayout>
    <Label x:Name="titleLabel"
           Text="Title"
           RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent, Property=Width, Factor=0.5}"
           RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height, Factor=0.1}" />
    
    <Entry RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView, ElementName=titleLabel, Property=X}"
           RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=titleLabel, Property=Y, Constant=50}" />
</RelativeLayout>

Controls and Views

<!-- Text Controls -->
<Label Text="Hello World" 
       FontSize="18" 
       TextColor="Blue" 
       HorizontalOptions="Center" />

<Entry Placeholder="Enter text" 
       Text="{Binding InputText}" 
       Keyboard="Email" />

<Editor Text="{Binding LongText}" 
        HeightRequest="100" />

<!-- Button -->
<Button Text="Click Me" 
        Command="{Binding ClickCommand}" 
        BackgroundColor="Blue" 
        TextColor="White" 
        CornerRadius="5" />

<!-- Image -->
<Image Source="icon.png" 
       Aspect="AspectFit" 
       HeightRequest="100" />

<Image Source="{Binding ImageUrl}" 
       LoadingPlaceholder="loading.png" 
       ErrorPlaceholder="error.png" />

<!-- Switch and Slider -->
<Switch IsToggled="{Binding IsEnabled}" />
<Slider Minimum="0" Maximum="100" Value="{Binding SliderValue}" />
<Stepper Minimum="0" Maximum="10" Value="{Binding StepperValue}" />

<!-- Progress -->
<ProgressBar Progress="{Binding ProgressValue}" />
<ActivityIndicator IsRunning="{Binding IsLoading}" />

<!-- Picker -->
<Picker Title="Select an option" 
        ItemsSource="{Binding Options}" 
        SelectedItem="{Binding SelectedOption}" />

<!-- DatePicker and TimePicker -->
<DatePicker Date="{Binding SelectedDate}" />
<TimePicker Time="{Binding SelectedTime}" />

<!-- ListView -->
<ListView ItemsSource="{Binding Items}" 
          SelectedItem="{Binding SelectedItem}"
          HasUnevenRows="True">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <StackLayout Padding="15,5">
                    <Label Text="{Binding Title}" FontAttributes="Bold" />
                    <Label Text="{Binding Subtitle}" FontSize="Small" TextColor="Gray" />
                </StackLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

<!-- CollectionView (newer alternative to ListView) -->
<CollectionView ItemsSource="{Binding Items}">
    <CollectionView.ItemTemplate>
        <DataTemplate>
            <Grid Padding="10">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>
                <Label Grid.Row="0" Text="{Binding Title}" FontAttributes="Bold" />
                <Label Grid.Row="1" Text="{Binding Description}" FontSize="Small" />
            </Grid>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>

MVVM Pattern

ViewModel Base

using System.ComponentModel;
using System.Runtime.CompilerServices;

public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected bool SetProperty<T>(ref T backingStore, T value, [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(backingStore, value))
            return false;

        backingStore = value;
        OnPropertyChanged(propertyName);
        return true;
    }
}

// Command implementation
public class RelayCommand : ICommand
{
    private readonly Action _execute;
    private readonly Func<bool> _canExecute;

    public RelayCommand(Action execute, Func<bool> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter) => _canExecute?.Invoke() ?? true;
    
    public void Execute(object parameter) => _execute();
}

public class RelayCommand<T> : ICommand
{
    private readonly Action<T> _execute;
    private readonly Predicate<T> _canExecute;

    public RelayCommand(Action<T> execute, Predicate<T> canExecute = null)
    {
        _execute = execute ?? throw new ArgumentNullException(nameof(execute));
        _canExecute = canExecute;
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public bool CanExecute(object parameter) => _canExecute?.Invoke((T)parameter) ?? true;
    
    public void Execute(object parameter) => _execute((T)parameter);
}

Sample ViewModel

public class UserListViewModel : BaseViewModel
{
    private readonly IUserService _userService;
    private ObservableCollection<User> _users;
    private User _selectedUser;
    private bool _isLoading;
    private string _searchText;

    public UserListViewModel(IUserService userService)
    {
        _userService = userService;
        Users = new ObservableCollection<User>();
        
        LoadUsersCommand = new RelayCommand(async () => await LoadUsersAsync());
        SearchCommand = new RelayCommand(async () => await SearchUsersAsync());
        RefreshCommand = new RelayCommand(async () => await RefreshUsersAsync());
        
        // Load initial data
        Task.Run(async () => await LoadUsersAsync());
    }

    public ObservableCollection<User> Users
    {
        get => _users;
        set => SetProperty(ref _users, value);
    }

    public User SelectedUser
    {
        get => _selectedUser;
        set
        {
            SetProperty(ref _selectedUser, value);
            if (value != null)
            {
                NavigateToUserDetail(value);
            }
        }
    }

    public bool IsLoading
    {
        get => _isLoading;
        set => SetProperty(ref _isLoading, value);
    }

    public string SearchText
    {
        get => _searchText;
        set => SetProperty(ref _searchText, value);
    }

    public ICommand LoadUsersCommand { get; }
    public ICommand SearchCommand { get; }
    public ICommand RefreshCommand { get; }

    private async Task LoadUsersAsync()
    {
        try
        {
            IsLoading = true;
            var users = await _userService.GetUsersAsync();
            
            Users.Clear();
            foreach (var user in users)
            {
                Users.Add(user);
            }
        }
        catch (Exception ex)
        {
            await Application.Current.MainPage.DisplayAlert("Error", ex.Message, "OK");
        }
        finally
        {
            IsLoading = false;
        }
    }

    private async Task SearchUsersAsync()
    {
        if (string.IsNullOrWhiteSpace(SearchText))
        {
            await LoadUsersAsync();
            return;
        }

        try
        {
            IsLoading = true;
            var users = await _userService.SearchUsersAsync(SearchText);
            
            Users.Clear();
            foreach (var user in users)
            {
                Users.Add(user);
            }
        }
        catch (Exception ex)
        {
            await Application.Current.MainPage.DisplayAlert("Error", ex.Message, "OK");
        }
        finally
        {
            IsLoading = false;
        }
    }

    private async Task RefreshUsersAsync()
    {
        await LoadUsersAsync();
    }

    private async void NavigateToUserDetail(User user)
    {
        await Shell.Current.GoToAsync($"userdetail?id={user.Id}");
    }
}

Navigation

Shell Navigation

<!-- AppShell.xaml -->
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       x:Class="MyApp.AppShell">

    <!-- Tab Bar -->
    <TabBar>
        <ShellContent Title="Home" 
                      Icon="home.png" 
                      ContentTemplate="{DataTemplate views:HomePage}" />
        
        <ShellContent Title="Users" 
                      Icon="users.png" 
                      ContentTemplate="{DataTemplate views:UsersPage}" />
        
        <ShellContent Title="Settings" 
                      Icon="settings.png" 
                      ContentTemplate="{DataTemplate views:SettingsPage}" />
    </TabBar>

    <!-- Flyout Menu -->
    <FlyoutItem Title="Dashboard">
        <ShellContent ContentTemplate="{DataTemplate views:DashboardPage}" />
    </FlyoutItem>
    
    <FlyoutItem Title="Profile">
        <ShellContent ContentTemplate="{DataTemplate views:ProfilePage}" />
    </FlyoutItem>

</Shell>
// AppShell.xaml.cs
public partial class AppShell : Shell
{
    public AppShell()
    {
        InitializeComponent();
        
        // Register routes
        Routing.RegisterRoute("userdetail", typeof(UserDetailPage));
        Routing.RegisterRoute("edituser", typeof(EditUserPage));
    }
}

// Navigation in ViewModels
public class MainViewModel : BaseViewModel
{
    private async Task NavigateToUserDetailAsync(int userId)
    {
        await Shell.Current.GoToAsync($"userdetail?id={userId}");
    }
    
    private async Task GoBackAsync()
    {
        await Shell.Current.GoToAsync("..");
    }
    
    private async Task NavigateAndPassDataAsync()
    {
        var user = new User { Id = 1, Name = "John" };
        await Shell.Current.GoToAsync("userdetail", new Dictionary<string, object>
        {
            { "User", user }
        });
    }
}

// Receiving navigation parameters
[QueryProperty(nameof(UserId), "id")]
public partial class UserDetailPage : ContentPage
{
    private int _userId;
    
    public int UserId
    {
        get => _userId;
        set
        {
            _userId = value;
            LoadUser(value);
        }
    }
    
    private async void LoadUser(int userId)
    {
        var user = await _userService.GetUserAsync(userId);
        BindingContext = new UserDetailViewModel(user);
    }
}

Traditional Navigation

// Push/Pop navigation
public async Task NavigateToPageAsync()
{
    await Navigation.PushAsync(new UserDetailPage());
}

public async Task NavigateBackAsync()
{
    await Navigation.PopAsync();
}

// Modal navigation
public async Task ShowModalAsync()
{
    await Navigation.PushModalAsync(new EditUserPage());
}

public async Task CloseModalAsync()
{
    await Navigation.PopModalAsync();
}

// Navigation with data
public async Task NavigateWithDataAsync(User user)
{
    var detailPage = new UserDetailPage();
    detailPage.BindingContext = new UserDetailViewModel(user);
    await Navigation.PushAsync(detailPage);
}

Data Access and Storage

HTTP Client

public class ApiService
{
    private readonly HttpClient _httpClient;
    private readonly string _baseUrl = "https://api.example.com";

    public ApiService()
    {
        _httpClient = new HttpClient();
        _httpClient.DefaultRequestHeaders.Add("Accept", "application/json");
    }

    public async Task<List<User>> GetUsersAsync()
    {
        var response = await _httpClient.GetStringAsync($"{_baseUrl}/users");
        return JsonConvert.DeserializeObject<List<User>>(response);
    }

    public async Task<User> GetUserAsync(int id)
    {
        var response = await _httpClient.GetStringAsync($"{_baseUrl}/users/{id}");
        return JsonConvert.DeserializeObject<User>(response);
    }

    public async Task<User> CreateUserAsync(User user)
    {
        var json = JsonConvert.SerializeObject(user);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        
        var response = await _httpClient.PostAsync($"{_baseUrl}/users", content);
        response.EnsureSuccessStatusCode();
        
        var responseJson = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<User>(responseJson);
    }

    public async Task<bool> UpdateUserAsync(int id, User user)
    {
        var json = JsonConvert.SerializeObject(user);
        var content = new StringContent(json, Encoding.UTF8, "application/json");
        
        var response = await _httpClient.PutAsync($"{_baseUrl}/users/{id}", content);
        return response.IsSuccessStatusCode;
    }

    public async Task<bool> DeleteUserAsync(int id)
    {
        var response = await _httpClient.DeleteAsync($"{_baseUrl}/users/{id}");
        return response.IsSuccessStatusCode;
    }
}

SQLite Database

// Install SQLite NuGet package: sqlite-net-pcl

// Data model
public class User
{
    [PrimaryKey, AutoIncrement]
    public int Id { get; set; }
    
    [MaxLength(100)]
    public string Name { get; set; }
    
    [MaxLength(200)]
    public string Email { get; set; }
    
    public DateTime CreatedAt { get; set; }
}

// Database service
public class DatabaseService
{
    private SQLiteAsyncConnection _database;

    public DatabaseService(string dbPath)
    {
        _database = new SQLiteAsyncConnection(dbPath);
        _database.CreateTableAsync<User>().Wait();
    }

    public async Task<List<User>> GetUsersAsync()
    {
        return await _database.Table<User>().ToListAsync();
    }

    public async Task<User> GetUserAsync(int id)
    {
        return await _database.Table<User>()
            .Where(u => u.Id == id)
            .FirstOrDefaultAsync();
    }

    public async Task<int> SaveUserAsync(User user)
    {
        if (user.Id != 0)
        {
            return await _database.UpdateAsync(user);
        }
        else
        {
            return await _database.InsertAsync(user);
        }
    }

    public async Task<int> DeleteUserAsync(User user)
    {
        return await _database.DeleteAsync(user);
    }
}

// Usage in App.xaml.cs
public partial class App : Application
{
    static DatabaseService database;

    public static DatabaseService Database
    {
        get
        {
            if (database == null)
            {
                database = new DatabaseService(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Users.db3"));
            }
            return database;
        }
    }
}

Preferences (Settings)

// Save settings
Preferences.Set("username", "john_doe");
Preferences.Set("user_id", 123);
Preferences.Set("is_logged_in", true);
Preferences.Set("theme", "dark");

// Read settings
var username = Preferences.Get("username", "");
var userId = Preferences.Get("user_id", 0);
var isLoggedIn = Preferences.Get("is_logged_in", false);
var theme = Preferences.Get("theme", "light");

// Check if key exists
if (Preferences.ContainsKey("username"))
{
    // Key exists
}

// Remove setting
Preferences.Remove("username");

// Clear all settings
Preferences.Clear();

// Save complex objects
var settings = new AppSettings { Theme = "Dark", Notifications = true };
var json = JsonConvert.SerializeObject(settings);
Preferences.Set("app_settings", json);

// Read complex objects
var settingsJson = Preferences.Get("app_settings", "");
if (!string.IsNullOrEmpty(settingsJson))
{
    var settings = JsonConvert.DeserializeObject<AppSettings>(settingsJson);
}

Platform-specific Code

Dependency Service

// Interface
public interface IFileService
{
    Task<string> ReadTextFileAsync(string filename);
    Task WriteTextFileAsync(string filename, string content);
    string GetLocalFilePath(string filename);
}

// Android implementation (in Android project)
[assembly: Dependency(typeof(FileService))]
namespace MyApp.Droid.Services
{
    public class FileService : IFileService
    {
        public string GetLocalFilePath(string filename)
        {
            string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
            return Path.Combine(path, filename);
        }

        public async Task<string> ReadTextFileAsync(string filename)
        {
            string path = GetLocalFilePath(filename);
            if (File.Exists(path))
            {
                return await File.ReadAllTextAsync(path);
            }
            return string.Empty;
        }

        public async Task WriteTextFileAsync(string filename, string content)
        {
            string path = GetLocalFilePath(filename);
            await File.WriteAllTextAsync(path, content);
        }
    }
}

// iOS implementation (in iOS project)
[assembly: Dependency(typeof(FileService))]
namespace MyApp.iOS.Services
{
    public class FileService : IFileService
    {
        public string GetLocalFilePath(string filename)
        {
            string docFolder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
            string libFolder = Path.Combine(docFolder, "..", "Library", "Databases");
            
            if (!Directory.Exists(libFolder))
            {
                Directory.CreateDirectory(libFolder);
            }
            
            return Path.Combine(libFolder, filename);
        }

        public async Task<string> ReadTextFileAsync(string filename)
        {
            string path = GetLocalFilePath(filename);
            if (File.Exists(path))
            {
                return await File.ReadAllTextAsync(path);
            }
            return string.Empty;
        }

        public async Task WriteTextFileAsync(string filename, string content)
        {
            string path = GetLocalFilePath(filename);
            await File.WriteAllTextAsync(path, content);
        }
    }
}

// Usage in shared code
public class DataManager
{
    private readonly IFileService _fileService;

    public DataManager()
    {
        _fileService = DependencyService.Get<IFileService>();
    }

    public async Task SaveDataAsync(string data)
    {
        await _fileService.WriteTextFileAsync("data.txt", data);
    }

    public async Task<string> LoadDataAsync()
    {
        return await _fileService.ReadTextFileAsync("data.txt");
    }
}

Platform-specific XAML

<!-- Use OnPlatform for platform-specific values -->
<Label Text="Hello World">
    <Label.FontSize>
        <OnPlatform x:TypeArguments="x:Double">
            <On Platform="iOS" Value="16" />
            <On Platform="Android" Value="18" />
            <On Platform="UWP" Value="14" />
        </OnPlatform>
    </Label.FontSize>
</Label>

<!-- Platform-specific styling -->
<Button Text="Click Me">
    <Button.BackgroundColor>
        <OnPlatform x:TypeArguments="Color">
            <On Platform="iOS" Value="Blue" />
            <On Platform="Android" Value="Green" />
        </OnPlatform>
    </Button.BackgroundColor>
</Button>

<!-- Device-specific layouts -->
<ContentPage.Content>
    <OnPlatform x:TypeArguments="View">
        <On Platform="iOS">
            <StackLayout BackgroundColor="LightBlue">
                <Label Text="iOS Layout" />
            </StackLayout>
        </On>
        <On Platform="Android">
            <Grid BackgroundColor="LightGreen">
                <Label Text="Android Layout" />
            </Grid>
        </On>
    </OnPlatform>
</ContentPage.Content>

Testing

Unit Testing

// NUnit test example
[TestFixture]
public class UserServiceTests
{
    private UserService _userService;
    private Mock<IHttpService> _httpServiceMock;

    [SetUp]
    public void Setup()
    {
        _httpServiceMock = new Mock<IHttpService>();
        _userService = new UserService(_httpServiceMock.Object);
    }

    [Test]
    public async Task GetUsersAsync_ReturnsUsersList()
    {
        // Arrange
        var expectedUsers = new List<User>
        {
            new User { Id = 1, Name = "John" },
            new User { Id = 2, Name = "Jane" }
        };
        
        _httpServiceMock.Setup(x => x.GetAsync<List<User>>("users"))
            .ReturnsAsync(expectedUsers);

        // Act
        var result = await _userService.GetUsersAsync();

        // Assert
        Assert.AreEqual(expectedUsers.Count, result.Count);
        Assert.AreEqual(expectedUsers[0].Name, result[0].Name);
    }

    [Test]
    public void ValidateEmail_ValidEmail_ReturnsTrue()
    {
        // Arrange
        var validEmail = "[email protected]";

        // Act
        var result = _userService.ValidateEmail(validEmail);

        // Assert
        Assert.IsTrue(result);
    }
}

// ViewModel testing
[TestFixture]
public class UserListViewModelTests
{
    private UserListViewModel _viewModel;
    private Mock<IUserService> _userServiceMock;

    [SetUp]
    public void Setup()
    {
        _userServiceMock = new Mock<IUserService>();
        _viewModel = new UserListViewModel(_userServiceMock.Object);
    }

    [Test]
    public async Task LoadUsersCommand_LoadsUsers()
    {
        // Arrange
        var users = new List<User>
        {
            new User { Id = 1, Name = "John" }
        };
        
        _userServiceMock.Setup(x => x.GetUsersAsync())
            .ReturnsAsync(users);

        // Act
        await _viewModel.LoadUsersCommand.ExecuteAsync();

        // Assert
        Assert.AreEqual(1, _viewModel.Users.Count);
        Assert.AreEqual("John", _viewModel.Users[0].Name);
    }
}

Deployment

Android Deployment

<!-- Android Manifest permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
// Build configuration
// Right-click Android project -> Properties -> Android Options

// For release build:
// - Set Target Android version
// - Enable ProGuard/R8 for code shrinking
// - Sign with release keystore

// Generate signed APK:
// Build -> Archive -> Distribute -> Ad Hoc

iOS Deployment

// Info.plist permissions
<key>NSCameraUsageDescription</key>
<string>This app uses camera to take photos</string>

<key>NSLocationWhenInUseUsageDescription</key>
<string>This app uses location services</string>

<key>NSMicrophoneUsageDescription</key>
<string>This app uses microphone for recording</string>

// Build configuration
// Right-click iOS project -> Properties -> iOS Bundle Signing
// Set provisioning profile and certificate for release

Performance Optimization

XAML Compilation

// Enable XAML compilation for better performance
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace MyApp
{
    public partial class App : Application
    {
        // App code
    }
}

ListView Performance

<!-- Use caching strategy -->
<ListView CachingStrategy="RecycleElement"
          HasUnevenRows="False"
          ItemsSource="{Binding Items}">
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell>
                <!-- Simple cell structure for better performance -->
                <StackLayout Orientation="Horizontal" Padding="10">
                    <Image Source="{Binding Image}" WidthRequest="50" HeightRequest="50" />
                    <Label Text="{Binding Title}" VerticalOptions="Center" />
                </StackLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

Memory Management

// Dispose of resources properly
public class MyPage : ContentPage, IDisposable
{
    private Timer _timer;
    
    public MyPage()
    {
        InitializeComponent();
        _timer = new Timer(OnTimerCallback, null, 1000, 1000);
    }
    
    private void OnTimerCallback(object state)
    {
        // Timer callback
    }
    
    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        Dispose();
    }
    
    public void Dispose()
    {
        _timer?.Dispose();
        _timer = null;
    }
}

Popular NuGet Packages

<!-- Essential packages -->
<PackageReference Include="Xamarin.Forms" Version="5.0.0" />
<PackageReference Include="Xamarin.Essentials" Version="1.7.0" />

<!-- MVVM -->
<PackageReference Include="Prism.Unity.Forms" Version="8.1.97" />
<PackageReference Include="ReactiveUI.XamForms" Version="13.2.10" />

<!-- HTTP and JSON -->
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Refit" Version="6.3.2" />

<!-- Database -->
<PackageReference Include="sqlite-net-pcl" Version="1.8.116" />
<PackageReference Include="SQLiteNetExtensions" Version="2.1.0" />

<!-- UI Components -->
<PackageReference Include="Xamarin.Forms.PancakeView" Version="2.3.0.759" />
<PackageReference Include="CarouselView.FormsPlugin" Version="5.2.0" />

<!-- Images -->
<PackageReference Include="FFImageLoading.Forms" Version="2.4.11.982" />

<!-- Permissions -->
<PackageReference Include="Plugin.Permissions" Version="6.0.1" />

<!-- Analytics and Crash Reporting -->
<PackageReference Include="Microsoft.AppCenter.Analytics" Version="4.4.0" />
<PackageReference Include="Microsoft.AppCenter.Crashes" Version="4.4.0" />

Official Resources

Community and Tools

About

Repo for xamarin

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published