question

IgorKravchenko-7896 avatar image
0 Votes"
IgorKravchenko-7896 asked LeonLu-MSFT edited

How to register service with platform code and use it?

I need to show material alert dialog. I have created implementation and want to use it in any place of my code.
I know that in MAUI we can register class: builder.Services.AddSingleton(typeof(IDialogService), typeof(DialogService));
How to use this instead of injecting in constructor? Because I prefer viewModels with empty constructors.
For example like in Xamarin Forms: DependencyService.Get<IDialogService>();
In blazor we can write something like @inject IDialogService DialogService

I remember in Xamarin Forms creating singleton class is popular solution:

 public class MenuService : IMenuService
     {
         private static Lazy<IMenuService> _instance = new Lazy<IMenuService>(() => new MenuService(), System.Threading.LazyThreadSafetyMode.PublicationOnly);
    
         public static IMenuService Instance => _instance.Value;
    
         public Task<string> ShowMenu(View view, string[] items) =>
             DependencyService.Get<IMenuService>().ShowMenu(view, items);
     }

I want to create something similar to this approach for my maui service. How to do this properly?
My implementation:

 public interface IDialogService
     {
         Task<string> ShowDialog(string title, string message, string accept, string cancel, string neutral);
         Task<bool> ShowDialog(string title, string message, string accept, string cancel);
     }
    
     public partial class DialogService : IDialogService
     {
         public partial Task<string> ShowDialog(string title, string message, string accept, string cancel, string neutral);
         public partial Task<bool> ShowDialog(string title, string message, string accept, string cancel);
     }

Android implementation:

 public partial class DialogService : IDialogService
     {
         static Context context;
         internal static Context Context
         {
             get
             {
                 var page = Application.Current.MainPage;
                 var renderer = page.GetRenderer();
    
                 if (renderer?.View.Context != null)
                     context = renderer.View.Context;
    
                 return renderer?.View.Context ?? context ?? throw new NullReferenceException($"{nameof(Context)} cannot be null");
             }
         }
    
         private ISpanned FromHtml(string message)
         {
             if (Build.VERSION.SdkInt >= BuildVersionCodes.N)
                 return Html.FromHtml(message, FromHtmlOptions.ModeLegacy);
             return Html.FromHtml(message);
         }
    
         public partial Task<string> ShowDialog(string title, string message, string accept, string cancel, string neutral)
         {
             TaskCompletionSource<string> taskCompletionSource = new TaskCompletionSource<string>();
             MaterialAlertDialogBuilder alertBuilder = new MaterialAlertDialogBuilder(Context);
             alertBuilder
                 .SetTitle(title)
                 .SetMessage(FromHtml(message))
                 .SetPositiveButton(accept, (sender, args) => taskCompletionSource.SetResult(accept));
             if (cancel != null)
                 alertBuilder.SetNegativeButton(cancel, (sender, args) => taskCompletionSource.SetResult(cancel));
             if (neutral != null)
                 alertBuilder.SetNeutralButton(neutral, (sender, args) => taskCompletionSource.SetResult(neutral));
             AndroidX.AppCompat.App.AlertDialog dialog = alertBuilder.Create();
             dialog.CancelEvent += (object sender, EventArgs e) => taskCompletionSource?.SetResult(null);
             dialog.Show();
             return taskCompletionSource.Task;
         }
    
         public partial Task<bool> ShowDialog(string title, string message, string accept, string cancel)
         {
             TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
             MaterialAlertDialogBuilder alertBuilder = new MaterialAlertDialogBuilder(Context);
             alertBuilder
                 .SetTitle(title)
                 .SetMessage(FromHtml(message))
                 .SetPositiveButton(accept, (sender, args) => taskCompletionSource.SetResult(true))
                 .SetNegativeButton(cancel, (sender, args) => taskCompletionSource.SetResult(false));
             AndroidX.AppCompat.App.AlertDialog dialog = alertBuilder.Create();
             dialog.CancelEvent += (object sender, EventArgs e) => taskCompletionSource?.SetResult(false);
             dialog.Show();
             return taskCompletionSource.Task;
         }
     }





dotnet-maui
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

1 Answer

LeonLu-MSFT avatar image
0 Votes"
LeonLu-MSFT answered LeonLu-MSFT edited

Hello,​

When you want to use this services, It can be invoked from cross-platform code by creating an object instance and invoking its operation like following code.

internal class MyViewModel
{
    public MyViewModel()
    {
     }

    private void PopAlert()
    {
        IDialogService dialogService = new DialogService();
        dialogService.ShowDialog("info", "this is a test dialog", "OK", "NO");
  }
}


Here is article about :Invoke the cross-platform API.

I test your code in my side, I find you need to set Material style, and your code get the empty context, so I change this initialize MaterialAlertDialogBuilder code.

MaterialAlertDialogBuilder alertBuilder = new MaterialAlertDialogBuilder(MainActivity.InStance, Resource.Style.MaterialComponentsAlert);


Add a style in styles.xml.

<resources>
<style name="MaterialComponentsAlert" parent="Theme.MaterialComponents.Dialog.Alert">
</style>
</resources>


Create Instance in the as context in the MainActivity.cs.

...
public class MainActivity : MauiAppCompatActivity
{
//add this property
    public static MainActivity InStance { get; set; }
    protected override void OnCreate(Bundle savedInstanceState){
//set it
        InStance = this;
....

}


Best Regards,

Leon Lu



If the answer is the right solution, please click "Accept Answer" and kindly upvote it. If you have extra questions about this answer, please click "Comment".

Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.




· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thank you.
I don't want to create a new instance of service all time. I have implemented singleton in service.

 public partial class DialogService : IDialogService
     {
         private static Lazy<IDialogService> _instance = new Lazy<IDialogService>(() => new DialogService(), System.Threading.LazyThreadSafetyMode.PublicationOnly);
         public static IDialogService Instance => _instance.Value;
    
         public partial Task<string> ShowDialog(string title, string message, string accept, string cancel, string neutral);
         public partial Task<bool> ShowDialog(string title, string message, string accept, string cancel);
     }

But this doesn't resolve my main problem. Maybe I was non-clear in my post. My main question is how to inject implementation to viewModel with parameterless constructor. Because I want to have possibility to use view models in other projects (winforms, wpf, etc) but need empty constructors. Thanks.

Update: after web surfing I found the thing I need called "property injection". I mean something like this:

 [Inject]
 public IDialogService DialogService { get; set; }

Do we have something built in MAUI or should I use Autofac or something else? Thanks.

0 Votes 0 ·
LeonLu-MSFT avatar image LeonLu-MSFT IgorKravchenko-7896 ·

Do we have something built in MAUI?

No. Autofac is a third part plugin, you can open a issue in Autofac Github page. https://github.com/autofac/Autofac/issues

0 Votes 0 ·