Es gibt immer mal wieder das Szenario, dass man eine Mail aus einer App heraus senden möchte. Dies ist zum Beispiel der Fall, wenn man dem Nutzer eine Möglichkeit bieten möchte, dem Entwickler einer Nachricht zu hinterlassen. Ebenso ist das Szenario für ein Export einsetzbar. In diesem Blog-Beitrag möchte ich jetzt aufzeigen, wie man mit wenig Aufwand eine eMail-Nachricht aus einer .NET MAUI App versenden kann.
Zunächst legen wir uns ein neues .NET MAUI App-Projekt in Visual Studio 2022 an.
Im nächsten Schritt installieren wir das CommunityToolkit.Mvvm NuGet-Package. Dieses ermöglicht uns die Verwendung von MVVM innerhalb unserer App und spart eine Menge an Boilerplate-Code ein. Ich habe zu diesem Package auch schon ein Video auf meinem YouTube-Kanal veröffentlicht.
Nun können wir uns bereits an die Entwicklung machen. Glücklicherweise stellt Maui.Essentials eine Options bereit den internen Mail-Client zu öffnen. Hierfür sind für die verschiedenen Plattformen jedoch Vorbereitungen zu treffen. Unter Android müsst ihr die Datei AndroidManifest.xml
öffnen, welche ihr im Ordner Platforms/Android
findet. Hier ergänzt ihr am Ende der Datei den folgenden queries/intent
-Knoten.
<queries>
<intent>
<action android:name="android.intent.action.SENDTO" />
<data android:scheme="mailto" />
</intent>
</queries>
Auch für iOS bzw. Mac Catalyst sind Anpassungen notwendig. Öffnet hierfür die Datei Info.plist
, welche sich einmal im Ordner Platforms/iOS
und einmal im Ordner Platforms/MacCatalyst
befindet und ergänzt den folgenden Eintrag.
<key>LSApplicationQueriesSchemes</key>
<array>
<string>mailto</string>
</array>
Wir erstellen uns nun einen Ordner ViewModels
und darin eine Klasse BaseViewModel
. Diese Klasse markieren wir mit dem INotifyPropertyChanged
-Attribut und machen die Klasse partial
. Anschließend fügen wir die ObservableProperty
_isLoading
hinzu.
[INotifyPropertyChanged]
public partial class BaseViewModel
{
[ObservableProperty]
private bool _isLoading;
}
Im nächsten Schritt erstellen wir ein neues ViewModel im ViewModels
-Ordner mit dem Namen MailViewModel
. Dieses hat als Basisklasse natürlich das gerade erstellte BaseViewModel
und auch hier markieren wir die Klasse wieder mit dem partial
Schlüsselwort. Über den Konstruktor lassen wir uns das Interface IEmail
injecten und speichern dieses als _emailService
in einer Variablen ab. Anschließend erstellen wir die Methode OpenMailClientAsync
, welche wir mit dem RelayCommand
-Attribut ausstatten, so dass wir diese Methode per Command aufrufen können. Innerhalb der Methode erstellen wir uns nun eine neue EmailMessage
und belegen einige Dinge, wie Betreff, Body und den Empfänger fest. Anschließend rufen wir ComposeAsync
auf, welche den Standard-Mailclient öffnet.
public partial class MailViewModel : BaseViewModel
{
private readonly IEmail _emailService;
private const string _subject = "My Subject";
private const string _body = "The Mail Body";
private const string _recipient = "<ENTER THE RECIPIENT HERE>";
public MailViewModel(IEmail emailService)
{
_emailService = emailService;
}
[RelayCommand(AllowConcurrentExecutions = false)]
private async Task OpenMailClientAsync()
{
try
{
IsLoading = true;
if (_emailService.IsComposeSupported)
{
var emailMessage = new EmailMessage
{
Subject = _subject,
Body = _body,
To = new List<string> { _recipient }
};
await _emailService.ComposeAsync(emailMessage);
}
else
{
await Application.Current.MainPage.DisplayAlert("Unsupported",
"The opening of the email client is currently not supported.", "OK");
}
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Error", ex.ToString(), "OK");
}
finally
{
IsLoading = false;
}
}
}
Nun erstellen wir uns noch einen Views
-Ordner und darin eine ContentPage
mit dem Namen MailPage
. Hier fügen wir nun den Namespace zu unseren ViewModels hinzu und setzen den DataType
auf unser MailViewModel
, um Code-Vervollständigung zu erhalten. In einem VerticalStackLayout
fügen wir nun einen Button hinzu und setzen OpenMailClientCommand
als Command
.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:MauiMail.ViewModels"
x:Class="MauiMail.Views.MailPage"
x:DataType="viewmodels:MailViewModel"
Title="Mail"
Padding="16">
<VerticalStackLayout Spacing="8">
<Button Text="Open Mail Client"
Command="{Binding OpenMailClientCommand}" />
<ActivityIndicator WidthRequest="50"
HeightRequest="50"
HorizontalOptions="Center"
IsVisible="{Binding IsLoading}"
IsRunning="{Binding IsLoading}" />
</VerticalStackLayout>
</ContentPage>
In der Code-Behind-Datei lassen wir uns jetzt das MailViewModel
noch injecten und setzen dann den BindingContext
für unsere Page.
public partial class MailPage : ContentPage
{
public MailPage(MailViewModel mailViewModel)
{
InitializeComponent();
BindingContext = mailViewModel;
}
}
Bevor wir unsere App jetzt einmal ausprobieren können, müssen wir noch die Page, das ViewModel und auch das IEmail-Interface registrieren.
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");
});
// Views
builder.Services.AddTransient<MailPage>();
// Services
builder.Services.AddSingleton(Email.Default);
// ViewModels
builder.Services.AddTransient<MailViewModel>();
return builder.Build();
}
}
In der AppShell tauschen wir nun noch die MainPage gegen die MailPage aus und anschließend können wir die App starten.
<?xml version="1.0" encoding="UTF-8" ?>
<Shell x:Class="MauiMail.AppShell"
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:views="clr-namespace:MauiMail.Views"
Shell.FlyoutBehavior="Disabled">
<ShellContent Title="Mail"
ContentTemplate="{DataTemplate views:MailPage}"
Route="MailPage" />
</Shell>
Die folgenden Screenshots zeigen das Verhalten der App unter Android.
Schauen wir uns nun einmal die Windows-Version an. Nach dem Klicken auf den Button sehen wir leider einen Dialog mit einer Fehlermeldung.
Ein wenig Recherche ergibt direkt, dass ein Problem mit dem Senden von eMail-Nachrichten über das IEmail
-Interface unter Windows gibt. Durch den Wechsel von UWP auf WinUI stehen leider nicht alle Dinge zur Verfügung. Dazu zählt auch das Öffnen des Mail-Clients. Weitere Informationen könnt ihr dem GitHub-Issue entnehmen.
Nun müssen wir uns eine Alternative überlegen. Glücklicherweise gibt es die Klasse SmtpClient
. Diese ermöglicht das direkte Senden einer eMail-Nachricht, so dass sich nicht erst noch der Email-Client öffnet. Dafür sind ein paar weitere Informationen, wie der SMTP Host oder der SMTP Port, sowie das Passwort für die Email-Adresse.
private const string _subject = "My Subject";
private const string _body = "The Mail Body";
private const string _recipient = "<ENTER THE RECIPIENT HERE>";
private const string _from = "<ENTER THE FROM ADDRESS HERE>";
private const string _smtpHost = "<ADD SMTP HOST>";
private const int _smtpPort = 587; // Update with the corresponding SMTP Port
private NetworkCredential _smtpCredentials = new("<MAIL ADDRESS>", "<MAIL PASSWORD>");
[RelayCommand(AllowConcurrentExecutions = false)]
private async Task SendMailViaSmtpClientAsync()
{
try
{
IsLoading = true;
var smtpClient = new SmtpClient(_smtpHost)
{
Port = _smtpPort,
Credentials = _smtpCredentials,
EnableSsl = true,
};
var mailMessage = new MailMessage
{
From = new MailAddress(_from),
Subject = _subject,
Body = _body
};
mailMessage.To.Add(_recipient);
await smtpClient.SendMailAsync(mailMessage);
await Application.Current.MainPage.DisplayAlert("Success",
"Message was sent.", "Ok");
}
catch (Exception ex)
{
await Application.Current.MainPage.DisplayAlert("Error", ex.ToString(), "OK");
}
finally
{
IsLoading = false;
}
}
Wir fügen nun noch einen weiteren Button auf der MailPage
hinzu und können uns die App nun einmal anschauen.
<Button Text="Send Mail via SmtpClient"
Command="{Binding SendMailViaSmtpClientCommand}" />
Hier nun die App unter Windows. Wie ihr sehen könnt, wurde die Nachricht nun versendet.
So haben wir zumindest einen Workaround gefunden, dass wir eine Email senden können. Aber Microsoft arbeitet bereits an der Anpassung, so dass das IEmail
-Interface auch unter WinUI demnächst funktionieren könnte.