首页 > 解决方案 > Xamarin 在 PushAsync() 之前初始化 ViewModel

问题描述

我有一个带有登录页面的 Xamarin 应用程序,我使用 ServiceContainer 注册了我的数据库、警报、媒体和导航服务。然后我尝试导航到登录页面,但在它出现在屏幕上之前,它会为每个 ViewModel 调用构造函数,我不知道为什么???

public partial class App : Application
{
    INavigationService NavigationService { get; set; }

    public App()
    {
        InitializeComponent();
        RegisterRepositories();
        RegisterServices();
        NavigationService.ReplaceRoot(ServiceContainer.GetInstance<LoginViewModel>(), false);   
    }

    void RegisterServices()
    {
        ServiceContainer.Register<IAlertService>(() => new AlertService());
        ServiceContainer.Register<IMediaService>(() => new MediaService());
        
        NavigationService = new NavigationService();
        NavigationService.AutoRegister(typeof(App).Assembly);

        ServiceContainer.Register(NavigationService);
    }

    void RegisterRepositories()
    {
        ServiceContainer.Register<IUserProfileRepository>(() => new UserProfileRepository());
    }

服务容器:

public static class ServiceContainer
{
    static readonly Container _container = new Container();

    public static void Register<TService, TImplementation>(bool transient = false) where TService : class where TImplementation : class, TService
    {
        Lifestyle style = transient ? Lifestyle.Transient : Lifestyle.Singleton;
        _container.Register<TService, TImplementation>(style);
    }

    public static void Register<TService>(Func<TService> generator, bool transient = false) where TService : class
    {
        Lifestyle style = transient ? Lifestyle.Transient : Lifestyle.Singleton;
        _container.Register(generator, style);
    }

    public static void Register(Type serviceType, Type implementationType, bool isTransient = false)
    {
        if (isTransient)
        {
            _container.Register(serviceType, implementationType, Lifestyle.Transient);
        }
        else
        {
            _container.Register(serviceType, implementationType, Lifestyle.Singleton);
        }
    }

    public static void Register<TService>(TService instance) where TService : class
    {
        _container.RegisterInstance(instance);
    }

    public static T GetInstance<T>() where T : class
    {
        try
        {
            return _container.GetInstance<T>();
        }
        catch (ActivationException)
        {
            return null;
        }
    }

    internal static T GetRequiredInstance<T>() where T : class
    {
        return GetInstance<T>() ?? throw new InvalidOperationException(
                   $@"A required dependency injection class is missing ({typeof(T).FullName}).");
    }
}

导航服务:

public interface IViewFor
{
    object ViewModel { get; set; }
}

public interface IViewFor<T> : IViewFor where T : BaseViewModel
{
    new T ViewModel { get; set; }
}

public class NavigationService : INavigationService
{
    INavigation FormsNavigation => Application.Current.MainPage.Navigation;
    readonly Dictionary<Type, Type> _viewModelViewDictionary = new Dictionary<Type, Type>();

    public void AutoRegister(Assembly asm)
    {
        // Loop through everything in the assembly that implements IViewFor<T>
        foreach (var type in asm.DefinedTypes.Where(dt => !dt.IsAbstract &&
                    dt.ImplementedInterfaces.Any(ii => ii == typeof(IViewFor))))
        {
            // Get the IViewFor<T> portion of the type that implements it
            var viewForType = type.ImplementedInterfaces.FirstOrDefault(
                ii => ii.IsConstructedGenericType &&
                ii.GetGenericTypeDefinition() == typeof(IViewFor<>));

            // Register it, using the T as the key and the view as the value
            Register(viewForType.GenericTypeArguments[0], type.AsType());

            ServiceContainer.Register(viewForType.GenericTypeArguments[0], viewForType.GenericTypeArguments[0], true);
        }
    }

    public void Register(Type viewModelType, Type viewType)
    {
        if (!_viewModelViewDictionary.ContainsKey(viewModelType))
        {
            _viewModelViewDictionary.Add(viewModelType, viewType);
        }
    }

    public void ReplaceRoot<T>(bool withNavigationEnabled = true) where T : BaseViewModel
    {
        ReplaceRoot(ServiceContainer.GetInstance<T>(), withNavigationEnabled);
    }

    public void ReplaceRoot(BaseViewModel viewModel, bool withNavigationEnabled = true)
    {
        if (InstantiateView(viewModel) is Page view)
        {
            if (withNavigationEnabled)
            {
                Application.Current.MainPage = new NavigationPage(view);
            }
            else
            {
                Application.Current.MainPage = view;
            }
        }
    }

    public Task PopAsync() => FormsNavigation.PopAsync(true);
    public Task PopToRootAsync(bool animate) => FormsNavigation.PopToRootAsync(animate);
    public Task PushAsync(BaseViewModel viewModel) => FormsNavigation.PushAsync((Page)InstantiateView(viewModel));

    IViewFor InstantiateView(BaseViewModel viewModel)
    {
        var viewModelType = viewModel.GetType();
        var viewType = _viewModelViewDictionary[viewModelType];
        var view = (IViewFor)Activator.CreateInstance(viewType);
        view.ViewModel = viewModel;
        return view;
    }
}

标签: c#xamarin.formsxamarin.android

解决方案


推荐阅读