2009年11月26日 星期四

香港旅遊新景點 馬灣挪亞方舟公園

    香港馬灣挪亞方舟公園(挪亞公園網站)是一個新完工的公園,2009年8月27日這天剛好陽光普照,微風中依稀聞到海風些許鹹味,帶著老婆女兒,踏上這個計畫已久的挪亞方舟公園之行。馬灣地理位置在香港島西北方青馬大橋附近,在大嶼山和青衣之間。

    怎麼去呢?因為時間上還算悠閒,故就晃啊晃地到紅勘站,利用新蓋好的紅勘站到南昌站這段地鐵,直接搭西鐵線到「荃灣西鐵站」不用中途換地鐵線。最後到「荃灣西鐵站」後,出地鐵站後右轉,馬上可以看到珀麗灣的渡輪。渡輪費用約為8元港幣左右(小孩4元),但要先看好渡輪來回時間,免得面對茫茫大海枯等渡輪。可以参考港鐵網站


這就是載我們去的渡輪。當然,香港渡輪冷氣都很強,帶件薄外套較好。


快到珀麗灣渡船頭的時候會穿越過青馬大橋下,當下發現橋底面還蠻乾淨的。


    
一出美輪美奐的渡輪站(那個淡水渡輪站可以参考一下),可以看到一個長條狀的公園,當然,旁邊有很多餐廳與店家,賣一般港式飲食,遊客可在這裡補充糧草,不算太貴,但是我們並沒有在這裡吃東西。走著走著,乖乖…..這在馬灣邊的一大群公寓可是高級私人住宅區啊,什麼進出車輛管制、人車分道、高級房車內載著優雅女仕….你想的到台灣豪宅各種條件是這裡都不缺,不過人家還多了一個面向海灣的美景 + 24小時俯視青馬大橋雄姿,這個咱們信義之星和帝寶可是沒有歐。


您看,人家社區前面連花花草草都是很有型,不隨便的。


    
快到的時候,馬路右側雖然是橋下,但也有布置一些白色海鷗(或鴿子)造型的裝置藝術,整體感覺好像走進了奧運運動會場,各種旗幟飄揚著。


    
離入口處不遠的公園門牌造型和大自然公園內的一個大鍋牛造型(我小孩說這是斑馬貝殼),聽說這個公園也是挪亞方舟公園的一部分,要憑門票才能進入,但是要逛自然公園,嗯….台北陽明山好像更寬廣吧,除了火山、溫泉還有鳳梨香菇雞湯可喝。



終於走到挪亞方舟公園門口了,自渡輪站到這裡,慢慢走大概要走個快半小時。


    
買門票吧!原來這裡也是自助餐式的售票方式,不同公園組合有不同的票價,不過第一種「彩虹套票」應該沒有人會去買吧,來挪亞方舟公園不到方舟公園,那不是白來了嗎,而且有方舟公園的第二三種票價又是那麼接近,我想或許是這裡這種公園太少競爭不激烈,台灣應該不敢有業者會推出這樣的售票方式吧!!


    
一進入口,就可以看到很大方舟船頭,方舟船體應該是木紋很細緻的「水泥」所建造。但是沒有買挪亞或方舟套票是看不到方舟上的動物也模不到方舟船體。切記啊,所有公園入口都有員工把守著,無法混進去的。


  
進去方舟下的入口後,只能依據導覽路線,直接從船體中間進入到「方舟多媒體博物館」,就是介紹舊約聖經上挪亞方舟的由來,還有現代人類發現到類似方舟的遺跡與探險過程介紹,特別介紹香港探險隊的發現過程,有各種圖片,景物與動畫影片,也有發現到的方舟遺跡。比較突兀的,裡面有一個小動物園,有各種小型動物,可能是要模擬方舟內容納世界上各種動物的狀況吧,可是感覺不像聖經上所描述的,倒很像「可愛動物園區」。另外還有一個電影放映室,播放真人與動畫結合的3D挪亞方舟故事電影,因為上次在香港迪士尼聽到講廣東話的米老鼠我被驚嚇到,所以這裡聽到3D電影中東方臉孔且講廣東話的挪亞,我就比較不那麼被震撼住了。因為方舟內所有行程與展覽完全不能拍照,所以就不放上來了。

    
這裡是方舟地下室的紀念品門市叫做「大地寶藏」,還算蠻舒適的,香港賣東西賺錢的地方都弄得很好,但免費的地方就比較不怎麼樣了


    
走出來後,可以看到珀麗灣那個私人住宅與馬灣東灣泳灘。那個沙灘是免費的,可以走進去戲水與玩沙沙。不過因為往來的大輪船太多,個人感覺沙子與海水有很重的柴油味。


這是方舟公園的入口。


好多動物啊!開始有到了動物蠟像館的感覺。





這裡是方舟船頭,左邊可看到珀麗灣渡船頭。


這是方舟的側面全景


這是方舟模擬大雨過後動物跑出來情景。





    
肚子餓了,原本以為這麼新的公園,賣的餐點應該是不便宜,害我只敢抱著女兒,在大太陽下父女倆躲到旁邊的便利商店買飲料果腹,還是我老婆比較有魄力,管它店裡賣甚麼,直接下達命令,走進去看看再說,免得小孩中暑。原來這個餐廳叫做繽紛廊,位置不明顯,但是View卻不錯,食物也不貴,至於好不好吃,嗯~普普通通啦。適合不想花太多錢在這裡吃的家庭來坐坐。


下面是我們點的義大利肉醬麵+飯+絲襪奶茶,這樣要76元港幣。


    
公園內標示都還算清楚,不過除了方舟公園,其他的地方可能只是一個小廣場、幾樣越野運動器材或是網球場之類的,不要抱太大期望。有些地方看來還在興建中,或許未來會有其他不錯的區域可玩。


跨越奇園,我只看到一座兩側有大護網的獨木橋讓「小朋友」可以走過。


    
挪亞運動場,透過木柵欄,可看就是整個運動場,還有看台,平常好像也預約才能進入,但運動器材自備。



    
最後還有一個挪亞廣場,感覺好像是一個小停車場,然後旁邊有一個賣熱狗可樂的店家所組成,更扯的是,廣場上是出租腳踏車的營業據點。


    
離開整個公園後經過東灣泳攤,也順便走下去看看,各項沐浴設施,安全設施還蠻完備的。



還會貼心的跟遊客說明目前這裡的天氣與水文狀況,這值得學習。


走著走著,回到了很典雅美觀渡船頭,跟整個公園結合在一起。


不要走錯入口,這裡是回荃灣的候船室,另外一邊是回中環的候船室。


    
希望方舟內部能在佈置得逼近書中所描述的船內有各種木頭柵欄,裡面有各種動物,然後分好幾層,哪怕是動物蠟像也好,然後可以上到甲板上,體會一下挪亞面對世界一片汪洋大海滄桑無望的感覺。不過方舟甲板上居然是一家飯店,得另外花錢錢才能帶小孩上去。也只能說這裡是香港,有一艘類似海港城型態的挪亞方舟也是不奇怪的。

2009年11月22日 星期日

ObjectDataSource配合Gridview來達成手動分頁完整範例

    網站的程式,常常需要將資料自資料庫中抓取出來後,顯示在user端並且提供分頁的功能,讓user可以方便的到想要看到資料的那一頁。但是若是以SqlDataSource的方式,抓取資料後,拋給前端顯示的aspx程式,通常只能依據使用者提供的條件查詢到資料後,全部拋給aspx程式,然後根據Gridview的[AllowPaging]設定,提供分頁的功能,但是假設查到1000筆資料,若是一頁顯示10筆資料,使用SqkDataSource這種方式,若沒有特別處理自資料庫抓出來的筆數,仍然會將這1000筆資料,一次拋給apsx程式,然後再顯示一頁10筆的方式給user瀏覽。一次抓出大量且使用者暫時不會看到的資料,對於網站伺服器而言是很大的負荷,瀏覽人數少的時候,可能這種負荷看不出來,但是當瀏覽人數越來越多時,網站存取資料的回應速度就會明顯的下降。

    當然要處理這種問題,或許加上Cache是一種稍微提升回應速度的方式,但是我們這邊說明以ObjectDataSource的方式是配合SQL Server 2005資料庫ROW_NUMBER()的指令,並且將存取資料庫模型化成一個獨立的元件,每次只從資料庫中抓取使用者一次要看到的筆數拋給前端的Gridview。以上一段所描述的例子而言,使用者查到1000筆,若此時每個分頁顯示10筆資料,我們每次會只自資料庫中抓取出10筆資料,回應給user端顯示的aspx程式,大大的減少了資料庫與網路的負荷,並配合使用Stored Procedure以提升整體網站存取速度。

步驟一:

    首先我們先建立一個網站專案與一個類別庫專案,名稱分別為Northwind與NorthwindSQL,在專案Northwind中需將專案NorthwindSQL加入参考;然後建立css目錄以存放style.css檔案;建立ShowCustomer.aspx,純粹負責顯示介面資料給user瀏覽;並且在NorthwindSQL專案下建立GetCustomer.cs檔案,用來處理與存取資料的邏輯之用。在.NET 2008的開發環境界面看來如下:



步驟二:

    接下來我們開啟ShowCustomer.aspx,分別加上
1.兩個TextBox,分別讓使用者可輸入每頁顯示筆數(txtPagesize)與欲查詢的國別(txtCountry),並加上RangeValidator以規範每頁顯示筆數在1到100筆間。
2.一個確認查詢的按鈕(btn_query)。
3.一個顯示資料的Gridview控制項,並設定可准許分頁(AllowPaging="True")與自動產生資料欄位(AutoGenerateColumns="true"),然後加上CSS檔案的描述。
4.一個ObjectDataSource控制項,控制項設定稍後步驟五會說明。
上述步驟完成後,網頁設計如下所述。



步驟三:

    接下來我們打開SQL Server 2005,利用Northwind資料庫現成資料來作範例展示;我們新增2個Stored Procedure名為sp_customerssp_customers;2

sp_customers:只抓取使用者每頁顯示的資料。
sp_customers;2:計算並Output出每次查詢的總筆數,此為Gridview分頁時所需要。

我們傳入4個參數給這兩個Stored Procedure:

startRowInde:每頁開始筆數的index。
maximumRows:每頁最後一列筆的筆數。
Country:使用者查詢的國家別條件字串。
total_count每次查詢的總筆數。

這兩段Stored Procedure全部內容如下:


--只抓取使用者每頁顯示的資料

CREATE PROCEDURE [dbo].[sp_customers]
@startRowIndex  int,
@maximumRows  int,
@Country  nvarchar(15)=''
AS
BEGIN
if len(@Country)=0
    begin
      SELECT * FROM
      (SELECT ROW_NUMBER() OVER(ORDER BY CustomerID) As RowNumber,* FROM Customers ) Customers
     where RowNumber between @startRowIndex+1 AND (@startRowIndex+@maximumRows)
    end
else
    begin
      SELECT * FROM
      (SELECT ROW_NUMBER() OVER(ORDER BY CustomerID) As RowNumber,* FROM Customers where Country=@Country) Customers
      where RowNumber between @startRowIndex+1 AND (@startRowIndex+@maximumRows)
    end
END

--計算並Output出每次查詢的總筆數,此為Gridview分頁時所需要。
CREATE PROCEDURE [dbo].[sp_customers];2
@Country  nvarchar(15)='',
@total_count  int=0 output
AS
BEGIN
if len(@Country)=0
    begin
      select @total_count=(SELECT COUNT(*) AS TOTAL_COUNT FROM Customers)
    end
else
    begin
      select @total_count=(SELECT COUNT(*) AS TOTAL_COUNT FROM Customers where Country=@Country)

    end
END



有關ROW_NUMBER()的用法可叁考微軟ROW_NUMBER(),主要是可在SQL描述中,不使用Cursor的方式,可以很方便的去讀取特定筆數範圍的資料。

步驟四:

    我們將如何存取資料的邏輯撰寫在NorthwindSQL元件中的GetCustomer.cs內,程式碼如下:


public class GetCustomer
{
    public DataTable GetData(int startRowIndex, int maximumRows, string Country)
    {
        DataTable dt = new DataTable();
        SqlConnection objConn = new SqlConnection();
        objConn.ConnectionString =ConfigurationManager.ConnectionStrings
        ["NorthwindConn"].ConnectionString;
        SqlCommand cmd = objConn.CreateCommand();
        cmd.CommandTimeout = objConn.ConnectionTimeout;
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandText = "sp_customers;1";
        cmd.Parameters.Add("@startRowIndex", SqlDbType.Int).Value = startRowIndex;
        cmd.Parameters.Add("@maximumRows", SqlDbType.Int).Value = maximumRows;
        cmd.Parameters.Add("@Country", SqlDbType.NVarChar, 15).Value = Country;
        using (objConn)
        {
            objConn.Open();
            using (cmd)
            {
                SqlDataReader sdr = cmd.ExecuteReader();
                using (sdr)
                {
                    dt.Load(sdr);
                    return dt;
                }
            }
        }
    }

    public int GetCount(int startRowIndex, int maximumRows, string Country)
    {
        SqlConnection objConn = new SqlConnection();
        objConn.ConnectionString = ConfigurationManager.ConnectionStrings
        ["NorthwindConn"].ConnectionString;
        SqlCommand cmd = objConn.CreateCommand();
        cmd.CommandTimeout = objConn.ConnectionTimeout;
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandText = "sp_customers;2";
        cmd.Parameters.Add("@Country", SqlDbType.NVarChar, 15).Value = Country;
        cmd.Parameters.Add("@total_count", SqlDbType.Int).Direction =
        ParameterDirection.Output;
        using (objConn)
        {
            objConn.Open();
            using (cmd)
            {
                SqlDataReader sdr = cmd.ExecuteReader();
                using (sdr)
                {
                    int t = (int)cmd.Parameters["@total_count"].Value;
                    return t;
                }
            }
        }
    }
}



步驟五:

上個步驟我們已經將存取資料邏輯的元件寫好了,接下來就是本文最重要的地方,須在ObjectDataSource中指名要取用的元件為何,如下:

(1)點選ShowCustomer.aspx上的控制項ObjectDataSource,並點開[設定資料來源]。



(2)選擇我們步驟四中製作好的元件NorthwindSQL,記得先對此元件執行Compile的動作。



(3)接下來選擇SELECT頁籤中的GetData()這個方法,作為我們要回傳給ShowCustomer.aspx的資料取用Method。



(4)因為我們可接受user輸入與查詢的國別,來找尋Northwind資料庫中的Customer。故接下來我們需將傳入GetData()著参數對應到ShowCustomer.aspx中的txtCountry這個控制項。



(5)以上設定完成後,在ShowCustomer.aspx網頁上的ObjectDataSource各項設定如下:





步驟六:

    最後我們在ShowCustomer.aspx.cs中將ObjectDataSource所抓取的資料交給網頁上的Gridview控制項,以呈現可讓使用者選擇分頁的效果



執行網頁的成果如下:




結語:

    使用ObjectDataSource的分頁設定功能,再配合資料庫中ROW_NUMBER()的語法,可讓我們很快的只將必要的資料拋給前端的使用者,這對提升網站效能,有相當大的幫助。此外,ObjectDataSource可將網站架構中,前端網頁程式儘量只負責顯示(View)的工作,與業務資料處理邏輯元件獨立開,網頁設計人員與程式開發人員可彼此獨立作業,相同的前端網頁呈現設計,也可很快的套用在不同的資料處理邏輯元件,而不須做很多修改,大大的降低了前端網頁程式(aspx)中還要去處理業務與資料存取的複雜性。

2009年11月16日 星期一

ASP.NET C#如何動態產生GridView控制項與資料列

在這裡,我們想要傳入一個DataTable,然後動態產生一個GridView,並且設定各種CSS給這個GridView。

步驟一:

首先我們可以在網頁上這樣描述,先設定一個Label,用來顯示發生錯誤時的訊息。接下來建立一個Panel,這個Panel就是等一下動態產生出來的GridView要擺放的位置:

<asp:Label ID="lbl_ErrMsg" runat="server" />
<asp:Panel ID="pnl_data" runat="server" />

步驟二:

我們建立一個method,並自外部傳入一個DataTable當作GridView的資料來源

protected void CreateGridView(DataTable dt)
{
    try
    {
        if (dt.Rows.Count > 0)
        {
            //產生一個GridView
            GridView gv = new GridView();
            gv.ID = dt.TableName;
            gv.AllowPaging = false;
            gv.AllowSorting = true;
            //先設定不要自動產生GridView中的每一欄
            gv.AutoGenerateColumns = false;
            gv.Style.Add("width", "100%");
            //動態設定每一欄型態、欄名與格式化,並將DataTable的資料
            自動對應每一欄中
            foreach (DataColumn c in dt.Columns)
            {
                BoundField bf = new BoundField();
                bf.DataField = c.ColumnName;
                bf.HeaderText = c.ColumnName;
                bf.HeaderStyle.CssClass = "StyleHead";
                bf.ItemStyle.CssClass = "StyleItem";
                gv.Columns.Add(bf);
            }
            //設定列的格式
            gv.FooterStyle.CssClass = "StyleFooter";
            gv.RowStyle.CssClass = "StyleRow";
            gv.PagerStyle.CssClass = "StylePageNo";
            gv.SelectedRowStyle.CssClass = "StyleSelectedRow";
            gv.HeaderStyle.CssClass = "StyleHead";
            gv.EditRowStyle.CssClass = "StyleEditRow";
            gv.AlternatingRowStyle.CssClass = "StyleAlternatingRow";
            gv.Visible = true;
            //將DataSource指向DataTable
            gv.DataSource = dt;
            gv.DataBind();
            gv.PageIndex = 0;
            //將產生出來的GridView放置到網頁的Panel內
            pnl_data.Controls.Add(gv);
        }
    }
    catch (Exception ex)
    {
        lbl_ErrMsg.Text = ex.Message;
    }
}

步驟三:

這樣就大功告成了。