MvvmCross 6(Xamarin.Forms)能否实现ViewModel优先导航至自定义XAML弹窗?
当然可以实现!我之前在MvvmCross 6.x版本里折腾过类似的需求,你遇到的问题大概率是对MvxFormsModalPresentationAttribute的使用方式理解错了——这个特性是标记ViewModel的,不是直接加在MvxContentView上的。下面给你一步步讲清楚怎么搞定:
1. 先把弹窗的View和ViewModel配正确
首先,你的自定义弹窗得是一个MvxFormsPage(如果是全屏模态弹窗),要是想做非全屏的悬浮弹窗,后面我再补充说明。先看基础的全屏自定义弹窗:
自定义弹窗XAML(View)
<?xml version="1.0" encoding="utf-8" ?> <mvx:MvxFormsPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:mvx="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms" x:Class="YourApp.Views.CustomPopupView"> <ContentPage.Content> <!-- 这里写你的弹窗内容,比如带关闭按钮的布局 --> <StackLayout Padding="24" BackgroundColor="White" CornerRadius="12" WidthRequest="300" HeightRequest="200"> <Label Text="这是我的自定义弹窗" FontSize="Title" Margin="0,0,0,16"/> <Button Text="关闭弹窗" Command="{Binding ClosePopupCommand}" /> </StackLayout> </ContentPage.Content> </mvx:MvxFormsPage>
对应的ViewModel(重点!这里加特性)
using MvvmCross.Commands; using MvvmCross.Navigation; using MvvmCross.ViewModels; using MvvmCross.Forms.Presenters.Attributes; namespace YourApp.ViewModels { // 这里用MvxFormsModalPresentationAttribute标记ViewModel,指定呈现样式 [MvxFormsModalPresentation(ModalPresentationStyle = UIModalPresentationStyle.FormSheet, WrapInNavigationPage = false)] public class CustomPopupViewModel : MvxViewModel { private readonly IMvxNavigationService _navigationService; // 关闭弹窗的命令 public IMvxCommand ClosePopupCommand => new MvxAsyncCommand(async () => { await _navigationService.Close(this); }); public CustomPopupViewModel(IMvxNavigationService navigationService) { _navigationService = navigationService; } } }
2. 从其他ViewModel触发导航
在你需要打开弹窗的ViewModel里,注入IMvxNavigationService,然后直接导航到弹窗的ViewModel就行——这就是ViewModel优先的核心,不用管具体的View:
// 在宿主ViewModel的构造函数里注入IMvxNavigationService private readonly IMvxNavigationService _navigationService; public MainViewModel(IMvxNavigationService navigationService) { _navigationService = navigationService; } // 触发弹窗的命令 public IMvxCommand OpenPopupCommand => new MvxAsyncCommand(async () => { await _navigationService.Navigate<CustomPopupViewModel>(); });
3. 解决你之前的误区:为什么标记MvxContentView没用?
MvxFormsModalPresentationAttribute是用来告诉MvvmCross“这个ViewModel对应的View要以模态方式呈现”,所以得加在ViewModel上,而不是View(比如你的MvxContentView)上。你之前把特性加在View上,MvvmCross根本不会识别这个配置,自然达不到预期效果。
4. 要是想做非全屏悬浮弹窗怎么办?
如果你的需求是那种悬浮在页面上的小弹窗(不是全屏模态),那MvvmCross默认的模态导航满足不了,得结合第三方弹窗库(比如Rg.Plugins.Popup),然后自定义MvvmCross的呈现器来处理这种弹窗类型。步骤大概是:
- 安装第三方弹窗包
- 让你的弹窗View继承自
PopupPage并实现IMvxView接口 - 自定义一个
MvxFormsPopupPresenter来处理弹窗导航逻辑 - 在Setup类里替换默认呈现器为自定义的那个
不过这个稍微复杂点,如果你只是需要全屏模态的自定义弹窗,上面的基础方案就足够了。
关于文档缺失的问题
确实,MvvmCross 6.0.0-beta5当时的Forms文档很不完善,我当时也是靠翻官方示例项目和GitHub源码里的测试用例才搞明白的,你可以去MvvmCross的官方仓库里找Forms相关的示例,里面有不少导航的案例。
内容的提问来源于stack exchange,提问作者BorbMizzet

