簡體   English   中英

如何在 .NET MAUI ViewModel 中顯示警報

[英]How to DisplayAlert in a .NET MAUI ViewModel

我在 Microsoft Learn 上瀏覽了“ 使用 .NET MAUI 構建移動和桌面應用程序”路徑。 現在我有一個簡單的 MAUI 應用程序,我正在嘗試使用CommunityToolkit.MVVM使其成為 MVVM。

該課程有一個名為OnCall的點擊事件,如下所示

private async void OnCall(object sender, EventArgs e)
{
   var confirmCall = DisplayAlert(
      "Dial a Number",
      $"Would you like to call {translatedNumber}?",
      "Yes",
      "No"
   );

   if (await confirmCall)
   {
      try
      {
         PhoneDialer.Open(translatedNumber);
      }
      catch (ArgumentNullException)
      {
         await DisplayAlert("Unable to dial", "Phone number was not valid.", "OK");
      }
      catch (FeatureNotSupportedException)
      {
         await DisplayAlert("Unable to dial", "Phone dialing not supported.", "OK");
      }
      catch (Exception)
      {
         await DisplayAlert("Unable to dial", "Phone dialing failed.", "OK");
      }
   }
}

所以我把它移到了我的 ViewModel 並把它變成了一個命令,就像這樣

[ICommand]
public async void OnCall ()
{
   var confirmCall = DisplayAlert(
      "Dial a Number",
      $"Would you like to call {translatedNumber}?",
      "Yes",
      "No"
   );

   if (await confirmCall)
   {
      try
      {
         PhoneDialer.Open(translatedNumber);
      }
      catch (ArgumentNullException)
      {
         await DisplayAlert("Unable to dial", "Phone number was not valid.", "OK");
      }
      catch (FeatureNotSupportedException)
      {
         await DisplayAlert("Unable to dial", "Phone dialing not supported.", "OK");
      }
      catch (Exception)
      {
         await DisplayAlert("Unable to dial", "Phone dialing failed.", "OK");
      }
   }
}

我的問題是如何從 ViewModel 中的命令調用DisplayAlert

有多種方法可以做到這一點。 最簡單的是這樣的:

if (await confirmCall)
{
   try
   {
      PhoneDialer.Open(translatedNumber);
   }
   catch (ArgumentNullException)
   {
      await Application.Current.MainPage.DisplayAlert("Unable to dial", "Phone number was not valid.", "OK");
   }
   catch (FeatureNotSupportedException)
   {
      await Application.Current.MainPage.DisplayAlert("Unable to dial", "Phone dialing not supported.", "OK");
   }
   catch (Exception)
   {
      await Application.Current.MainPage.DisplayAlert("Unable to dial", "Phone dialing failed.", "OK");
   }
}

它的作用是通過Application對象找到當前頁面並在其上調用DisplayAlert

為了使其更易於維護(並且可能對依賴注入友好),您可以將其包裝在服務中,例如像這樣簡單:

public class DialogService : IDialogService
{
    public async Task<string> DisplayActionSheet(string title, string cancel, string destruction, params string[] buttons)
    {
        return await Application.Current.MainPage.DisplayActionSheet(title, cancel, destruction, buttons);
    }

    public async Task<bool> DisplayConfirm(string title, string message, string accept, string cancel)
    {
        return await Application.Current.MainPage.DisplayAlert(title, message, accept, cancel);
    }
}

現在您可以創建該服務的一個實例,如果在某個時候您想以另一種方式顯示您的對話框,您可以在此處交換實現。

如果您決定也添加接口並將其注冊到依賴注入容器中,您還可以讓服務被注入並更容易地替換實現或取決於其他潛在變量。

第三種選擇是查看ACR.UserDialogs 之類的插件(即將支持 .NET MAUI)。 基本上,它所做的是創建自己的實現,在當前可見頁面上顯示一個對話框,並為您提供開箱即用的服務,以便與 MVVM 場景一起使用。

雖然 Adarsh 的回答顯示了基本調用,但直接引用該 UI 方法意味着您的視圖模型“知道”該 UI 方法。 這很好用(如果代碼在主(調度程序)線程上;如果不是,你會得到“錯誤線程”異常),但是如果你以后想添加“單元測試”,會干擾可測試性。 它也被認為是保持視圖模型獨立於 UI 代碼的良好做法。

這可以通過通過interface訪問已注冊服務來避免。

我對傑拉德的回答使用了以下變體。

MauiProgram.cs:

    ...
    public static MauiApp CreateMauiApp()
    {
        ...
        builder.Services.AddSingleton<IAlertService, AlertService>();
        ...

App.xaml.cs(跨平台的,設置了 MainPage):

    ...
    public static IServiceProvider Services;
    public static IAlertService AlertSvc;

    public App(IServiceProvider provider)
    {
        InitializeComponent();

        Services = provider;
        AlertSvc = Services.GetService<IAlertService>();

        MainPage = ...
    }

其他文件中接口和類的聲明:

public interface IAlertService
{
    // ----- async calls (use with "await" - MUST BE ON DISPATCHER THREAD) -----
    Task ShowAlertAsync(string title, string message, string cancel = "OK");
    Task<bool> ShowConfirmationAsync(string title, string message, string accept = "Yes", string cancel = "No");

    // ----- "Fire and forget" calls -----
    void ShowAlert(string title, string message, string cancel = "OK");
    /// <param name="callback">Action to perform afterwards.</param>
    void ShowConfirmation(string title, string message, Action<bool> callback,
                          string accept = "Yes", string cancel = "No");
}

internal class AlertService : IAlertService
{
    // ----- async calls (use with "await" - MUST BE ON DISPATCHER THREAD) -----

    public Task ShowAlertAsync(string title, string message, string cancel = "OK")
    {
        return Application.Current.MainPage.DisplayAlert(title, message, cancel);
    }

    public Task<bool> ShowConfirmationAsync(string title, string message, string accept = "Yes", string cancel = "No")
    {
        return Application.Current.MainPage.DisplayAlert(title, message, accept, cancel);
    }


    // ----- "Fire and forget" calls -----

    /// <summary>
    /// "Fire and forget". Method returns BEFORE showing alert.
    /// </summary>
    public void ShowAlert(string title, string message, string cancel = "OK")
    {
        Application.Current.MainPage.Dispatcher.Dispatch(async () =>
            await ShowAlertAsync(title, message, cancel)
        );
    }

    /// <summary>
    /// "Fire and forget". Method returns BEFORE showing alert.
    /// </summary>
    /// <param name="callback">Action to perform afterwards.</param>
    public void ShowConfirmation(string title, string message, Action<bool> callback,
                                 string accept="Yes", string cancel = "No")
    {
        Application.Current.MainPage.Dispatcher.Dispatch(async () =>
        {
            bool answer = await ShowConfirmationAsync(title, message, accept, cancel);
            callback(answer);
        });
    }
}

這是測試,表明可以從任何地方調用“即發即棄”方法:

Task.Run(async () =>
{
    await Task.Delay(2000);
    App.AlertSvc.ShowConfirmation("Title", "Confirmation message.", (result =>
    {
        App.AlertSvc.ShowAlert("Result", $"{result}");
    }));
});

注意:如果您改為使用“...異步”方法,但不在窗口的調度程序線程(主線程)上,則在運行時您將收到錯誤的線程異常。

信用:傑拉德對另一個問題的回答顯示了如何獲得毛伊島的 IServiceProvider。

這是你要找的嗎?

bool x =  await Application.Current.MainPage.DisplayAlert("Tittle","Hello","OK","NotOK");

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2025 STACKOOM.COM