使用基于类型(或以前的 ViewModel)的现有委托工厂获取实例

Get Instance using an existing delegate Factory based on Type (or Previous ViewModel)

this page 的基础上,我们创建了一个包含三个步骤的向导。一切正常,但 link 中给出的代码存在一个问题,即它如何创建下一步实例(从 link 复制粘贴):

protected override IScreen DetermineNextItemToActivate(IList<IScreen> list, int lastIndex)
{
    var theScreenThatJustClosed = list[lastIndex] as BaseViewModel;
    var state = theScreenThatJustClosed.WorkflowState;

    var nextScreenType = TransitionMap.GetNextScreenType(theScreenThatJustClosed);

    var nextScreen = Activator.CreateInstance(nextScreenType, state);

    return nextScreen as IScreen;
}

目前在我们的项目中是这样的:

protected override IWizardScreen DetermineNextItemToActivate(IList<IWizardScreen> list, int lastIndex)
{
    var theScreenThatJustClosed = list[lastIndex];
    if (theScreenThatJustClosed == null) throw new Exception("Expecting a screen here!");

    if (theScreenThatJustClosed.NextTransition == WizardTransition.Done)
    {
        TryClose(); // Close the entire Wizard
    }

    var state = theScreenThatJustClosed.WizardAggregateState;
    var nextScreenType = _map.GetNextScreenType(theScreenThatJustClosed);
    if (nextScreenType == null) return null;
    // TODO: CreateInstance requires all constructors for each WizardStep, even if they aren't needed. This should be different!
    var nextScreen = Activator.CreateInstance(nextScreenType, state, _applicationService, _wfdRegisterInstellingLookUp,
        _adresService, _userService, _documentStore, _windowManager, _fileStore, _fileUploadService, _dialogService,
        _eventAggregator, _aanstellingViewModelFactory);

    return nextScreen as IWizardScreen;
}

如您所见,我们在某些步骤中需要使用相当多的参数。在第 1 步中我们只需要两个,但由于 Activator.CreateInstance(nextScreenType, state, ...); 我们仍然需要通过所有这些。

我想要的是使用 delegate Factory。我们在项目的更多地方使用它们,让 AutoFac 处理其余参数。对于这三个步骤中的每一个,我们只需要一个使用 state.

delegate Factory

因为这三个都使用相同的 delegate Factory 而只是 state,我将这个工厂放在他们的基地 class:

public delegate WizardBaseViewModel<TViewModel> Factory(AggregateState state);

我想如何更改 DetermineNextItemToActivate 方法:

protected override IWizardScreen DetermineNextItemToActivate(IList<IWizardScreen> list, int lastIndex)
{
    var theScreenThatJustClosed = list[lastIndex];
    if (theScreenThatJustClosed == null) throw new Exception("Expecting a screen here!");

    if (theScreenThatJustClosed.NextTransition == WizardTransition.Done)
    {
        TryClose(); // Close the entire Wizard
    }

    return _map.GetNextScreenFactoryInstance(state);
}

但现在我一直在制作 GetNextScreenFactoryInstance 方法:

public IWizardScreen GetNextScreenFactoryInstance(IWizardScreen screenThatClosed)
{
    var state = screenThatClosed.WizardAggregateState;

    // This is where I'm stuck. How do I get the instance using the Factory, when I only know the previous ViewModel
    // ** Half-Pseudocode
    var nextType = GetNextScreenType(screenThatClosed);
    var viewModelFactory = get delegate factory based on type?;
    var invokedInstance = viewModelFactory.Invoke(state);
    // **

    return invokedInstance as IWizardScreen;
}

您可以随意更改 GetNextScreenFactoryInstance。只要我能根据地图中的上一个获得下一个 Step-ViewModel 即可。


注意:其他相关代码,可以在 link 中找到,但我将 post 放在这里,以便将它们放在一起:

WizardTransitionMap(唯一的变化是它不再是 Singleton,所以我们可以自己实例化一个地图):

public class WizardTransitionMap : Dictionary<Type, Dictionary<WizardTransition, Type>>
{
    public void Add<TIdentity, TResponse>(WizardTransition transition) 
        where TIdentity : IScreen 
        where TResponse : IScreen
    {
        if (!ContainsKey(typeof(TIdentity)))
        {
            Add(typeof(TIdentity), new Dictionary<WizardTransition, Type> { { transition, typeof(TResponse) } });
        }
        else
        {
            this[typeof(TIdentity)].Add(transition, typeof(TResponse));
        }
    }

    public Type GetNextScreenType(IWizardScreen screenThatClosed)
    {
        var identity = screenThatClosed.GetType();
        var transition = screenThatClosed.NextTransition;
        if (!transition.HasValue) return null;

        if (!ContainsKey(identity))
        {
            throw new InvalidOperationException(String.Format("There are no states transitions defined for state {0}", identity));
        }

        if (!this[identity].ContainsKey(transition.Value))
        {
            throw new InvalidOperationException(String.Format("There is no response setup for transition {0} from screen {1}", transition, identity));
        }

        return this[identity][transition.Value];
    }
}

我们的InitializeMap-方法:

protected override void InitializeMap()
{
    _map = new WizardTransitionMap();

    _map.Add<ScreenOneViewModel, ScreenTwoViewModel>(WizardTransition.Next);

    _map.Add<ScreenTwoViewModel, ScreenOneViewModel>(WizardTransition.Previous);
    _map.Add<ScreenTwoViewModel, ScreenThreeViewModel>(WizardTransition.Next);

    _map.Add<ScreenThreeViewModel, ScreenTwoViewModel>(WizardTransition.Previous);
    _map.Add<ScreenThreeViewModel, ScreenThreeViewModel>(WizardTransition.Done);
}

我们更改了代码:

WizardTransitionMap 现在接受委托。此外,我们现在不再根据 WizardTransition 枚举值(Next、Previous 等)检索类型,而是根据下一个类型检索工厂调用(因此内部字典被反转)。所以,这是我们当前的 WizardTransitionMap:

using System;
using System.Collections.Generic;

namespace NatWa.MidOffice.CustomControls.Wizard
{
    public class WizardTransitionMap : Dictionary<Type, Dictionary<Type, Delegate>>
    {
        public void Add<TCurrentScreenType, TNextScreenType>(Delegate delegateFactory)
        {
            if (!ContainsKey(typeof(TCurrentScreenType)))
            {
                Add(typeof(TCurrentScreenType), new Dictionary<Type, Delegate> { { typeof(TNextScreenType), delegateFactory } });
            }
            else
            {
                this[typeof(TCurrentScreenType)].Add(typeof(TNextScreenType), delegateFactory);
            }
        }

        public IWizardScreen GetNextScreen(IWizardScreen screenThatClosed)
        {
            var identity = screenThatClosed.GetType();
            var state = screenThatClosed.State;
            var transition = screenThatClosed.NextScreenType;

            if (!ContainsKey(identity))
            {
                throw new InvalidOperationException(String.Format("There are no states transitions defined for state {0}", identity));
            }

            if (!this[identity].ContainsKey(transition))
            {
                throw new InvalidOperationException(String.Format("There is no response setup for transition {0} from screen {1}", transition, identity));
            }

            if (this[identity][transition] == null)
                return null;

            return (IWizardScreen)this[identity][transition].DynamicInvoke(state);
        }
    }
}

我们的InitializeMap现在改成了这样:

protected override void InitializeMap()
{
    _map = new WizardTransitionMap();

    _map.Add<ScreenOneViewModel, ScreenTwoViewModel>(_screenTwoFactory);

    _map.Add<ScreenTwoViewModel, ScreenOneViewModel>(_screenOneFactory);
    _map.Add<ScreenTwoViewModel, ScreenThreeViewModel>(_screenThreeFactory);

    _map.Add<ScreenThreeViewModel, ScreenTwoViewModel>(_screenTwoFactory);
    _map.Add<ScreenThreeViewModel, ScreenThreeViewModel>(null);
}

我们的 DetemineNexttemToActivate 方法为此:

protected override IWizardScreen DetermineNextItemToActivate(IList<IWizardScreen> list, int previousIndex)
{
    var theScreenThatJustClosed = list[previousIndex];
    if (theScreenThatJustClosed == null) throw new Exception("Expecting a screen here!");

    var nextScreen = _map.GetNextScreen(theScreenThatJustClosed);

    if (nextScreen == null)
    {
        TryClose();
        return ActiveItem; // Can't return null here, because Caliburn's Conductor will automatically get into this method again with a retry
    }

    return nextScreen;
}

我们还删除了整个 WizardBaseViewModel,只让每个 Step-ViewModel 实现 IWizardScreen:

public interface IWizardScreen : IScreen
{
    AggregateState State { get; }
    Type NextScreenType { get; }

    void Next();
    void Previous();
}

在我们的 ScreenOneViewModel 中使用以下实现:

public AggregateState State { get { return _state; }  }
public Type NextScreenType { get; private set; }

public void Next()
{
    if (!IsValid()) return;

    NextScreenType = typeof(ScreenTwoViewModel);
    TryClose();
}

public void Previous()
{
    throw new NotImplementedException(); // Isn't needed in first screen, because we have no previous
}

以及我们的 ScreenThreeViewModel 中的以下实现:

public AggregateState State { get { return _state; } }
public Type NextScreenType { get; private set; }

public void Next()
{
    NextScreenType = typeof(ScreenThreeViewModel); // Own type, because we have no next
    TryClose();
}

public void Previous()
{
    NextScreenType = typeof(ScreenTwoViewModel);
    TryClose();
}

并且每个 Step-ViewModel 都有自己的 delegate Factory,例如 ScreenTwoViewModel 的这个:

public delegate ScreenTwoViewModel Factory(AggregateState state);