看起来您需要了解系统中的几件事。
- 首先,您需要每个步骤的步骤和任务列表。
- 其次,您需要了解每个步骤的每个任务的状态。这可能很简单,例如位标志,也可能是具有更复杂状态的枚举,例如未开始、未完成和已完成。每个任务都应该知道自己的状态以及确定该状态的标准。完成一个步骤中的所有任务后,该步骤将自动被视为完成。您可以选择将页面 URL 关联到每个步骤,甚至将控件 ID 关联到每个任务。
- 最后,您需要一个全局上下文,它知道页面加载时所有任务的状态,并保留您提到的复杂规则。这样做的一种方法是定义必须完成的任务和/或步骤列表,并用于基于此 List.All(xx=> true); 设置属性 CanBeVisible;这可以在数据库、XML 中完成,只要将映射加载到每个任务的更新状态信息的上下文中即可。然后,任何导航控件都可以进入这个全局上下文,并确切地知道哪些选项可以呈现可见。
这个想法是将可见性依赖关系与每个任务的状态的映射封装在一个中心位置,然后任何其他 UI 元素(例如导航面板)可以使用此信息仅呈现满足任何给定步骤的可见性标准的那些控件,并且一组任务。
来自 CKirb250 的回复:对不起,我不得不在这里发表评论,因为评论框中的格式很糟糕。
是的,我确实需要一个步骤列表。我应该把它们放在哪里?它们应该是简单的enum
吗?它们是否应该在某个地方以 XML 格式布局,以便我可以使用键值、应该向用户显示的名称以及与该步骤对应的 aspx 页面来引用它们?
这在数据库中被跟踪。为了知道每一步的状态,查询数据库。
如何定义复杂的规则?我在想象一个巨大的 switch 语句,上面写着if (currentStep == steps.Step1) { if (page.IsFilledOut) { enableSteps(1, 2, 3, 4, 5); } }
.
这是一种在 XML 中进行设置的方法 - 每个元素都应该定义为一个类,如果在数据库中进行跟踪(如推荐的那样),那么您有一些表(至少是 Step、Task、VisibilityDependency)。您可以生成来自 DB 的 XML(或直接从 DB 填充类)。我使用 XML 作为一个简单的例子来可视化我的想法:
<WizardSchema>
<Steps>
<Step>
<StepID>1</StepID>
<StepOrder>1</StepOrder>
<StepTitle>First Step</StepTitle>
<StepUrl>~/step1.aspx</StepUrl>
<Tasks>
<Task>
<TaskID>1</TaskID>
<TaskOrder>1</TaskOrder>
<TaskPrompt>Enter your first name:</TaskPrompt>
<TaskControlID>FirstNameTextBox</TaskControlID>
<VisibilityDependencyList></VisibilityDependencyList>
<IsCompleted>True</IsCompleted>
</Task>
<Task>
<TaskID>2</TaskID>
<TaskOrder>2</TaskOrder>
<TaskPrompt>Enter your last name:</TaskPrompt>
<TaskControlID>LastNameTextBox</TaskControlID>
<VisibilityDependencyList>
<VisibilityDependency StepID="1" TaskID="1" />
</VisibilityDependencyList>
<IsCompleted>False</IsCompleted>
</Task>
</Tasks>
</Step>
<Step>
<StepID>2</StepID>
<StepOrder>2</StepOrder>
<StepTitle>Second Step</StepTitle>
<StepUrl>~/step2.aspx</StepUrl>
<Tasks>
<Task>
<TaskID>3</TaskID>
<TaskOrder>1</TaskOrder>
<TaskPrompt>Enter your phone number type:</TaskPrompt>
<TaskControlID>PhoneNumberTypeDropDown</TaskControlID>
<VisibilityDependencyList>
<VisibilityDependency StepID="1" />
<!-- Not setting a TaskID attribute here means ALL tasks should be complete in Step 1 for this dependency to return true -->
</VisibilityDependencyList>
<IsCompleted>False</IsCompleted>
</Task>
<Task>
<TaskID>4</TaskID>
<TaskOrder>2</TaskOrder>
<TaskPrompt>Enter your phone number:</TaskPrompt>
<TaskControlID>PhoneNumberTextBox</TaskControlID>
<VisibilityDependencyList>
<VisibilityDependency StepID="1" />
<VisibilityDependency StepID="2" TaskID="1" />
</VisibilityDependencyList>
<IsCompleted>False</IsCompleted>
</Task>
</Tasks>
</Step>
</Steps>
</WizardSchema>
现在我在想的是您的导航控件将轮询您的全局上下文,现在为此编写一些代码只是为了让您了解我将如何做到这一点。
这里有一些代码来表示这个模式和一个返回所有可以在页面上显示的任务的方法;没有 switch 语句!
using System;
using System.Linq;
using System.Collections.Generic;
namespace Stackoverflow.Answers.WizardSchema
{
// Classes to represent your schema
public class VisibilityDependency
{
public int StepID { get; set; }
public int? TaskID { get; set; } // nullable to denote lack of presense
}
public class Task
{
public int TaskID { get; set; }
public int TaskOrder { get; set; }
public string TaskControlID { get; set; }
public bool IsComplete { get; set; }
public List<VisibilityDependency> VisibilityDependencyList { get; set; }
}
public class Step
{
// properties in XML
public int StepID { get; set; }
public string StepTitle { get; set; }
public List<Task> Tasks { get; set; }
}
// Class to act as a global context
public class WizardSchemaProvider
{
/// <summary>
/// Global variable to keep state of all steps (which contani all tasks)
/// </summary>
public List<Step> Steps { get; set; }
/// <summary>
/// Current step, determined by URL or some other means
/// </summary>
public Step CurrentStep { get { return null; /* add some logic to determine current step from URL */ } }
/// <summary>
/// Default Constructor; can get data here to populate Steps property
/// </summary>
public WizardSchemaProvider()
{
// Init; get your data from DB
}
/// <summary>
/// Utility method - returns all tasks that match visibility dependency for the current page;
/// Designed to be called from a navigation control;
/// </summary>
/// <param name="step"></param>
/// <returns></returns>
private IEnumerable<Task> GetAllTasksToDisplay(Step step)
{
// Let's break down the visibility dependency for each one by encapsulating into a delegate;
Func<VisibilityDependency, bool> isVisibilityDependencyMet = v =>
{
// Get the step in the visibility dependency
var stepToCheck = Steps.First(s => s.StepID == v.StepID);
if (null == v.TaskID)
{
// If the task is null, we want all tasks for the step to be completed
return stepToCheck
.Tasks // Look at the List<Task> for the step in question
.All(t => t.IsComplete); // make sure all steps are complete
// if the above is all true, then the current task being checked can be displayed
}
// If the task ID is not null, we only want the specific task (not the whole step)
return stepToCheck
.Tasks
.First(t => t.TaskID == v.TaskID) // get the task to check
.IsComplete;
};
// This Func just runs throgh the list of dependencies for each task to return whether they are met or not; all must be met
var tasksThatCanBeVisible = step
.Tasks
.Where(t => t.VisibilityDependencyList
.All(v => isVisibilityDependencyMet(v)
));
return tasksThatCanBeVisible;
}
public List<string> GetControlIDListForTasksToDisplay(Step step)
{
return this.GetAllTasksToDisplay(this.CurrentStep).Select(t => t.TaskControlID).ToList();
}
}
}
让我知道这是否足以激发您自己的想法,以一种干净的方式重构您的代码。我已经开发和工作过许多向导风格的系统,并且我亲眼目睹了您所描述的内容;也就是说,如果从一开始就没有很好地架构,它会很快变得一团糟。祝你好运!