[英]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.