随笔-19  评论-482  文章-1  trackbacks-13

WebForms中,大家应该都体会过SiteMapPath给开发带来的便利,而今格式各样的导航栏、导航菜单已经成了网站不可缺少的一部分,接下去大家会看到一个在MVC下使用的,并且符合MVC设计规范的导航栏“插件”,以在MVC中取代之前SiteMapPath的应用。

首先我们还是明确一下这个插件的意义和需要完成的基本功能:

问:既然有SiteMapPath,为什么还要重复开发一个同样功能的导航栏?

答:没错,SiteMapPath服务器控件在MVC(以下无特别说明都专指ASP.NET MVC)中仍然可以很好地“显示”,但是显然无法很好满足C-V结构的分离,SiteMapPath控件依赖于aspx页面,而在MVC中,早在aspx页面执行之前,几乎所有数据都应该在Controller处理完成,“打包”给ViewData这就要求这个控件能够同时在ControllerView中被很好地控制,并且View主要只起到显示的作用。还有一点就是SiteMapPath默认的sitemap格式已经无法满足MVChttp请求的规则,使得无法很好地进行控制。

问:新开发的导航栏有哪些功能?

答:

1、完全兼容原有WebForms项目下的Web.sitemap文件格式,即当网站中同时存在MVCWebForms项目时, 可以共享sitemap文件。但须按照MVC的执行方式对原有文件稍加补充。

2、自动从Web.sitemap获得当前页面(Controller,Action)对应的网站地图位置,自动生成导航条, 使用时不需要编写任何代码。

3、根据MVCController-Action规则自动创建对应链接,也可自由设置,包括只显示文字,不使用连接等。

4、可以完全或部分手动设置、增减节点。

5、可以限制节点显示层数。

以上的大部分功能都是在SiteMapPath可实现的,但是我们已经不再需要PostBack功能的设置。

遵照这些前提,我给大家展示一下我的实现方法:

一、建立全局共享的Model层的NavigationInfo,包含在Models/ExtentionEntity.cs

public class NavigationInfo

        
{

            
public string Title setget; }

            
public string ActionName setget; }

            
public string ControllerName setget; }

            
public object Values setget; }

            
public void SetNavInfo(string title, string actionName, string controllerName, object values)

            
{

                Title 
= title;

                ActionName 
= actionName;

                ControllerName 
= controllerName;

                Values 
= values;

            }


            
public NavigationInfo()

            
{ }

            
public NavigationInfo(string title, string actionName, string controllerName, object values)

            
{

                
this.SetNavInfo(title, actionName, controllerName, values);

            }


            
public NavigationInfo(string title, string actionName, string controllerName)

            
{

                
this.SetNavInfo(title, actionName, controllerName, null);

            }


            
public NavigationInfo(string title, string actionName, object values)

            
{

                
this.SetNavInfo(title, actionName, null, values);

            }


            
public NavigationInfo(string title)

            
{

                
this.SetNavInfo(title, nullnullnull);

            }


        }


   
    其中,TitleActionNameControllerNameValues分别对应View页面ActionLink需要的链接文字、actioncontrollervalues。里面也提供了对NavigationInfo4种重写的方法,以便和ActionLink的参数重写尽量配套,在适应使用习惯的同时也提供了更大的灵活性。

二、创建BaseViewData,所有继承了这个类的ViewData都将获得NavigationInfo属性。Models/BaseViewData.cs

   public class BaseViewData

    
{

        
public string Title getset; }//这个Title现在这儿埋个伏笔,我会在下文中说明,和导航拦没有太直接关系

        
public List<ExtentionEntity.NavigationInfo> NavInfo getset; }

    }

    三、创建Views/Shared/Navigation.ascx,供页面调用。

注:这里我使用了用户控件的形式是因为考虑到开发时实际调用导航栏的页面不会太多(一般都只在母板页),如果你觉引用的比较多,这样不太方便,也可以加到HtmlHelper中,其中要执行的代码是一样的:

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="Navigation.ascx.cs"

    Inherits
="MVCTools.Views.Shared.Navigation" 
%>

<div id="Navigation" class="Navigation">

    
<% 

        
if (ViewData.ContainsDataItem("navInfo")) {

            System.Collections.Generic.List
<MVCTools.Models.ExtentionEntity.NavigationInfo> navInfo = new System.Collections.Generic.List<MVCTools.Models.ExtentionEntity.NavigationInfo>();

            
if (ViewData["navInfo"== null || ((System.Collections.Generic.List<MVCTools.Models.ExtentionEntity.NavigationInfo>)ViewData["navInfo"]).Count == 0)

            {

                navInfo 
= MVCTools.Common.Navigation.GetAutoNavigationInfo();

            }

            
else

            {

                navInfo 
= (System.Collections.Generic.List<MVCTools.Models.ExtentionEntity.NavigationInfo>)ViewData["navInfo"];

            }

%>您的位置:<%

             
int i = 0;

             foreach (MVCTools.Models.ExtentionEntity.NavigationInfo nav in navInfo)

             {

                 
if (++> 1)

                 {
%>&nbsp;&nbsp;><%--//TODO:间隔标记可以扩展为用户自定义--%>&nbsp;&nbsp;<%}%>

<% if (nav.Values != null)

   {

        
if (!string.IsNullOrEmpty(nav.ActionName))//if (nav.Values.ToAttributeList().Contains("controller"))

           {
%><%= Html.ActionLink(nav.Title,nav.ActionName, nav.Values)%><%}

           
else

           {
%><%= nav.Title%><%}

   }

   
else if (!string.IsNullOrEmpty(nav.ActionName))

       {
%><%= Html.ActionLink(nav.Title, nav.ActionName, nav.ControllerName)%><%}

       
else

       { 
%><%= nav.Title%><%}

         } 
%>

<% } %>

</div>

在aspx中,我们只需要这样引用就行了:

<!-- 导航条 -->

<%= Html.RenderUserControl("/Views/Shared/Navigation.ascx")%>

    四、准备工作基本做好了,下面来看一下在Controller中如何对导航栏灵活控制。

在此之前,我需要在Common中建了一个专门负责处理NavigationInfo的类Common/Navigation.cs

Code