Xamarin Plattformübergreifende Entwicklungsbeispiele

Xamarin.Forms und Xamarin.Native Implementierungsbeispiele für plattformübergreifende mobile Entwicklung mit C# und .NET

💻 Xamarin.Forms Grundlegende CRUD-Operationen csharp

🟢 simple

Erstellen Sie eine plattformübergreifende mobile App mit grundlegenden CRUD-Operationen mit Xamarin.Forms und SQLite

// XAML - 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="XamarinCRUDApp.MainPage"
             Title="Contact Manager">

    <StackLayout Padding="20" Spacing="10">
        <Label Text="Contact Manager"
               FontSize="Large"
               FontAttributes="Bold"
               HorizontalOptions="Center" />

        <Entry x:Name="NameEntry" Placeholder="Name" />
        <Entry x:Name="EmailEntry" Placeholder="Email" Keyboard="Email" />
        <Entry x:Name="PhoneEntry" Placeholder="Phone" Keyboard="Telephone" />

        <StackLayout Orientation="Horizontal" HorizontalOptions="Center">
            <Button Text="Add" Clicked="OnAddClicked" />
            <Button Text="Update" Clicked="OnUpdateClicked" />
            <Button Text="Delete" Clicked="OnDeleteClicked" />
        </StackLayout>

        <ListView x:Name="ContactsListView"
                  ItemSelected="OnContactSelected">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <TextCell Text="{Binding Name}"
                              Detail="{Binding Email}" />
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>

// C# - Contact.cs
using SQLite;
using System.ComponentModel;

namespace XamarinCRUDApp.Models
{
    public class Contact : INotifyPropertyChanged
    {
        [PrimaryKey, AutoIncrement]
        public int Id { get; set; }

        private string _name;
        public string Name
        {
            get => _name;
            set
            {
                _name = value;
                OnPropertyChanged(nameof(Name));
            }
        }

        private string _email;
        public string Email
        {
            get => _email;
            set
            {
                _email = value;
                OnPropertyChanged(nameof(Email));
            }
        }

        private string _phone;
        public string Phone
        {
            get => _phone;
            set
            {
                _phone = value;
                OnPropertyChanged(nameof(Phone));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

// C# - DatabaseService.cs
using SQLite;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using XamarinCRUDApp.Models;

namespace XamarinCRUDApp.Services
{
    public class DatabaseService
    {
        private readonly SQLiteAsyncConnection _database;

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

        public Task<List<Contact>> GetContactsAsync()
        {
            return _database.Table<Contact>().ToListAsync();
        }

        public Task<Contact> GetContactAsync(int id)
        {
            return _database.Table<Contact>()
                            .Where(c => c.Id == id)
                            .FirstOrDefaultAsync();
        }

        public Task<int> SaveContactAsync(Contact contact)
        {
            if (contact.Id != 0)
            {
                return _database.UpdateAsync(contact);
            }
            else
            {
                return _database.InsertAsync(contact);
            }
        }

        public Task<int> DeleteContactAsync(Contact contact)
        {
            return _database.DeleteAsync(contact);
        }
    }
}

💻 Xamarin.Forms Navigation mit MasterDetail-Seite csharp

🟡 intermediate

Implementieren Sie erweiterte Navigationsmuster mit MasterDetail-Seiten, Menünavigation, Seitenübergängen und mobilenspezifischen UX-Mustern

// XAML - AppShell.xaml
<?xml version="1.0" encoding="UTF-8"?>
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
       xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
       xmlns:views="clr-namespace:XamarinNavigationApp.Views"
       x:Class="XamarinNavigationApp.AppShell"
       FlyoutHeaderBehavior="FlyoutHeaderBehavior.OnScroll">

    <Shell.FlyoutHeader>
        <Grid BackgroundColor="#2196F3" HeightRequest="150">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>

            <Image Grid.Row="0"
                   Source="appicon.png"
                   HeightRequest="60"
                   WidthRequest="60"
                   HorizontalOptions="Center"
                   VerticalOptions="Center" />

            <Label Grid.Row="1"
                   Text="My App"
                   TextColor="White"
                   FontSize="Large"
                   HorizontalTextAlignment="Center" />
        </Grid>
    </Shell.FlyoutHeader>

    <FlyoutItem Title="Home" Icon="home.png">
        <ShellContent Route="home" ContentTemplate="{DataTemplate views:HomePage}" />
    </FlyoutItem>

    <FlyoutItem Title="Products" Icon="products.png">
        <TabBar>
            <ShellContent Title="All Products" Route="products"
                          ContentTemplate="{DataTemplate views:ProductsPage}" />
            <ShellContent Title="Categories" Route="categories"
                          ContentTemplate="{DataTemplate views:CategoriesPage}" />
            <ShellContent Title="Search" Route="search"
                          ContentTemplate="{DataTemplate views:SearchPage}" />
        </TabBar>
    </FlyoutItem>

    <FlyoutItem Title="Profile" Icon="profile.png">
        <ShellContent Route="profile" ContentTemplate="{DataTemplate views:ProfilePage}" />
    </FlyoutItem>

    <FlyoutItem Title="Settings" Icon="settings.png">
        <ShellContent Route="settings" ContentTemplate="{DataTemplate views:SettingsPage}" />
    </FlyoutItem>

    <MenuItem Text="Logout" Icon="logout.png" Clicked="OnLogoutClicked" />
</Shell>

// XAML - HomePage.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="XamarinNavigationApp.Views.HomePage"
             Title="Home">

    <RefreshView x:Name="RefreshView" IsRefreshing="{Binding IsRefreshing, Mode=TwoWay}">
        <ScrollView>
            <StackLayout Padding="20" Spacing="20">

                <!-- Welcome Section -->
                <Frame BackgroundColor="#2196F3" Padding="20" CornerRadius="10">
                    <StackLayout>
                        <Label Text="Welcome Back!"
                               TextColor="White"
                               FontSize="24"
                               FontAttributes="Bold" />
                        <Label Text="{Binding UserWelcome}"
                               TextColor="White"
                               FontSize="16"
                               Opacity="0.8" />
                    </StackLayout>
                </Frame>

                <!-- Quick Actions -->
                <Label Text="Quick Actions"
                       FontSize="18"
                       FontAttributes="Bold" />

                <Grid RowSpacing="15" ColumnSpacing="15">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>

                    <Frame Grid.Row="0" Grid.Column="0"
                           BackgroundColor="#FF9800" Padding="20"
                           HasShadow="True">
                        <StackLayout>
                            <Image Source="add.png"
                                   WidthRequest="30"
                                   HeightRequest="30"
                                   HorizontalOptions="Center" />
                            <Label Text="Add Item"
                                   TextColor="White"
                                   HorizontalTextAlignment="Center"
                                   FontSize="12" />
                        </StackLayout>
                        <Frame.GestureRecognizers>
                            <TapGestureRecognizer Command="{Binding AddItemCommand}" />
                        </Frame.GestureRecognizers>
                    </Frame>
                </Grid>
            </StackLayout>
        </ScrollView>
    </RefreshView>
</ContentPage>

// C# - HomeViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace XamarinNavigationApp.ViewModels
{
    public class HomeViewModel : INotifyPropertyChanged
    {
        private bool _isRefreshing;
        private string _userWelcome;

        public bool IsRefreshing
        {
            get => _isRefreshing;
            set
            {
                _isRefreshing = value;
                OnPropertyChanged();
            }
        }

        public string UserWelcome
        {
            get => _userWelcome;
            set
            {
                _userWelcome = value;
                OnPropertyChanged();
            }
        }

        public ObservableCollection<RecentActivity> RecentActivities { get; set; }
        public Command AddItemCommand { get; }
        public Command SearchCommand { get; }
        public Command FavoritesCommand { get; }
        public Command SettingsCommand { get; }
        public Command RefreshCommand { get; }

        public HomeViewModel()
        {
            UserWelcome = "John Doe";
            RecentActivities = new ObservableCollection<RecentActivity>();

            AddItemCommand = new Command(async () => await Shell.Current.GoToAsync("additem"));
            SearchCommand = new Command(async () => await Shell.Current.GoToAsync("search"));
            FavoritesCommand = new Command(async () => await Shell.Current.GoToAsync("favorites"));
            SettingsCommand = new Command(async () => await Shell.Current.GoToAsync("settings"));
            RefreshCommand = new Command(async () => await RefreshData());

            LoadRecentActivities();
        }

        private void LoadRecentActivities()
        {
            RecentActivities.Clear();

            RecentActivities.Add(new RecentActivity
            {
                Title = "New Product Added",
                Description = "Added 'Wireless Headphones' to inventory",
                TimeAgo = "2 hours ago",
                Icon = "add.png"
            });
        }

        private async Task RefreshData()
        {
            IsRefreshing = true;
            await Task.Delay(2000);
            LoadRecentActivities();
            IsRefreshing = false;
        }

        public event PropertyChangedEventHandler PropertyChanged;

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

    public class RecentActivity
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public string TimeAgo { get; set; }
        public string Icon { get; set; }
    }
}

💻 Xamarin Web API Integration mit Authentifizierung csharp

🔴 complex

Erstellen Sie eine produktionsreife Xamarin-App mit REST-API-Integration, JWT-Authentifizierung, sicherer Token-Speicherung und Offline-Synchronisierung

// C# - Models/ApiResponse.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;

namespace XamarinAPIClient.Models
{
    public class ApiResponse<T>
    {
        public bool Success { get; set; }
        public string Message { get; set; }
        public T Data { get; set; }
        public List<string> Errors { get; set; } = new List<string>();
    }

    public class User
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Email { get; set; }
        public string PhoneNumber { get; set; }
        public DateTime CreatedAt { get; set; }
        public bool IsActive { get; set; }
    }

    public class LoginRequest
    {
        public string Email { get; set; }
        public string Password { get; set; }
    }

    public class LoginResponse
    {
        public string Token { get; set; }
        public DateTime ExpiresAt { get; set; }
        public User User { get; set; }
    }
}

// C# - Services/ApiService.cs
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using XamarinAPIClient.Helpers;
using XamarinAPIClient.Models;

namespace XamarinAPIClient.Services
{
    public class ApiService
    {
        private readonly HttpClient _httpClient;
        private readonly IAuthenticationService _authService;

        public ApiService(IAuthenticationService authService)
        {
            _httpClient = new HttpClient
            {
                BaseAddress = new Uri(Settings.ApiBaseUrl)
            };
            _httpClient.DefaultRequestHeaders.Accept.Clear();
            _httpClient.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));

            _authService = authService;
        }

        private async Task<bool> AddAuthorizationHeaderAsync()
        {
            var token = await _authService.GetTokenAsync();
            if (!string.IsNullOrEmpty(token))
            {
                _httpClient.DefaultRequestHeaders.Authorization =
                    new AuthenticationHeaderValue("Bearer", token);
                return true;
            }
            return false;
        }

        public async Task<ApiResponse<LoginResponse>> LoginAsync(LoginRequest loginRequest)
        {
            try
            {
                var json = JsonConvert.SerializeObject(loginRequest);
                var content = new StringContent(json, Encoding.UTF8, "application/json");

                var response = await _httpClient.PostAsync("/api/auth/login", content);

                var responseContent = await response.Content.ReadAsStringAsync();
                var result = JsonConvert.DeserializeObject<ApiResponse<LoginResponse>>(responseContent);

                if (response.IsSuccessStatusCode && result.Success)
                {
                    await _authService.SaveTokenAsync(result.Data.Token, result.Data.ExpiresAt);
                    await _authService.SaveUserAsync(result.Data.User);
                }

                return result;
            }
            catch (Exception ex)
            {
                return new ApiResponse<LoginResponse>
                {
                    Success = false,
                    Message = $"Network error: {ex.Message}"
                };
            }
        }

        public async Task<ApiResponse<List<Product>>> GetProductsAsync()
        {
            try
            {
                if (!await AddAuthorizationHeaderAsync())
                {
                    return new ApiResponse<List<Product>>
                    {
                        Success = false,
                        Message = "Authorization required"
                    };
                }

                var response = await _httpClient.GetAsync("/api/products");

                if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
                {
                    await _authService.SignOutAsync();
                    return new ApiResponse<List<Product>>
                    {
                        Success = false,
                        Message = "Session expired. Please login again."
                    };
                }

                var responseContent = await response.Content.ReadAsStringAsync();
                var result = JsonConvert.DeserializeObject<ApiResponse<List<Product>>>(responseContent);

                return result;
            }
            catch (Exception ex)
            {
                return new ApiResponse<List<Product>>
                {
                    Success = false,
                    Message = $"Network error: {ex.Message}"
                };
            }
        }
    }
}

// C# - Services/AuthenticationService.cs
using XamarinAPIClient.Helpers;
using XamarinAPIClient.Models;
using System.Threading.Tasks;
using Plugin.SecureStorage;

namespace XamarinAPIClient.Services
{
    public interface IAuthenticationService
    {
        Task<bool> IsAuthenticatedAsync();
        Task<bool> LoginAsync(string email, string password);
        Task SignOutAsync();
        Task<User> GetCurrentUserAsync();
        Task<string> GetTokenAsync();
        Task SaveTokenAsync(string token, DateTime expiresAt);
    }

    public class AuthenticationService : IAuthenticationService
    {
        private readonly ApiService _apiService;

        public AuthenticationService(ApiService apiService)
        {
            _apiService = apiService;
        }

        public async Task<bool> IsAuthenticatedAsync()
        {
            try
            {
                var token = await GetTokenAsync();
                var expiresAt = CrossSecureStorage.Current.GetValue("token_expires");

                if (string.IsNullOrEmpty(token) || string.IsNullOrEmpty(expiresAt))
                    return false;

                var expiryDate = DateTime.Parse(expiresAt);

                // Refresh token if it's expired or about to expire (within 5 minutes)
                if (expiryDate < DateTime.UtcNow.AddMinutes(5))
                {
                    var refreshResult = await _apiService.RefreshTokenAsync();
                    return refreshResult.Success;
                }

                return true;
            }
            catch
            {
                return false;
            }
        }

        public async Task<bool> LoginAsync(string email, string password)
        {
            try
            {
                var loginRequest = new LoginRequest
                {
                    Email = email,
                    Password = password
                };

                var response = await _apiService.LoginAsync(loginRequest);
                return response.Success;
            }
            catch
            {
                return false;
            }
        }

        public async Task SignOutAsync()
        {
            CrossSecureStorage.Current.DeleteKey("auth_token");
            CrossSecureStorage.Current.DeleteKey("refresh_token");
            CrossSecureStorage.Current.DeleteKey("token_expires");
            CrossSecureStorage.Current.DeleteKey("user_data");
        }

        public async Task<User> GetCurrentUserAsync()
        {
            try
            {
                var userData = CrossSecureStorage.Current.GetValue("user_data");
                if (!string.IsNullOrEmpty(userData))
                {
                    return JsonConvert.DeserializeObject<User>(userData);
                }
            }
            catch { }

            return null;
        }

        public async Task<string> GetTokenAsync()
        {
            return await Task.FromResult(
                CrossSecureStorage.Current.GetValue("auth_token"));
        }

        public async Task SaveTokenAsync(string token, DateTime expiresAt)
        {
            await Task.Run(() =>
            {
                CrossSecureStorage.Current.SetValue("auth_token", token);
                CrossSecureStorage.Current.SetValue("token_expires", expiresAt.ToString());
            });
        }
    }
}