AsyncCommand in .NET MAUI - xamarin

I can't seem to find AsyncCommand in .NET MAUI or .NET MAUI Community Toolkit. Any idea what package/namespace I can find it?

https://devblogs.microsoft.com/dotnet/introducing-the-net-maui-community-toolkit-preview/#what-to-expect-in-net-maui-toolkit
The .NET MAUI Toolkit will not contain the MVVM features from Xamarin
Community Toolkit, like AsyncCommand. Going forward, we will be adding
all MVVM-specifc features to a new NuGet Package,
CommunityToolkit.MVVM.

install CommunityToolkitMVVM 8.0.0
[RelayCommand]
async Task your_method (){...}

Even if it has been marked as solved, someone might profit from this solution which did wrote very well John Thiriet. I implied it, and it worked fine.
https://johnthiriet.com/mvvm-going-async-with-async-command/
public interface IAsyncCommand<T> : ICommand
{
Task ExecuteAsync(T parameter);
bool CanExecute(T parameter);
}
public class AsyncCommand<T> : IAsyncCommand<T>
{
public event EventHandler CanExecuteChanged;
private bool _isExecuting;
private readonly Func<T, Task> _execute;
private readonly Func<T, bool> _canExecute;
private readonly IErrorHandler _errorHandler;
public AsyncCommand(Func<T, Task> execute, Func<T, bool> canExecute = null, IErrorHandler errorHandler = null)
{
_execute = execute;
_canExecute = canExecute;
_errorHandler = errorHandler;
}
public bool CanExecute(T parameter)
{
return !_isExecuting && (_canExecute?.Invoke(parameter) ?? true);
}
public async Task ExecuteAsync(T parameter)
{
if (CanExecute(parameter))
{
try
{
_isExecuting = true;
await _execute(parameter);
}
finally
{
_isExecuting = false;
}
}
RaiseCanExecuteChanged();
}
public void RaiseCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
//#region Explicit implementations
bool ICommand.CanExecute(object parameter)
{
return CanExecute((T)parameter);
}
void ICommand.Execute(object parameter)
{
ExecuteAsync((T)parameter).FireAndForgetSafeAsync(_errorHandler);
}
//#endregion
}
Then it can be used in MAUI just like in Xamarin.
public MyMVVM()
{
MyCommand = new AsyncCommand(async()=> await MyMethod);
}
...
public AsynCommand MyCommand {get;}

Add the AsyncAwaitBestPractices.MVVM Nuget package to your project to get the AsyncCommand back.
For more information see the Github project page: https://github.com/brminnick/AsyncAwaitBestPractices

Related

How to implement / register singleton HttClientFactory in Xamarin Forms

I am using Xamarin Forms with Prism.
How can I register a service for HttpClientFactory?
Is there a equivalent in xamarin/prism to the ConfigureServices method in .net core?
I would like to use the equivalent of this code in Xamarin forms:
services.AddHttpClient<MyClient>("MyHttpClient",
x => { x.BaseAddress = new Uri("example.com"); }
).AddPolicyHandler(GetRetryPolicy());
services.AddSingleton<MyClientFactory>();
I have been trying different ways and I cannot find the right way to do it.
Thanks a lot.
UPDATE
I have got these clases in a netstandard ClassLibrary called BusinessLogic
MyClientFactory.cs:
namespace BusinessLogic.Services
{
public class MyClientFactory
{
private readonly IServiceProvider _serviceProvider;
public MyClientFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public MyClient Create()
{
return _serviceProvider.GetRequiredService<MyClient>();
}
}
}
MyClient.cs:
namespace ServiceBusinessLogic.Services
{
public class MyClient : IMyClient
{
private readonly HttpClient _httpClient;
private readonly ILogger<MyClient> _logger;
public MyClient(ILogger<MyClient> logger, HttpClient httpClient)
{
_logger = logger;
_httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
public async Task<Result<Token>> GetToken(CancellationToken cancellationToken, string userName, string password)
{
try
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "Token");
request.Content = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "password"),
new KeyValuePair<string, string>("username", userName),
new KeyValuePair<string, string>("password", password)
});
HttpResponseMessage result = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
if (!result.IsSuccessStatusCode)
{
_logger.LogError("the status code is: {0}", (int)result.StatusCode);
// CODE abbreviated for reading
}
Interface IMyClient.cs:
namespace MyBusinessLogic.Services
{
public interface IMyClient
{
Task<Result<Token>> GetToken(CancellationToken cancellationToken, string userName, string password);
// CODE abbreviated for reading
MyClientQueries.cs:
namespace MyBusinessLogic.Services
{
public class MyClientQueries
{
private readonly MyClientFactory _myClientFactory;
public MyClientQueries(MyClientFactory myClientFactory)
{
_MyClientFactory = myClientFactory ?? throw new ArgumentNullException(nameof(myClientFactory));
}
public async Task<Result<Token>> GetToken(CancellationToken cancellationToken, string username, string password)
{
var mysClient = _myClientFactory.Create();
var response = await tsClient.GetToken(cancellationToken, username, password).ConfigureAwait(true);
return response;
}
// CODE abbreviated
Then I have got a xamarin forms Project with Prism called App_Mobile_test
App.xaml.cs:
public partial class App : PrismApplication
{
public App() : this(null) { }
public App(IPlatformInitializer initializer) : base(initializer) { }
protected override async void OnInitialized()
{
InitializeComponent();
await NavigationService.NavigateAsync("NavigationPage/MainPage");
}
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.RegisterForNavigation<NavigationPage>();
containerRegistry.RegisterForNavigation<MainPage>();
}
}
I would like to use MyClientQueries which uses DI for HttpClientFactory and HttpClient in the Xamarin Forms project as a service (singleton) so I can call GetToken and all the other methods that are in that class.
But I do not know how to do this in Xamarin Forms.
MyBusinessProject that contains MyClientQueries which I want to use, is already used in a asp.net core mvc and the services were added in the startup.cs in .net core like this:
public void ConfigureServices(IServiceCollection services)
{
#region "api service"
services.AddSingleton<MyClientQueries>();
services.AddHttpClient<MyClient>("MyHttpClient",
x => { x.BaseAddress = new Uri(Configuration["APIConfiguration:BaseAddress"]); }
).AddPolicyHandler(GetRetryPolicy());
services.AddSingleton<MyClientFactory>();
#endregion
//CODE abbreviated
}
How can I register the same objects I am doing in the .net Core app, but in Xamarin Forms and use the dependency injection for the view models?
The shared project "MyBusinessLogic" works fine in the asp.net core, and I would like to reuse this project in the Xamarin project. Specially MyClientQueries which has got HttpClientFactory dependency.
It will depend on what DI you are using as to what the syntax will be, but generally that sort of code will be in the App constructor or a method called by the constructor.
So for example you might have a method called RegisterServices in your App.xaml.cs that is called from the constructor, after the Xamarin Init() has been called.
Inside that RegisterServices Method you would have something like this:
FreshIOC.Container.Register<IHttpManager, HttpManager>().AsSingleton();
We use FreshMvvm which uses TinyIOC for Dependancy Injection, so the syntax may differ, but the concept will be the same.

Caliburn.Micro 3.0 equivalent to Xamarin.Forms Navigation.PushModalAsync

Does Caliburn.Micro 3.0 (and Caliburn.Micro.Xamarin.Forms) implement functionality to mimic/support Navigation.PushModalAsync in Xamarin.Forms?
No. It's not build in, but its easy to enhance it. Usually, MvvM frameworks are navigating by ViewModels. Caliburn is following this pattern. So it needs some kind of navigation service. This navigationservice is responsible for creating the Views for the ViewModels and call the view framework (Xamarin.Froms in our case) specific navigation functions. NavigationPageAdapter is the thing we are searching for. Now let's enhance it.
public interface IModalNavigationService : INavigationService
{
Task NavigateModalToViewModelAsync<TViewModel>(object parameter = null, bool animated = true);
// TODO: add more functions for closing
}
public class ModalNavigationPageAdapter : NavigationPageAdapter, IModalNavigationService
{
private readonly NavigationPage _navigationPage;
public ModalNavigationPageAdapter(NavigationPage navigationPage) : base(navigationPage)
{
_navigationPage = navigationPage;
}
public async Task NavigateModalToViewModelAsync<TViewModel>(object parameter = null, bool animated = true)
{
var view = ViewLocator.LocateForModelType(typeof(TViewModel), null, null);
await PushModalAsync(view, parameter, animated);
}
private Task PushModalAsync(Element view, object parameter, bool animated)
{
var page = view as Page;
if (page == null)
throw new NotSupportedException(String.Format("{0} does not inherit from {1}.", view.GetType(), typeof(Page)));
var viewModel = ViewModelLocator.LocateForView(view);
if (viewModel != null)
{
TryInjectParameters(viewModel, parameter);
ViewModelBinder.Bind(viewModel, view, null);
}
page.Appearing += (s, e) => ActivateView(page);
page.Disappearing += (s, e) => DeactivateView(page);
return _navigationPage.Navigation.PushModalAsync(page, animated);
}
private static void DeactivateView(BindableObject view)
{
if (view == null)
return;
var deactivate = view.BindingContext as IDeactivate;
if (deactivate != null)
{
deactivate.Deactivate(false);
}
}
private static void ActivateView(BindableObject view)
{
if (view == null)
return;
var activator = view.BindingContext as IActivate;
if (activator != null)
{
activator.Activate();
}
}
}
We just declared the interface IModalNavigationService that extends INavigationService and implement it in our ModalNavigationPageAdapter. Unfortunately Caliburn made alot of functions private, so we have to copy them over to our inherited version.
In caliburn you can navigate via navigationservice.For<VM>().Navigate(). We want to follow this style, so we have to implement something like navigationservice.ModalFor<VM>().Navigate() which we do in an extension method.
public static class ModalNavigationExtensions
{
public static ModalNavigateHelper<TViewModel> ModalFor<TViewModel>(this IModalNavigationService navigationService)
{
return new ModalNavigateHelper<TViewModel>().AttachTo(navigationService);
}
}
This method returns a ModalNavigateHelperthat simplifies the usage of our navigation service (similar to Caliburn's NavigateHelper). It's nearly a copy, but for the IModalNavigationService.
public class ModalNavigateHelper<TViewModel>
{
readonly Dictionary<string, object> parameters = new Dictionary<string, object>();
IModalNavigationService navigationService;
public ModalNavigateHelper<TViewModel> WithParam<TValue>(Expression<Func<TViewModel, TValue>> property, TValue value)
{
if (value is ValueType || !ReferenceEquals(null, value))
{
parameters[property.GetMemberInfo().Name] = value;
}
return this;
}
public ModalNavigateHelper<TViewModel> AttachTo(IModalNavigationService navigationService)
{
this.navigationService = navigationService;
return this;
}
public void Navigate(bool animated = true)
{
if (navigationService == null)
{
throw new InvalidOperationException("Cannot navigate without attaching an INavigationService. Call AttachTo first.");
}
navigationService.NavigateModalToViewModelAsync<TViewModel>(parameters, animated);
}
}
Last but not least, we have to use our shiny new navigation service instead of the old one. The App class is registering the NavigationPageAdapter for the INavigationService as singleton in PrepareViewFirst. We have to change it as follows
public class App : FormsApplication
{
private readonly SimpleContainer container;
public App(SimpleContainer container)
{
this.container = container;
container
.PerRequest<LoginViewModel>()
.PerRequest<FeaturesViewModel>();
Initialize();
DisplayRootView<LoginView>();
}
protected override void PrepareViewFirst(NavigationPage navigationPage)
{
var navigationService = new ModalNavigationPageAdapter(navigationPage);
container.Instance<INavigationService>(navigationService);
container.Instance<IModalNavigationService>(navigationService);
}
}
We are registering our navigation service for INavigationService and IModalNavigationService.
As you can see in the comment, you have to implement close functions that call PopModalAsync by yourself.

MvvmCross Plugin Loading Issue in Windows Phone 8.1

I develop a Custom Plugin of MvvmCross for Windows Phone 8.1 and iOS
Its loading in iOS project but giving error in Windows Phone 8.1 at the manager.EnsurePlatformAdaptionLoaded();
public class PluginLoader : IMvxPluginLoader
{
public static readonly PluginLoader Instance = new PluginLoader();
public void EnsureLoaded()
{
var manager = Mvx.Resolve<IMvxPluginManager>();
manager.EnsurePlatformAdaptionLoaded<PluginLoader>();
}
}
Error is
An exception of type 'Cirrious.CrossCore.Exceptions.MvxException'
occurred in Cirrious.CrossCore.DLL but was not handled in user code
Additional information: could not load plugin assembly for type
Confiz.MvvmCross.Plugins.Timer.PluginLoader
I am loading my Plugin in Windows Phone 8.1 using the following code
public class TimerPluginBootstrap : MvxPluginBootstrapAction<Confiz.MvvmCross.Plugins.Timer.PluginLoader>
{
}
I am Using Windows Phone 8.1 and MvvmCross 3.2.1 and Following are the more info
Confiz.MvvmCross.Plugins.Timer(Portable)
ITimer.cs
public interface ITimer
{
void Start();
void Stop();
Action CallBackMethod { get; set; }
}
PluginLoader.cs
public class PluginLoader : IMvxPluginLoader
{
public static readonly PluginLoader Instance = new PluginLoader();
public void EnsureLoaded()
{
var manager = Mvx.Resolve<IMvxPluginManager>();
manager.EnsurePlatformAdaptionLoaded<PluginLoader>();
}
}
Confiz.MvvmCross.Plugins.Timer.WindowsPhone
MvxWindowsPhoneTimer.cs
public class MvxWindowsPhoneTimer : ITimer
{
private readonly DispatcherTimer timer;
public MvxWindowsPhoneTimer(double kronosApiRecallTimeInMinutes)
{
timer = new DispatcherTimer();
Timer.Interval = TimeSpan.FromSeconds(kronosApiRecallTimeInMinutes);
Timer.Tick += Timer_Tick;
}
void Timer_Tick(object sender, object e)
{
CallBackMethod.Invoke();
}
public DispatcherTimer Timer
{
get { return timer; }
}
public void Start()
{
Timer.Start();
}
public void Stop()
{
Timer.Stop();
}
public Action CallBackMethod { get; set; }
}
Plugin.cs
public class Plugin : IMvxPlugin
{
public void Load()
{
Mvx.RegisterSingleton<ITimer>(new MvxWindowsPhoneTimer(1));
}
}
The plugin extension name for Silverlight is WindowsPhone
For Jupiter Xaml Windows Phone apps, the extension name is WindowsCommon (if shared with Win81) and WindowsPhoneStore for Store only.
So Confiz.MvvmCross.Plugins.Timer.WindowsPhone.dll is for Silverlight
And Confiz.MvvmCross.Plugins.Timer.WindowsPhoneStore.dll is for Jupiter Xaml apps

Visual Studio 2013 Team Explorer extension not showing

I'm trying to build an extension to the team explorer in Visual Studio 2013. I've found the blog at http://31og.com/post/getting-start-with-a-team-explorer-plugin-for-vs-2013-part-3 which goes through the steps for adding a navigation item and a page but when I run the project I do not see the extension.
There are no errors in the output. I can see the constructor being called but nothing is added to team explorer.
Here is our current code:
namespace UoA.Cecil.VsTools.WindowPanes
{
using System;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Drawing;
using System.Runtime.CompilerServices;
using Microsoft.TeamFoundation.Controls;
using Microsoft.VisualStudio.Shell;
[TeamExplorerNavigationItem(TeamExplorerGuids.TimesheetNavigationItem, 100)]
public class TimesheetTeamExplorerNavigationItem
: ITeamExplorerNavigationItem
{
private readonly IServiceProvider serviceProvider;
private bool isVisible;
[ImportingConstructor]
public TimesheetTeamExplorerNavigationItem([Import(typeof(SVsServiceProvider))] IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public event PropertyChangedEventHandler PropertyChanged;
public Image Image
{
get { return Resources.TimesheetImage; }
}
public bool IsVisible
{
get { return this.isVisible; }
private set
{
this.isVisible = value;
this.FirePropertyChanged();
}
}
public string Text
{
get { return "Timesheet"; }
}
public void Dispose()
{
}
public void Execute()
{
// Do something here
}
public T GetService<T>()
{
if (this.serviceProvider != null)
{
return (T)this.serviceProvider.GetService(typeof(T));
}
return default(T);
}
public void Invalidate()
{
}
private void FirePropertyChanged([CallerMemberName] string propertyName = null)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Any ideas on why this would be happening?
You will be able to see the button often when you are connected to the Team Project. Verify if CurrentContext property is not null and carries your connected TFS context.
Alternatively, you can also verify if your serviceProvider field in the constructor is not null.
The visibility is usually handled in Invalidate method as below. I have implemented below for my Team Rooms extension.
public override void Invalidate()
{
if (CurrentContext != null && CurrentContext.HasCollection && CurrentContext.HasTeamProject)
{
IsVisible = true;
}
else
{
IsVisible = false;
}
}

MvvmCross plugin for setting up Alarms

I want to write a cross mobile platform app that sets up the alarm by specifying the required parameters like Date and Time. I just want to set up only one time and not repeatedly.
I was unable to find any readily available plugin in mvvmcross or in Xamarin ?
Please help
Since there is no existing plugin within MVVMCross, you may want to write your own plugin. You can find the documentation here:
https://github.com/MvvmCross/MvvmCross/wiki/MvvmCross-plugins
Because you'd like to specify a few parameters, you'd want to see the following section:
https://github.com/MvvmCross/MvvmCross/wiki/MvvmCross-plugins#writing-a-configurable-plugin
Overall this is what you might do:
General Interface
public interface IAlarm
{
void SetupAlarm();
}
public class PluginLoader
: IMvxPluginLoader
{
public static readonly PluginLoader Instance = new PluginLoader();
public void EnsureLoaded()
{
var manager = Mvx.Resolve<IMvxPluginManager>();
manager.EnsurePlatformAdaptionLoaded<PluginLoader>();
}
}
Android Implementation
public class DroidAlarmConfiguration
: IMvxPluginConfiguration
{
public AlarmLength { get; set;}
}
public class DroidAlarm : IAlarm
{
public TimeSpan AlarmLength { get; set; }
public void SetupAlarm()
{
//ALARM IMPLEMENTATION HERE. NOTE THIS IS SOME JAVA SYNTAX!!!!
var globals = Mvx.Resolve<Cirrious.CrossCore.Droid.IMvxAndroidGlobals>();
var alarm = globals.ApplicationContext
.GetSystemService(Context.ALARM_SERVICE)
as AlarmManager;
Intent intent = new Intent(context, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
alarmLength, alarmIntent);
}
}
public class Plugin
: IMvxPlugin
{
private _alarmLength = **Your Value Here**;
public void Configure(IMvxPluginConfiguration configuration)
{
if (configuration == null)
return;
var droidConfiguration = (DroidAlarmConfiguration)configuration;
_alarmLength = droidConfiguration.AlarmLength;
}
public void Load()
{
var instance = new DroidAlarm();
instance.AlarmLength = _AlarmLength;
Mvx.RegisterSingleton<IAlarm>(instance);
}
}
Setup.cs - To set the values in one core place for all android/ios/windows
protected override IMvxPluginConfiguration GetPluginConfiguration(Type plugin)
{
if (plugin == typeof(Yours.Alarm.Droid.Plugin))
{
return new Yours.Alarm.Droid.DroidAlarmConfiguration()
{
AlarmLength = **YOUR VALUE HERE**
};
}
return null;
}
You would then follow the same Droid step for iOS and Windows Phone. I hope this helps!

Resources