【问题标题】:Wizard navigation with IEnumerable / yield return使用 IEnumerable / yield return 进行向导导航
【发布时间】:2011-12-09 11:28:38
【问题描述】:

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

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

我想我的问题是:在这种情况下滥用枚举模型有多大的错误?有更好的方法吗?我真的需要以某种方式定义控制流,它与产量返回能力非常吻合,这样我就可以让流逻辑返回到 Steps 访问器以获取下一页。

【问题讨论】:

  • 什么是_currentStepEnumerator?好吧,我想这是一个IEnumerator,但你能澄清一下它是如何声明的吗?它是静态成员吗​​?最后,我认为您的代码没有任何问题:如果它简化了您的应用程序逻辑,我很确定您可以使用它。无论如何都是好问题:)
  • 更新了完整的 WizardBase 类。感谢您的帮助!

标签: c# .net ienumerable


【解决方案1】:

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

显然,您已经猜到这不是 IEnumerable 的经典用法。但是,收益回报几乎总是用于(至少根据我的经验)用于像这样稍微不合时宜的情况。只要您不需要“

至于替代方案,我使用了多种方法,但没有一种方法真正符合我的口味。带有分支路径的向导总是有点凌乱。

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

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

【讨论】:

    猜你喜欢
    • 2011-01-04
    • 2010-11-07
    • 1970-01-01
    • 1970-01-01
    • 2012-01-03
    • 2011-04-20
    • 1970-01-01
    • 2016-01-28
    • 1970-01-01
    相关资源
    最近更新 更多