【问题标题】:How do I implement wizard type page navigation in an ASP.NET MVC 2 application?如何在 ASP.NET MVC 2 应用程序中实现向导类型的页面导航?
【发布时间】:2010-07-02 15:12:00
【问题描述】:

我在 Visual Studio 2008 中使用 ASP.NET MVC 2 和 .Net 3.5。

好的,我所说的“向导式页面导航”指的是一个网站,您可以在其中列出给定流程或工作流程中的阶段。有某种视觉符号来指示您处于阶段的哪个部分。我已经通过以下方式实现了这部分(尽管它闻起来像黑客):

css 类当前表示活动页面。 css 类 notcurrent 表示非活动页面(即您不在的页面)

我在一个名为 NavigationTracker 的类中声明了以下方法。

public static String getCss(String val, String currView)
{
    String result = String.Empty;
    String trimmedViewName = currView.Substring(currView.LastIndexOf("/") + 1).Replace(".aspx", "");
    if (val.ToLower().Equals(trimmedViewName.ToLower()))
        result = "current";
    else
        result = "notcurrent";

    return result;
}

我在这样的控件中有我的阶段:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%@ Import Namespace="TheProject.Models" %>

<link href="../../Content/custom.css" rel="stylesheet" type="text/css" />
<% 
   String currentView = ((WebFormView)ViewContext.View).ViewPath;
%>
<table width="100%">
    <tr>       
        <td class="<%= NavigationTracker.getCss("LogIn",currentView)%>"  style="width:18%;">Log In</td>
        <td class="<%= NavigationTracker.getCss("YearSelect",currentView)%>"  style="width:18%;">Year Section</td>
        <td class="<%= NavigationTracker.getCss("GoalEntry",currentView)%>"  style="width:18%;">Goals</td>
        <td class="<%= NavigationTracker.getCss("AssessmentEntry",currentView)%>"  style="width:18%;">Assessment</td>
        <td class="<%= NavigationTracker.getCss("SummaryEntry",currentView)%>"  style="width:18%;">Summary</td>                
    </tr>
</table>

*******************这是我需要帮助的地方 ***********

为了补充这个过程,我想创建一个用户控件,它只有上一个和下一个按钮来管理这个过程。到目前为止,我遇到的一个问题是该控件不能放在母版页中,但必须在表单结束之前包含在每个视图中。我不介意那么多。单击“上一个”或“下一个”按钮将包含的表单提交给适当的操作;但是,我不确定如何执行以下操作:

1) 检测是否点击了上一个或下一个按钮 2) 分别在流程开始和结束时显示/隐藏上一个和下一个按钮的逻辑。

我注意到我的应用程序的另一个奇怪之处在于,在经历了几个页面之后,如果我单击后退按钮,我的模型中的一些值会填充到页面上,而其他值不会。例如,为文本区域输入的文本显示,但未选择为单选按钮选择的值,但在直接检查模型时,适当的对象确实具有要绑定到单选按钮的值。

我可能只需要将最后一部分放在一个新问题中。我的主要问题是导航控件。任何有关处理该逻辑和检测是否单击 Next 或 Previous 的指针或提示都会非常有帮助。

编辑 1

我想在显示“上一个”和“下一个”按钮的控件中放置一个隐藏字段。根据单击的按钮,我将使用 JavaScript 更新隐藏字段的值。现在的问题似乎是隐藏字段从未创建或与表单一起提交。我已经修改了控制器发布参数以接受附加字段,但它永远不会被提交,也不会在 FormCollection 中。

这是隐藏字段的代码。请注意,它是在育儿视图的表单内部调用的用户控件中生成的(希望这是有道理的)。

<% Html.Hidden("navDirection", navDirection); %>

想法?

编辑 2

我已经解决了所有这些问题。我最迟会在星期二详细发布代码。下面介绍的解决方案可能会被选为答案,因为这让我有了正确的思考过程。

简而言之,解决方案是使用一个类似于建议的导航类,并根据当前视图和所有视图的字符串列表确定下一页或上一页的逻辑。创建了一个局部视图/用户控件来显示上一个/下一个按钮。用户控件有 2 个隐藏字段:1) 一个具有当前视图的值 2) 一个指示导航方向的字段(上一个或下一个)。 JavaScript 用于根据单击的按钮更新隐藏的导航字段值。与当前视图相比,用户控件中的逻辑根据向导中的第一个和最后一个视图确定是否显示“上一个”或“下一个”按钮。

总而言之,我对结果很满意。当我回到这个问题时,我可能会发现一些代码异味问题,但是,就目前而言,它可以工作。

感谢大家的帮助!

编辑 3 - 解决方案

这是我为显示“下一个”和“上一个”导航按钮而构建的控件的代码:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%@ Import Namespace="Project.Models" %>
<link href="../../Content/custom.css" rel="stylesheet" type="text/css" />
<script type="text/javascript" >
    function setVal(val) {
        var nav = document.getElementById("NavigationDirection");
        nav.value = val;
    }
</script>

<% 
   String currentView = ((WebFormView)ViewContext.View).ViewPath;    
   String navDirection = "empty";
   currentView = NavigationTracker.getShortViewName(currentView);
%>

<input type="hidden" value="<%= currentView %>" name="CurrentView" id="CurrentView" />
<input type="hidden" value="<%= navDirection %>" name="NavigationDirection" id="NavigationDirection" />

<% if( currentView != NavigationTracker.FirstPage) {  %>
    <div style="float:left;">        
        <input type="submit" value="Previous" onclick="setVal('previous')" />      <!-- On click set navDirection = "previous" -->
    </div>
<% } %>

<% if (currentView != NavigationTracker.LastPage)
   {  %>
<div style="float:right;">
    <input type="submit" value="Next" onclick="setVal('next')"  />              <!-- On click set navDirection = "next" -->
</div>    
<% } %>

从那里,您在表单的结束标记之前呈现控件,就像这样:

<% Html.RenderPartial("BottomNavControl"); %>
    <% } %>

现在我不能真正发布所有 NavigationTracker 代码,但它的工作原理可以从所选答案和以下 sn-p 中推断出来,该 sn-p 根据当前视图和返回视图名称方向(上一个或下一个)。

public String NextView
{
    get
    {
        if (String.IsNullOrEmpty(this.NavigationDirection)) return string.Empty;
        int index = this.MyViews.IndexOf(this.CurrentView);
        String result = string.Empty;
        if (this.NavigationDirection.Equals("next") && (index + 1 < MyViews.Count ))
        {
            result = this.MyViews[index + 1];
        }
        else if (this.NavigationDirection.Equals("previous") && (index > 0))
        {
            result = this.MyViews[index - 1];
        }
        return result;
    }
}

现在,执行所有这些操作都会产生一些副作用,很容易被认为是代码异味。首先,我必须修改所有标记为 [HTTPPOST] 的控制器方法,以接受 NavigationTracker 对象作为参数。此对象包含辅助方法和 CurrentView 和 NavigationDirection 属性。完成此操作后,我可以在所有操作中以相同的方式获得下一个视图:

return RedirectToAction(nav.NextView);

nav 是 NavigationTracker 类型。

另一个注意事项是 NavigationTracker 的 FirstPage 和 LastPage 属性是静态的,因此我实际上在 global.asax.cs 文件中使用 NavigationTracker.FirstPage 进行路由。这意味着我可以转到我的 NavigationTracker 类并在一个地方更改整个应用程序的流程。

我很乐意对此解决方案提出任何意见或批评。我承认这可能不是一个很好的解决方案,但乍一看,我很满意。

希望对你有帮助。

【问题讨论】:

    标签: c# asp.net asp.net-mvc asp.net-mvc-2


    【解决方案1】:

    您应该实现强类型视图或控件。在这种类型中定义一个属性,指示您所在的步骤和其他逻辑。例如:

    public class WizardView
    {
         public List<string> Steps { get; set; }
         public int CurrentStepNumber {get;set;}
    
         public bool ShowNextButton
         {
             get
             {
                 return CurrentStepNumber < this.Steps.Count-1;
             }
         }  
    
         public bool ShowPreviousButton
         {
             get
             {
                 return CurrentStepNumber > 0;
             }
         }  
    }
    

    还有你的控制:

    <%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<WizardView>" %>
    
    
        <table width="100%">
           <tr>    
    
             <% 
                int index=0;
                foreach(string step in Model.Steps)
                {
                %>   
                    <td class='<%=Model.CurrentStep==index?"current":"notcurrent" %> style="width:18%;">
                           <%= step %>
                   </td>
               <%
                    index++;  
                 } %>
            </tr>
    </table>
    

    在你的控制器中

    public ActionResult MyAction(int step)
    {
        return View (new WizardControl {
           Steps = Myrepository.getSteps();
           CurrentStep = step
         });
    }
    

    }

    【讨论】:

    • 这是一个非常棒的解决方案。谢谢!现在我只需要弄清楚如何知道是点击了上一个按钮还是下一个按钮。
    • @baijajusav - 如果 Gregoire 的解决方案适合您,为什么不支持它呢? 答案似乎不再被接受和投票成为一种常态。当一个人确实找到一个问题但答案既不被接受也不被赞成时,这真的很困难
    • @Ahmad。这是一个很好的观点。我赞成他的解决方案,但没有接受它,因为我的问题的核心是检测是否单击了“上一个”或“下一个”按钮。按钮本身只是提交表单。我想我必须在控制器中添加逻辑,以根据单击的按钮来处理重定向操作。
    • 请注意,这个答案并不能 100% 解决我的问题,但是,正如我之前所说,它确实让我以不同的方式思考,因此我可以得出我所做的解决方案。
    【解决方案2】:

    这可能不是您想要的,但是如果您使用 jQuery 隐藏和显示 DIV 标签会怎样。然后用户将获得向导的体验,而所有内容都在一个视图中。那么这一切都可以用一个控制器很好地处理

    另一种选择可能是为每个向导步骤创建一个单独的视图“步骤”

    http://example.com/page/step/1

    然后您可以创建一个包含所有字段的类对象,并在导航向导时通过添加到会话对象来添加它

    (Custom.Class.Obj)Session["myWizard"]

    通过这种方式,您可以构建标准视图并从会话对象加载您拥有的信息。

    【讨论】:

      【解决方案3】:

      这是另一种方法 - 扩展 WizardView 方法。您所描述的是一个状态引擎 - 了解状态、转换以及与每个相关的行为和触发器的东西。

      如果您查看无状态 (http://code.google.com/p/stateless/) 之类的实现,您会发现您可以描述状态(向导视图方法中的步骤)以及与每个状态相关联的触发器。根据您的描述,您可以在状态引擎连接中捕获此信息 - 或者完全忽略它,因为所有转换都由引擎离散处理。

      更进一步,您的视图连接现在可以变得非常通用。视图不需要知道操作的状态完整性,只依赖于视图模型本身。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-12-12
        • 1970-01-01
        • 2018-10-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-17
        相关资源
        最近更新 更多