2011-10-17 5 views
5

私はC#の新機能で、カスタムIEnumerable列挙型を作成するためにyield returnを使用する方法を発見しました。ウィザードを作成するためにMVVMを使用しようとしていますが、あるページから次のページへのフローを制御する方法がわかりませんでした。場合によっては、特定のステップを表示したい場合がありますが、それ以外の場合は適用されません。IEnumerable/yieldを使用したウィザードのナビゲーション

私の問題は、IEnumerableを使用して後続の各ページを返すことです。実際にはうまくいきますが、おそらく言語で不適切な/意図しないことをしていることがわかります。子クラスは、抽象のステップにIEnumerableをアクセサをオーバーライドすることがあります。

public class HPLDTWizardViewModel : WizardBase 
{ 
    protected override IEnumerable<WizardStep> Steps 
    { 
    get 
    { 
     WizardStep currentStep; 

     // 1.a start with assay selection 
     currentStep = new AssaySelectionViewModel(); 
     yield return currentStep; 
     // 1.b return the selected assay. 
     SigaDataSet.Assay assay = ((AssaySelectionViewModel)currentStep).SelectedAssay; 
     sigaDataSet = (SigaDataSet)assay.Table.DataSet; 

     // 2.a get the number of plates 
     currentStep = new NumPlatesViewModel(sigaDataSet); 
     yield return currentStep; 
     ... 
    } 
    } 
} 

親クラスがステップを使用したナビゲーション・ロジックが含まれている列挙子を属性:

public abstract class WizardBase : ViewModelBase 
{ 
    private ICommand _moveNextCommand; 
    private ICommand _cancelCommand; 
    private IEnumerator<WizardStep> _currentStepEnumerator; 

    #region Events 

    /// <summary> 
    /// Raised when the wizard window should be closed. 
    /// </summary> 
    public event EventHandler RequestClose; 

    #endregion // Events 

    #region Public Properties 

    /// <summary> 
    /// Gets the steps. 
    /// </summary> 
    /// <value>The steps.</value> 
    protected abstract IEnumerable<WizardStep> Steps { get;} 

    /// <summary> 
    /// Gets the current step. 
    /// </summary> 
    /// <value>The current step.</value> 
    public WizardStep CurrentStep 
    { 
    get 
    { 
     if (_currentStepEnumerator == null) 
     { 
     _currentStepEnumerator = Steps.GetEnumerator(); 
     _currentStepEnumerator.MoveNext(); 
     } 

     return _currentStepEnumerator.Current; 
    } 
    } 

    #endregion //Public Properties 

    #region Commands 

    public ICommand MoveNextCommand 
    { 
    get 
    { 
     if (_moveNextCommand == null) 
     _moveNextCommand = new RelayCommand(
      () => this.MoveToNextPage(), 
      () => this.CanMoveToNextPage()); 

     return _moveNextCommand; 
    } 
    } 

    public ICommand CancelCommand 
    { 
    get 
    { 
     if (_cancelCommand == null) 
     _cancelCommand = new RelayCommand(() => OnRequestClose()); 

     return _cancelCommand; 
    } 
    } 

    #endregion //Commands 

    #region Private Helpers 

    /// <summary> 
    /// Determines whether this instance [can move to next page]. 
    /// </summary> 
    /// <returns> 
    /// <c>true</c> if this instance [can move to next page]; otherwise, <c>false</c>. 
    /// </returns> 
    bool CanMoveToNextPage() 
    { 
    if (CurrentStep == null) 
     return false; 
    else 
     return CurrentStep.IsValid(); 
    } 

    /// <summary> 
    /// Moves to next page. 
    /// </summary> 
    void MoveToNextPage() 
    { 
    _currentStepEnumerator.MoveNext(); 

    if (_currentStepEnumerator.Current == null) 
     OnRequestClose(); 
    else 
     OnPropertyChanged("CurrentStep"); 
    } 

    /// <summary> 
    /// Called when [request close]. 
    /// </summary> 
    void OnRequestClose() 
    { 
    EventHandler handler = this.RequestClose; 
    if (handler != null) 
     handler(this, EventArgs.Empty); 
    } 

    #endregion //Private Helpers 
} 

そして、ここではWizardStep抽象クラスは、それぞれがありますウィザードのページの実装:

public abstract class WizardStep : ViewModelBase 
{ 
    public abstract string DisplayName { get; } 

    public abstract bool IsValid(); 

    public abstract List<string> GetValidationErrors(); 
} 

私が言ったように、私は列挙子でリストをナビゲートするので、これはすばらしく機能します。ナビゲーションロジックは抽象親クラスにあり、すべての子が行う必要があるのはSteps属性をオーバーライドすることです。 WizardSteps自体にはロジックが含まれているため、有効期限がわかっており、ユーザーは続行できます。 MVVMを使用しているので、次のボタンは、コマンドを使用してCanMoveToNextPage()およびMoveToNextPage()関数にバインドされています。

私の質問は:この場合、列挙モデルを悪用するのはいかがですか?より良い方法がありますか?私は実際に何らかの形でコントロールフローを定義する必要があります。そして、それは歩留まりリターン能力と実際によく似ていますので、フローロジックをステップアクセサーに戻して次のページを取得させることができます。

+1

このブログ記事をご覧くださいhttp://blogs.msdn.com/b/shawnhar/archive/2010/10/01/iterator-state-machines.aspx – asawyer

+1

'_currentStepEnumerator'とは何ですか?まあ、それは 'IEnumerator'だと思いますが、宣言方法を明確にしてください。それは静的なメンバーですか?最後に、あなたのコードには何も悪いことはありません。アプリケーションロジックを単純化すれば、私はあなたがそれを使うことができると確信しています。とにかく素敵な質問:) –

+0

完全なWizardBaseクラスで更新されました。あなたの有益なコメントありがとう! – millejos

答えて

1

何かが要件を満たし、維持しやすく、読みやすいとすれば、それはそれほど悪くないと思います。

あなたが推測したように、これはIEnumerableの古典的な使用ではありません。しかし、このようなわずかなオフビートの状況では、利回りのリターンはほとんどの場合(少なくとも私の経験では)使用されています。あなたが "< Back"のサポートを必要としない限り、私はそのままにしておきます。

代わりに、私は複数のアプローチを使用しましたが、本当に私の味には何もありません。ブランチングパスを持つウィザードは、いつも大変です。

ヘビーウェイトウィザードの場合、1つのオプションは状態マシンです。状態間をどのようにトラバースするか、どのトランジションが有効かを知る1つまたは2つのメソッドを記述します。各状態のUserControlを作成し、ListCollectionViewを介してTabControlに公開します。

私が一度使った素敵な軽い解決策は、ウィザード内のすべてのページをグリッド内で重ねて表示し、列挙によって表される状態にバインドすることで可視性を切り替えることです。 ValueConverterを使用すると、魔法の数を避けることさえできます。ページを切り替えるだけで、Statusプロパティを増減することができます。

関連する問題