向导导航与IEnumerable / yield返回

本文关键字:yield 返回 IEnumerable 导航 向导 | 更新日期: 2023-09-27 18:11:24

我是c#新手,刚刚发现如何使用yield return来创建自定义IEnumerable枚举。我试图使用MVVM来创建一个向导,但我很难弄清楚如何控制从一个页面到下一个页面的流程。在某些情况下,我可能希望某个步骤出现,在其他情况下,它不适用。

无论如何,我的问题是,我使用IEnumerable返回每个后续页面,这工作真的很棒,但我知道我可能在做一些不当/意想不到的语言。子类只需要重写抽象的Steps 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;
      ...
    }
  }
}

父类包含使用Steps属性枚举器的导航逻辑:

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 ();
}

正如我说过的,这非常有效,因为我用Enumerator来导航列表。导航逻辑位于抽象父类中,子类所要做的就是重写Steps属性。WizardSteps本身包含逻辑,以便它们知道何时有效,用户可以继续。我使用MVVM,所以下一个按钮通过命令绑定到CanMoveToNextPage()和MoveToNextPage()函数。

我想我的问题是:在这种情况下滥用枚举模型有多错误?有没有更好的办法?我确实需要以某种方式定义控制流,它与yield返回能力非常适合,以便我可以将流逻辑返回到Steps访问器以获得下一页

向导导航与IEnumerable / yield返回

我认为只要满足要求,易于维护,易于阅读,那么它就不会那么糟糕。

显然,正如你所猜到的,这不是IEnumerable的经典用法。但是,收益率回报几乎总是(至少在我的经验中)用于这种稍微不正常的情况。只要你不需要"<如果没有"支持",我会让解决方案保持原样。>

至于其他选择,我已经使用了多种方法,但没有一种真正符合我的口味。带有分支路径的向导总是有点混乱。

对于重量级向导,一个选项是状态机。编写一两个知道如何在状态之间遍历以及哪些转换是有效的方法。为每个状态建立一个UserControl,并通过ListCollectionView将它们暴露给TabControl。

我曾经使用过的一个轻量级解决方案是,将向导中的所有页面堆叠在网格中,并通过绑定到由枚举表示的状态来切换它们的可见性。使用ValueConverter,您甚至可以避免魔术数字。然后在页面之间切换就变成了增加或减少Status属性的问题。