Похоже, вам нужно знать несколько вещей в вашей системе.
- Для начала вам нужен список шагов и задач для каждого шага.
- Во-вторых, вам нужно знать состояние каждого задания каждого шага. Это может быть просто, например, битовый флаг, или перечисление с более сложными состояниями, такими как «Не начато», «Не завершено» и «Завершено». Каждая задача должна знать свой собственный статус и свои критерии для определения этого статуса. Как только все задачи в шаге выполнены, этот шаг автоматически считается завершенным. Вы можете связать URL-адрес страницы с каждым шагом и даже идентификатор элемента управления с каждой задачей.
- Наконец, вам нужен глобальный контекст, который знает о состоянии всех задач при загрузке страницы и сохраняет сложные правила, которые вы упомянули. Один из способов сделать это - определить список задач и / или шагов, которые должны быть выполнены и использованы для установки свойства CanBeVisible на основе этого List.All (x x => true); это можно сделать в базе данных, XML, в зависимости от того, что загружено в контекст с обновленной информацией о состоянии для каждой задачи. Затем любой элемент управления навигацией может подключиться к этому глобальному контексту и точно знать, какие параметры отображать.
Идея состоит в том, чтобы инкапсулировать отображение зависимостей видимости в состояние каждой задачи в центральном местоположении, и тогда любой другой элемент пользовательского интерфейса, такой как панель навигации, может использовать эту информацию для отображения только тех элементов управления, которые удовлетворяют критериям видимости для любого данный шаг и набор задач.
Ответ от CKirb250:
Извините, пришлось разместить мой комментарий здесь, потому что форматирование в поле для комментариев отстой.
Да, мне нужен список шагов. Куда мне их положить? Должны ли они быть простыми enum
? Должны ли они быть размещены в формате XML где-нибудь, чтобы я мог ссылаться на них со значением ключа, их именем, которое должно быть показано пользователю, и какая страница aspx соответствует этому шагу?
Отслеживается в базе данных. Чтобы узнать состояние каждого шага, запрашивается база данных.
Как можно определить сложные правила? Я представляю себе гигантское заявление о переключении, которое гласит if (currentStep == steps.Step1) { if (page.IsFilledOut) { enableSteps(1, 2, 3, 4, 5); } }
.
Вот способ настроить это в XML - каждый элемент должен быть определен как класс, и если он отслеживается в базе данных (как рекомендуется), то у вас есть несколько таблиц (как минимум, Step, Task, VisibilityDependency.) Вы можете генерировать XML из БД (или просто заполнять классы напрямую из БД). Я использовал 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>
Теперь я думаю, что ваш навигационный элемент управления будет опрашивать ваш глобальный контекст, написав некоторый код для этого, просто чтобы дать вам представление о том, как я это сделаю.
Вот некоторый код для представления этой схемы и метод для возврата всех задач, которые могут быть отображены на странице; нет операторов переключения!
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();
}
}
}
Дайте мне знать, достаточно ли этого, чтобы раскрутить ваши собственные идеи для чистого способа рефакторинга вашего кода. Я разработал и работал над многими системами стилей волшебников и из первых рук увидел то, что вы описываете; а именно, что это становится беспорядком очень быстро, если не спроектировано хорошо с самого начала. Удачи!