Set of tools designed to be used in MAUI projects, including Views, ViewModels, Services, Extensions and more...
| Nuget Package | Current Version |
|---|---|
| zoft.MauiExtensions.Core |
I'm currently in the process of deploying a sample app (Android) to demonstrate the capabilities of the package. If you're interested in becoming a tester to allow the app to be published, please add your email in the issue created for this purpose: Sample App Testing
Install nuget package: zoft.MauiExtensions.Core
Install-Package zoft.MauiExtensions.Core
Refer to the sample to have a better understanding of package capabilities. Bellow you can find the most common features and how to use them
The package provides a set of tools to implement localization in your app:
ILocalizationService: Interface for the localization service. The interface exists to make it easier to use iwith IOC and to override the base implementationResourceManagerLocalizationService: Implementation of theILocalizationServiceusing resource files (.resx)
builder.Services.AddSingleton<ILocalizationService>(new ResourceManagerLocalizationService(AppResources.ResourceManager, SupportedLanguages.DefaultLanguage));TranslationMarkup: XAML markup that provides an easy way to apply the translation directly in XAML
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:zoft="http://zoft.maui.extensions"
x:Class="zoft.MauiExtensions.Sample.Views.LocalizationView"
Title="{zoft:Translate LocalizationPage_Title}">
...
...
<ScrollView>
<VerticalStackLayout Spacing="10">
<Label Text="{zoft:Translate LocalizationPage_Label1}" FontSize="16" FontAttributes="Bold"/>
<Label Text="{zoft:Translate LocalizationPage_Label2}" />
<Label Text="{zoft:Translate LocalizationPage_Label3}" FontAttributes="Italic" BackgroundColor="LightGray"/>
</VerticalStackLayout>
</ScrollView>
...
</ContentPage>Based on the Component Models of the CommunityToolkit.MVVM, the package provides a set of base models that can be used to create Models, ViewModels and Services in your app.
-
ZoftObservableObject: Based on the
ObservableObjectclass, provides a base implementation of theINotifyPropertyChangedinterface with additional features:IsBusyandIsNotBusyproperties for UI bindingBusyMessageproperty for displaying status messagesDoWorkAsync()methods for executing background tasks with busy state management- Implements
IDisposablefor proper resource cleanup
-
ZoftObservableRecipient: Based on the
ObservableRecipientclass, provides messaging capabilities:- Inherits all features from
ZoftObservableObject - Built-in messenger functionality for communication between ViewModels
- Automatic message registration and cleanup
Broadcast()method for sending property change messages
- Inherits all features from
-
ZoftObservableValidator: Based on the
ObservableValidatorclass, provides validation capabilities:- All features of
ZoftObservableObject - Data annotation validation support
ValidateAllProperties()andClearErrors()methods- Override
OnErrorsChanged()to handle validation state changes - Automatic error collection and management
- All features of
Base models provide methods to execute code in a background thread, while providing with updated on IsBusy and BusyMessage properties that can be bound to an UI element (i.e. ActivityIndicator)
await DoWorkAsync(() => ..., "Busy Message");
var result = await DoWorkAsync(() => return some_object, "BusyMessage");The ZoftObservableValidator base class provides comprehensive validation capabilities using data annotations:
public partial class ValidationViewModel : ZoftObservableValidator
{
[ObservableProperty]
[Required]
[MinLength(2)]
[MaxLength(100)]
public partial string FirstName { get; set; }
[ObservableProperty]
[Required]
[EmailAddress]
public partial string Email { get; set; }
protected override void OnErrorsChanged(INotifyDataErrorInfo source, ZoftObservableValidator target, DataErrorsChangedEventArgs e)
{
base.OnErrorsChanged(source, target, e);
ErrorMessage = string.Join(Environment.NewLine, GetErrors().Select(e => e.ErrorMessage));
}
[RelayCommand]
private void Validate()
{
ValidateAllProperties();
}
[RelayCommand]
private void ClearValidation()
{
ClearErrors();
}
}The ZoftObservableRecipient base class provides built-in messaging capabilities for ViewModel communication:
public partial class MessengerViewModel : ZoftObservableRecipient,
IRecipient<PropertyChangedMessage<string>>,
IRecipient<CustomMessage>
{
public MessengerViewModel() : base()
{
IsActive = true; // Enable message reception
}
// Receive property change messages
void IRecipient<PropertyChangedMessage<string>>.Receive(PropertyChangedMessage<string> message)
{
if (message.PropertyName == nameof(Text))
Text = message.NewValue;
}
// Receive custom messages
void IRecipient<CustomMessage>.Receive(CustomMessage message)
{
// Handle custom message
}
[RelayCommand]
private void SendMessage()
{
// Broadcast property changes
Broadcast(oldValue, newValue, nameof(PropertyName));
// Send custom messages
Messenger.Send<CustomMessage>();
}
}The package provides a set of extension methods to subscribe to events using weak references, avoiding memory leaks when the subscriber is not disposed properly:
more info will come...
The library provides numerous extension methods to simplify common operations:
// Add missing items to a collection
targetCollection.AddMissing(itemsToAdd);
targetCollection.AddMissing(itemsToAdd, customValidationFunc);
// Safe count with fallback
int count = collection.Count(fallbackValue: 0);// Null-safe string operations
bool isEmpty = text.IsNullOrEmpty();
bool isWhitespace = text.IsNullOrWhiteSpace();
// Template formatting with null safety
string result = template.FormatTemplate(arg1, arg2);
// Regular expression matching
bool matches = input.IsMatch(@"\d+");
string extract = input.Extract(@"(\d+)");// Null-safe formatting
string formatted = nullableDate.Format("yyyy-MM-dd");
// Date operations
DateTime dateOnly = dateTime.ToDate();
string shortDate = dateTime.ToShortDateString();
DateTime utcAdjusted = dateTime.GetUtcAdjustedTime();// Reflection-based property access
object value = obj.GetPropertyValue("PropertyName");
string stringValue = obj.GetPropertyValueAsString("PropertyName", "default");
obj.SetPropertyValue("PropertyName", newValue);
// Null checking
var nonNull = obj.ThrowIfNull(nameof(obj));// Timeout support
var result = await task.WithTimeout(5000); // 5 seconds
var result = await task.WithTimeout(TimeSpan.FromMinutes(1));
// Safe fire-and-forget execution
task.SafeFireAndForget(onException: ex => Console.WriteLine(ex.Message));// Find index with predicate
int index = list.FindIndex(item => item.Name == "test");
// Apply action to all items
list.ForEach(item => item.Process());// Safe operations
dictionary.AddOrUpdate(key, value);
dictionary.AddOrUpdate(keyValuePairs); // Bulk operations
dictionary.AddOrIgnore(keyValuePairs); // Add only if key doesn't exist
dictionary.RemoveIfExists(key);
// Apply action to all items
dictionary.ForEach((key, value) => Console.WriteLine($"{key}: {value}"));
// Convert to tuple list
var tuples = dictionary.ToTupleList();// Get full exception description including inner exceptions
string fullDescription = exception.GetFullDescription();// Get properties with various options
var publicProps = type.GetProperties(onlyPublic: true);
var allProps = type.GetProperties(onlyPublic: false, includeInherited: true);