首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在DisplayAlert中实现.NET毛伊岛ViewModel

如何在DisplayAlert中实现.NET毛伊岛ViewModel
EN

Stack Overflow用户
提问于 2022-05-30 04:07:19
回答 3查看 6.8K关注 0票数 7

我在微软学习上走过了"使用.NET MAUI构建移动和桌面应用程序“之路。现在我有了一个简单的MAUI应用程序,我正在尝试使用CommunityToolkit.MVVM来实现MVVM。

该课程有一个名为OnCall的单击事件,如下所示

代码语言:javascript
运行
复制
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中,并使它成为一个命令,如下所示

代码语言:javascript
运行
复制
[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");
      }
   }
}

我的问题是如何从DisplayAlert中的命令调用ViewModel。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2022-05-30 20:29:52

虽然Adarsh的回答显示了基本的调用,但直接引用该UI方法意味着您的视图模型“知道”该UI方法。这很好(如果代码位于主线程(Dispatcher)线程上;如果不是,您将得到“错误线程”异常),但是如果您以后想添加“单元测试”,则会干扰可测试性。它还被认为是保持视图模型独立于UI代码的良好实践。

可以通过interface访问注册服务来避免这种情况。

我对杰拉尔德的回答使用了以下变体。

MauiProgram.cs:

代码语言:javascript
运行
复制
    ...
    public static MauiApp CreateMauiApp()
    {
        ...
        builder.Services.AddSingleton<IAlertService, AlertService>();
        ...

App.xaml.cs (跨平台的,其中设置了MainPage ):

代码语言:javascript
运行
复制
    ...
    public static IServiceProvider Services;
    public static IAlertService AlertSvc;

    public App(IServiceProvider provider)
    {
        InitializeComponent();

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

        MainPage = ...
    }

其他文件中的接口和类声明:

代码语言:javascript
运行
复制
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);
        });
    }
}

下面是测试,表明可以从任何地方调用“失火和遗忘”方法:

代码语言:javascript
运行
复制
Task.Run(async () =>
{
    await Task.Delay(2000);
    App.AlertSvc.ShowConfirmation("Title", "Confirmation message.", (result =>
    {
        App.AlertSvc.ShowAlert("Result", $"{result}");
    }));
});

注意:如果您使用"...Async“方法,而不是在窗口的Dispatcher线程(主线程)上,那么在运行时您将得到一个错误的线程异常。

信用:杰拉尔德对另一个问题的回答展示了如何获得毛伊岛的IServiceProvider。

票数 8
EN

Stack Overflow用户

发布于 2022-05-30 14:22:34

有多种方法可以做到这一点。最简单的办法是:

代码语言:javascript
运行
复制
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

为了使它更具可维护性(并且潜在的依赖注入友好),您可以将它封装在一个服务中,例如,如下所示:

代码语言:javascript
运行
复制
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这样的插件(在版本8时支持.NET MAUI )。基本上,这是创建自己在当前可见页面上显示对话框的实现,并为您提供该对话框的服务,以便与MVVM场景一起使用。

票数 4
EN

Stack Overflow用户

发布于 2022-05-30 04:28:44

这就是你要找的吗?

代码语言:javascript
运行
复制
bool x =  await Application.Current.MainPage.DisplayAlert("Tittle","Hello","OK","NotOK");
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72429055

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档