You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Xamarin MvvmCross中通过BottomNavigationView导航视图模型

Hey there! Let's walk through how to get your BottomNavigationView navigation working properly with MvvmCross 6.0.1 while sticking strictly to MVVM and MvvmCross best practices.


Your existing project structure (host MainContainerActivity/MainContainerViewModel + individual Fragment/ViewModel pairs) is already aligned with MvvmCross's ViewModel-first navigation philosophy. Here's how to flesh out the navigation logic:

a. Add Navigation Commands to MainContainerViewModel

First, define MvxAsyncCommand properties in your host ViewModel to handle navigation triggers. Use MvvmCross's built-in IMvxNavigationService to handle the heavy lifting of ViewModel/Fragment resolution:

public class MainContainerViewModel : MvxViewModel
{
    private readonly IMvxNavigationService _navigationService;
    private int _selectedMenuItemId;

    // Navigation commands
    public ICommand NavigateToMainCommand { get; }
    public ICommand NavigateToSettingsCommand { get; }
    public ICommand NavigateToFavoritesCommand { get; }

    // Bindable property for bottom nav selected state
    public int SelectedMenuItemId
    {
        get => _selectedMenuItemId;
        set => SetProperty(ref _selectedMenuItemId, value);
    }

    public MainContainerViewModel(IMvxNavigationService navigationService)
    {
        _navigationService = navigationService;
        _selectedMenuItemId = Resource.Id.menu_main; // Initialize with first tab

        // Initialize commands with navigation logic
        NavigateToMainCommand = new MvxAsyncCommand(async () => await NavigateToTab<MainViewModel>(Resource.Id.menu_main));
        NavigateToSettingsCommand = new MvxAsyncCommand(async () => await NavigateToTab<SettingsViewModel>(Resource.Id.menu_settings));
        NavigateToFavoritesCommand = new MvxAsyncCommand(async () => await NavigateToTab<FavoritesViewModel>(Resource.Id.menu_favorites));
    }

    private async Task NavigateToTab<TViewModel>(int menuItemId) where TViewModel : IMvxViewModel
    {
        // Skip navigation if we're already on the target tab
        var currentVm = Mvx.IoCProvider.Resolve<IMvxCurrentViewModel>().CurrentViewModel;
        if (currentVm is TViewModel) return;

        // Navigate to the target ViewModel and update selected menu state
        await _navigationService.Navigate<TViewModel>();
        SelectedMenuItemId = menuItemId;
    }
}

b. Bind BottomNavigationView to ViewModel in MainContainerActivity

Use MvvmCross's binding system to link menu item clicks to your ViewModel commands, and sync the selected state with the SelectedMenuItemId property:

public class MainContainerActivity : MvxAppCompatActivity<MainContainerViewModel>
{
    private BottomNavigationView _bottomNav;

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        SetContentView(Resource.Layout.activity_main_container);

        _bottomNav = FindViewById<BottomNavigationView>(Resource.Id.bottom_navigation);

        // Setup data bindings
        var bindingSet = this.CreateBindingSet<MainContainerActivity, MainContainerViewModel>();
        
        // Bind menu item clicks to corresponding commands
        bindingSet.Bind(_bottomNav)
            .For(v => v.ItemClick)
            .To(vm => vm.NavigateToMainCommand)
            .WithConversion(new BottomNavItemMatcher(Resource.Id.menu_main));
        
        bindingSet.Bind(_bottomNav)
            .For(v => v.ItemClick)
            .To(vm => vm.NavigateToSettingsCommand)
            .WithConversion(new BottomNavItemMatcher(Resource.Id.menu_settings));
        
        bindingSet.Bind(_bottomNav)
            .For(v => v.ItemClick)
            .To(vm => vm.NavigateToFavoritesCommand)
            .WithConversion(new BottomNavItemMatcher(Resource.Id.menu_favorites));
        
        // Sync selected menu state with ViewModel
        bindingSet.Bind(_bottomNav).For(v => v.SelectedItemId).To(vm => vm.SelectedMenuItemId);
        
        bindingSet.Apply();

        // Load initial fragment if first launch
        if (savedInstanceState == null)
        {
            ViewModel.NavigateToMainCommand.Execute(null);
        }
    }
}

You'll need a custom value converter to trigger the command only when the correct menu item is clicked:

public class BottomNavItemMatcher : MvxValueConverter<BottomNavigationView.ItemSelectedEventArgs, object>
{
    private readonly int _targetItemId;

    public BottomNavItemMatcher(int targetItemId) => _targetItemId = targetItemId;

    protected override object Convert(BottomNavigationView.ItemSelectedEventArgs value, Type targetType, object parameter, CultureInfo culture)
    {
        // Only pass the command parameter if the clicked item matches our target
        return value.Item.ItemId == _targetItemId ? parameter : MvxBindingConstant.UnsetValue;
    }
}

c. Configure ViewModel-Fragment Mappings

Tell MvvmCross which Fragment corresponds to each ViewModel by updating your Setup.cs:

protected override void InitializeViewLookup()
{
    base.InitializeViewLookup();
    var viewMappings = new Dictionary<Type, Type>
    {
        { typeof(MainViewModel), typeof(MainFragment) },
        { typeof(SettingsViewModel), typeof(SettingsFragment) },
        { typeof(FavoritesViewModel), typeof(FavoritesFragment) }
    };
    Mvx.IoCProvider.RegisterSingleton<IMvxViewsContainer>(new MvxViewsContainer(viewMappings));
}

Ensure your activity has a FrameLayout container (with ID fragment_container) where MvvmCross will load the fragments.


2. Is Your Current Setup MVVM & MvvmCross Compliant?

Short answer: Yes, your structure is fully aligned with MVVM and MvvmCross best practices—here's why:

  • You're following the ViewModel-first pattern: Navigation logic lives in the ViewModel, not the Activity/Fragment.
  • Each UI component (Fragment) has its own dedicated ViewModel, separating business logic from presentation code.
  • The host MainContainerViewModel acts as a navigation coordinator, which is a clean way to manage bottom tab navigation in MVVM.

Just make sure you avoid:

  • Writing direct Fragment replacement code in your Activity/Fragment (let MvvmCross handle this via navigation service).
  • Putting business logic in views—keep all state and logic in ViewModels.

3. Bonus Tips

  • Handle Fragment Lifecycle: Use MvvmCross's IMvxViewModelLifecycle interface in your Fragment ViewModels to react to Appearing/Disappearing events (useful for refreshing data when a tab is selected).
  • Avoid Duplicate Fragments: The check for the current ViewModel in NavigateToTab prevents redundant Fragment creation.
  • Animate Transitions: You can customize Fragment transitions by modifying the MvvmCross presenter in Setup.cs if needed.

内容的提问来源于stack exchange,提问作者Dominik Roszkowski

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:29:29