代码之家  ›  专栏  ›  技术社区  ›  jason

如何在ASP.NET MVC 2应用程序中实现向导类型的页面导航?

  •  2
  • jason  · 技术社区  · 16 年前

    我正在将ASP.NET MVC 2&NET 3.5与Visual Studio 2008一起使用。

    好的,我所指的“向导式页面导航”是一个站点,在该站点中,您有给定流程或工作流中的阶段列表。有一些视觉符号可以指示你所处阶段的哪一部分。我已经通过以下方式实现了这一部分(尽管它有点像黑客程序):

    css class current表示活动页。 css class 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)分别在流程开始和结束时显示/隐藏上一个和下一个按钮的逻辑。

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

    我可能只需要把最后一部分放到一个新问题中。我的主要问题是导航控制。处理该逻辑的任何指针或提示,以及检测是否单击了下一个或上一个,都将是最有用的。

    编辑1

    我曾想过在控件中放置一个隐藏字段,显示上一个和下一个按钮。根据单击的按钮,我将使用javascript更新隐藏字段值。现在的问题似乎是,隐藏字段从未与表单一起创建或提交。我已经修改了controller post参数以接受附加字段,但它从未被提交,也不在formcollection中。

    这是隐藏字段的代码。请注意,它是在一个用户控件中生成的,该控件在父级视图的窗体内部调用(希望这是有意义的)。

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

    思想?

    编辑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"); %>
        <% } %>
    

    现在我不能真正地发布所有的导航跟踪器代码,但是它的工作原理可以从选择的答案和下面的代码片段推断出来,这些代码片段根据当前视图和方向(上一个或下一个)返回视图的名称。

    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类,在一个地方为整个应用程序更改流。

    我希望对这个解决方案有任何评论或批评。我承认这可能不是一个很好的解决方案,但乍一看,我很满意。

    希望它有帮助。

    3 回复  |  直到 14 年前
        1
  •  3
  •   Gregoire    16 年前

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

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

    }

        2
  •  1
  •   Chase Florell    16 年前

    这可能不是您正在寻找的,但如果您使用jquery隐藏和显示DIV标记,会怎么样呢?当所有内容都在一个视图中时,用户将获得向导的体验。这样就可以用一个控制器很好地处理所有的问题。

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

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

    然后,您可以创建一个包含所有字段的类对象,并在导航向导时将其添加到会话对象中。

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

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

        3
  •  0
  •   S. Hebert    16 年前

    这是另一种方法-扩展wizardview方法。您所描述的是一个状态引擎——它能够感知状态、转换以及与之相关联的行为和触发器。

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

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

    推荐文章