使用基于Type(或Previous ViewModel)的现有委托Factory获取实例

时间:2015-06-24 10:06:14

标签: c# wpf delegates factory wizard

基于this page,我们创建了一个包含三个步骤的向导。一切都很好,但我们对链接中给出的代码有一个问题,就是它如何创建下一个步骤实例(从链接粘贴的副本):

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处理其余的参数。对于这三个步骤中的每一步,我们只需要使用delegate Factory的{​​{1}}。

因为所有三个state仅使用delegate Factory,所以我将这个工厂放在他们的基类中:

state

我想如何更改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; } 您想要的任何方式。只要我能够根据地图中的前一个Step-ViewModel获得下一个Step-ViewModel。

注意:其他相关代码可以在链接中找到,但我会在这里发布以便将它们保存在一起:

GetNextScreenFactoryInstance(只有更改不再是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

1 个答案:

答案 0 :(得分:0)

我们已经更改了代码:

WizardTransitionMap现在接受代理。此外,我们现在不是通过WizardTransition-enum值(Next,Previous等)检索类型,而是根据下一个Type检索Factory-invoke(因此内部Dictionary被反转)。所以,这是我们当前的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);