必備的東西:
Windows XP/Vista/7/2003/2008
Visual Studio 2005 or 2008 (download the correct version of Home Site project above)
.NET Framework 2.0 and ASP.NET AJAX 1.0
今天,很多瀏覽器提供了使用tab的能力去瀏覽更多的網頁和網站。當然這是一個非常有用的功能來替代同時打開幾個瀏覽器,但如果提供在一個頁面中瀏覽多個網頁也非常的不錯。
例如,如果你的主頁是由很多不同的有用的Web工具或者站點組成,一個tab頁面將可能非常的有用。使用框架集,IFRAME等等,都是宿主外部內容的典型方式。這些方法允許你在單個頁面上宿主多個網頁。 但是使他們能正確的布局卻非常不容易。 更不用說去處理頁面和IFRAME的scrollbars等問題。
這篇文章中,嘗試去宿主外部數據,提供了一個基本的解決方案,利用ASP.NET,AJAX和javascript 去處理一些遇到的麻煩。
計劃
主旨是提供一個簡單的宿主外部數據的方案,這個解決方案有下面這些簡單的需求。
1、提供一個tab界面,方便瀏覽。
2、提供一個配置方法來添加tab
3、使每個tab頁都能宿主配置的頁面
基本的技術需要是:
1、僅當tab被選中的時候,加載外部的數據內容
2、保證縱向的scrollbars的設置成顯示,而且僅當需要處理的數據溢出的時候,才顯示scrollbars 。
3、保證該解決方案是能跨瀏覽器工作
解決方案的名字和主頁面的名字都是 Home Site
分析
對于這個解決方案,我決定使用JQuery UI Tabs 來實現表格式的導航功能。我以前也使用過商業的開源的tab控件。但是JQuery UI Tabs 是輕量級的,實現非常地簡單,而且是免費的。
除了JQuery 和tab控件以及.net提供的功能,不需要其它的控件。 VS2005 將足以結合整個項目的開發環境,選擇C#作為開發語言。
我將使用一個IFRAME的宿主網站內容,由于跨站點(又名跨域)的安全限制,使用JQuery UI Tabs去宿主外部網頁將無法直接工作。
設計
這里有一個為客戶提供視覺上的最低限度的需求: 
該解決方案,將需要三種不同的功能模塊:
1、配置模塊
2、使用JQuery UI Tabs 插件的tab界面
3、使用IFRAME元素宿主網頁內容辦法。
配置模塊:
一個需求的功能是是使tab可配置。 我選擇最低限度,將tab的配置信息放到一個xml文件之中。雖然我可以更進一步的深入,使tab能的動態增加和刪除,我決定在本文的第二篇中提供此功能。
XML文件的格式如下:
代碼


namespace HomeSite
{
/// <summary>
/// Tab configuration static handling class
/// </summary>
public static class TabConfiguration
{
/// <summary>
/// This class returns a collection of TabDefinition classes created from
/// parsing the tab definitions defined in the TabConfig.xml file.
/// </summary>
/// <param name"page">The Page reference
/// calling this class</param>
/// <returns>ArrayList of TabDefinition classes</returns>
public static ArrayList LoadConfiguration(Page page)
{
// Local container for tab definitions
ArrayList tabList = new ArrayList();
try
{
// Read the contents of the TabConfig.xml file
StreamReader reader = new StreamReader(new FileStream(
page.MapPath("./TabConfig.xml"),
FileMode.Open, FileAccess.Read));
string xmlContent = reader.ReadToEnd();
reader.Close();
reader.Dispose();
// Create an XML document and load the tab configuration file contents
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(xmlContent);
// Iterate through each tab definition, create a TabDefinition class,
// and add the TabDefinition to the local ArrayList container
foreach (XmlNode node in xmlDoc.SelectNodes("/configuration/tab"))
{
TabDefinition tab = new TabDefinition();
tab.ID = node.Attributes["id"].Value;
tab.DisplayName = node.Attributes["displayName"].Value;
tab.Path = node.Attributes["path"].Value;
tabList.Add(tab);
}
}
catch
{
// Do nothing
}
// Return the tab definition
return tabList;
}
}
/// <summary>
/// This class serves as the container for a tab definition
/// </summary>
public class TabDefinition
{
/// <summary>
/// Member variable for the Unique ID for the tab
/// </summary>
private string _id;
/// <summary>
/// Member variable for the displayed name of the tab
/// </summary>
private string _displayName;
/// <summary>
/// Member variable for the web page URL to host in the tab (IFRAME)
/// </summary>
private string _path;
/// <summary>
/// Property for the Unique ID for the tab
/// </summary>
public string ID
{
get { return _id; }
set { _id = value; }
}
/// <summary>
/// Property for the displayed name of the tab
/// </summary>
public string DisplayName
{
get { return _displayName; }
set { _displayName = value; }
}
/// <summary>
/// Property for the web page URL to host in the tab (IFRAME)
/// </summary>
public string Path
{
get { return _path; }
set { _path = value; }
}
}
}
現在,讓我們看看Home Site 的主頁的標記
代碼
// JQuery scripting
$(document).ready(function()
{
var browser = navigator.appName;
var heightAdjust = 23;
var widthAdjust = 7;
// Make height and width offset adjusts for non-IE browsers
if (browser != "Microsoft Internet Explorer")
{
heightAdjust = 18;
widthAdjust = 9;
}
// Show the panelList UL element so we can setup the tabs
// Please note this approach eliminates Flash of Unstyled Content (FOUC)
$('#panelList').show();
// Setup the jQuery UI tabs
$('#tabPage').tabs({
cache: true, // This ensures selecting a tab does not refresh the page
load: function(event, ui)
{
// Keep links, form submissions, etc. contained within the tab
$(ui.panel).hijack();
// Adjust the IFRAME size correctly in the browser window
$('.contentsIframe').width((ViewPortWidth() - widthAdjust));
$('.contentsIframe').height((ViewPortHeight() -
$('.menuRow').height() - $('.tabs').height() - heightAdjust));
}
});
// Toggle arrow button image and hide/show menu area
$('#collapseArrow').click(function()
{
if ($(this).hasClass('ui-icon-circle-triangle-s'))
{
$(this).removeClass('ui-icon-circle-triangle-s');
$(this).addClass('ui-icon-circle-triangle-n');
$('#menuDiv').show();
}
else
{
$(this).removeClass('ui-icon-circle-triangle-n');
$(this).addClass('ui-icon-circle-triangle-s');
$('#menuDiv').hide();
}
// Adjust the IFRAME size correctly in the browser window
$('.contentsIframe').width((ViewPortWidth() - widthAdjust));
$('.contentsIframe').height((ViewPortHeight() -
$('.menuRow').height() - $('.tabs').height() - heightAdjust));
});
// Adjust tab header width and visible iframe window
// height and width after the window is resized
$(window).resize(function(){
$('.contentsIframe').width((ViewPortWidth() - widthAdjust));
$('.contentsIframe').height((ViewPortHeight() -
$('.menuRow').height() - $('.tabs').height() - heightAdjust));
$('.ui-widget-header').width(ViewPortWidth() - widthAdjust);
});
// Adjust tab header height and width according to the IE client viewing area
$('.ui-widget-header').width(ViewPortWidth() - widthAdjust);
// Adjust the IFRAME height correctly in the browser window
$('.contentsIframe').height((ViewPortHeight() -
$('.menuRow').height() - $('.tabs').height() - heightAdjust));
});
// Returns width of viewable area in the browser
function ViewPortWidth()
{
var width = 0;
if ((document.documentElement) &&
(document.documentElement.clientWidth))
{
width = document.documentElement.clientWidth;
}
else if ((document.body) && (document.body.clientWidth))
{
width = document.body.clientWidth;
}
else if (window.innerWidth)
{
width = window.innerWidth;
}
return width;
}
// Returns height of viewable area in the browser
function ViewPortHeight()
{
var height = 0;
if (window.innerHeight)
{
height = window.innerHeight;
}
else if ((document.documentElement) &&
(document.documentElement.clientHeight))
{
height = document.documentElement.clientHeight;
}
return height;
}
</script>
</head>
<body class="mainBody" style="margin:0">
<form id="form1" runat="server">
<asp:ScriptManager id="ScriptManager1" runat="server" />
<div>
<table id="mainTable" cellpadding="0" cellspacing="0">
<tr class="menuRow">
<td align="left" valign="top">
<span id="collapseArrow"
title="Show/Hide Header"
class="menuSpan ui-icon ui-icon-circle-triangle-n"></span>
<div id="menuDiv"
class="menuDiv">This is the header area.
<br /><i>Please customize this area as you set
fit; i.e. add a logo, menu options, links,
etc.</i><br /><br /></div>
</td>
</tr>
<tr>
<td class="tabPageCell" colspan="2"
valign="top" align="left">
<div id="tabPage" class="contents">
<ul id="panelList"
class="tabs" runat="server" />
</div>
</td>
</tr>
</table>
</div>
</form>
</body>
</html>
Home Site 頁面的隱藏代碼如下:
代碼
namespace HomeSite
{
/// <summary>
/// Home Site (default) web page code behind class
/// </summary>
public partial class _Default : System.Web.UI.Page
{
/// <summary>
/// On page load we need to create the tab
/// list items for tab interface construction
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
AddTabsToForm();
}
/// <summary>
/// This method calls to our business logic
/// class to read the tab configuration file,
/// which will return an ArrayList of TabDefinition
/// classes. This method iterates
/// through the ArrayList building HTML controls to add to the tab panel.
/// </summary>
protected void AddTabsToForm()
{
foreach (TabDefinition tab in TabConfiguration.LoadConfiguration(this.Page))
{
HtmlGenericControl tabListItem = new HtmlGenericControl();
tabListItem.TagName = "li";
tabListItem.InnerHtml = "<a title=/"" +
tab.DisplayName + "/" href=/"ContentLoader.aspx?ID=" +
tab.ID + "&Path=" + tab.Path +
"/">" + tab.DisplayName + "</a>";
panelList.Controls.Add(tabListItem);
}
}
}
}
遇到的問題
我遇到的主要的問題是跨瀏覽器的情況下自動適應IFRAME的大小,該方案在IE 8,Firefox v3.5.6,和谷歌v3.0.195.38瀏覽器進行測試。
我必須進行瀏覽器檢測,根據IFRAME在三個瀏覽器測試的尺寸,調整相應的寬度和高度。當瀏覽器改變大小的時候,Chrome 和FireFox看起來IFRAME有個固定的高度。 但是, IE8看來來會丟失在IFRAME和瀏覽器頂部之間的padding。調整寬度和高度特別是IE似乎應該盡可能的減少IFRAME到IE瀏覽器窗口的底部“蜷縮”影響。
限制
1、以下JavaScript將使你加載的網頁跳出IFRAME,我不知道任何解決辦法此(如果存在)。Code Project 網站上目前擁有類似下面的代碼,這樣配置選項指向http://www.codeproject.com/非常的容易,這里重現描述的動作。
<script type="text/javascript" language="javascript">
if (top!=self) top.location.href = location.href;
</script>
2、在瀏覽器中,Web網頁被迫改變頁面本身的大小,有可能跳出IFRAME窗體,從而取代了前父窗口。
3、我沒有使用Safari,Opera,早期版本的IE瀏覽器,或任何其他瀏覽器的早期版本測試的此解決方案,所以要在Home Site中適當地調整heightAdjust和widthAdjust變量,適應沒有測試的IE瀏覽器或低于IE8瀏覽器的版本。
總結和興趣點
雖然這種解決方案不是很復雜,通過一個標簽界面宿主外部網站內容。這是我所見過的許多網絡論壇和博客要求的功能。請注意:您也可以配置標簽可顯示自己相關的域名或網站(在同一臺服務器)。
新聞熱點
疑難解答