How to register service with platform code and use it?

Igor Kravchenko 281 Reputation points
2022-05-04T19:37:19.207+00:00

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;
        }
    }
.NET MAUI
.NET MAUI
A Microsoft open-source framework for building native device applications spanning mobile, tablet, and desktop.
3,596 questions
0 comments No comments
{count} votes

Accepted answer
  1. Leon Lu (Shanghai Wicresoft Co,.Ltd.) 76,551 Reputation points Microsoft Vendor
    2022-05-05T05:57:27.28+00:00

    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.


0 additional answers

Sort by: Most helpful

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.