程式碼高𠅙

2009/09/15

WinForm: 如何判斷表單是否開啟在 Visual Studio 的 Designer 中

最近工作上用到比較多微軟的開發工具,像是 Visual Studio, Reporting Servoces, ASP.NET 等。

我們採用了一套 UI Framework,可以在 ASP.NET 上,以 WinForm 的程式設計模式來開發 Web 表單。在表單開發上,可完全免除自行撰寫 Client 端 JavaScript 的需要。簡言之,這種開發模式具有 WinForm 的優點,相對的,開發 WinForm 時所需要面對的問題,這裡同樣要面對。

例如,在開發 WinForm 表單時,我們常會在元件的 Load 事件載入資料來源,進行資料綁定。在執行環境這樣的邏輯沒有問題,但在開發環境,以 Visual Studio 的 Designer 開啟元件時,就可能會遇到無法讀取設定檔、資料連結錯誤等等問題,而造成 Designer 無法順利開啟表單。

我查詢了一下,在 MSDN Windows Developer Center 上也有人詢問這個問題,其中有個開發者已經給了一個可用的 solution,但因為他的描述過於複雜,且要求改寫基底類別,所以我擷取了他的解法,加以簡化。

首先,我們撰寫一個 GuiUtil 類別,其中包括下列方法:

public static bool InDesignMode() 
{
    try 
    { 
        return System.Diagnostics.Process.GetCurrentProcess().ProcessName == "devenv";
    }
    catch
    {
        return false;
    }
}

如此一來,我們就可以採用以下的程式碼,在 Client 端程式的 Load 事件中進行呼叫,而不會產生設計畫面出不來的狀況了。

if (!GuiUtil.InDesignMode())
{
        InitDataBinding();
}

-- 完 --

2009/07/07

有趣的 MIS 大大

剛剛 MIS 大大寄來一封停機公告,重點在那張圖:

各位辛苦的系統開發大大們,

這件事情已經拖了一個多月了,實在不一在拖下去了…

知道各位都相當的忙碌,不過一個多月的時間應該也足夠才對。

剛剛看過黃曆,7/18 是個好日子,我們會設定在 7/18 子時系統自動關機,之後就不再提供服務了!

如果各位還有資料沒有轉到新的測試主機 xx.xxx.xx.xxx 的,請利用這兩週趕快轉移。

感謝您的配合!!

yellow calendar.png

2009/07/04

Firefox 小技巧--將收藏庫開啟於分頁標籤中

Open Firefox Library in a Tab

不曉得有沒有人曾想過,把 Firefox 內的收藏庫 (Library, 管理書籤跟歷史的使用界面),開啟於分頁標籤中,而不是獨立視窗中。

如果你也曾想這麼做,那就這麼做吧 -- 在網址列中輸入以下 URL:

chrome://browser/content/places/places.xul

噹噹…

現在收藏庫便開啟於分頁標籤之中了:

Firefox Library in a Tab

Technorati : , , , ,

2009/06/05

JavaFX 與 TIOBE Programming Community Index

TIOBE Programming Community Index 是一個用來評量程式語言受歡迎程度的指標,其排名方式,在頁面上說明如下:

The index is updated once a month. The ratings are based on the number of skilled engineers world-wide, courses and third party vendors. The popular search engines Google, MSN, Yahoo!, Wikipedia and YouTube are used to calculate the ratings.

我大概每個月初都會略微的瞄一下,了解一下目前各主要程式語言的排名狀況。昨天在檢視的時候,發現了 Sun 推了一陣子的 JavaFX 語言,並沒有在排名裡面。可能原因是 JavaFX 並沒有在百大排名裡面,或者是 TIOBE 並沒注意到這個程式語言。而我發現網頁上有這一行字:

If you have the impression there is a programming language lacking, please notify us at tpci@tiobe.com.

也就是說,如果你發現 TIOBE 可能漏掉某個程式語言的排名,可以寫信告訴他們。因此,我想先確定一下,JavaFX 目前是否有資格擠進百大。我用了一個「比較法」來確認,也就是,如果某語言在 Top 100 或 Top 50,而 JavaFX 的熱門程式與該語言相當,那麼 JavaFX 也該在 Top 100 或 Top 50 左右。

這個用來做比較的語言,最好不要有一字多義的傾向。例如,Ruby、Python、Groovy、Alice 等就都不適合,因為他們不是一字多義,就是菜市場名字。後來我決定以 Erlang 來做比較,比較結果如下:

就查詢量來看,javafx 有超越 erlang 的趨勢。有了這個發現,我便動筆寫了信給 TIOBE 的成員,而沒想到昨天才寄的信,今早就收到回信。而 TIOBE 也把 JavaFX 納入排名,目前排名第 72。另一個值得小小高興一下的是,TIOBE 的回信者 Paul 在信中說道,為了感謝我的建議,他會在最近一期的排名中公提到我的名字。看看 6 月份的排名,哇,真的有提到我耶 ^_^!

2009/05/05

有趣的 Ruby 字串操作

irb(main):025:0> a= "abc def"
=> "abc def"
irb(main):026:0> a["def"] = "abc"
=> "abc"
irb(main):027:0> a
=> "abc abc"
irb(main):028:0> a["abc"] = "xyz"
=> "xyz"
irb(main):029:0> a
=> "xyz abc"
irb(main):030:0> a["abc"]
=> "abc"
irb(main):031:0> a["def"]
=> nil
irb(main):032:0>

date = "2009/12/12"
month, day, year = date.split('/')

Technorati : ,

2009/04/23

C # 裡面 ?? (雙問號)運算子的妙用

?? 運算子是在 2.0 版後新增的,至今我在別人的程式中還很少看到有在使用。
它的使用方式如下:

a = b ?? c;

上式的意思是,如果 b != null,那就把 b 的值指定給 a,否則就把 c 的值指定給 a。
照理講,?? 的功能完全可以由 ?: 運算子替代,如上式可以改寫如下:

a = b != null ? b : c;

不過,?? 對於簡化程式碼很有幫助,我相信許多 one liner 會很喜歡它,例如:

if (Session["WEB_TYPE"] == null)
{
Session.Add("WEB_TYPE", "");
}

可以改寫為:

Session["WEB_TYPE"] = Session["WEB_TYPE"] as string ?? "";

?? 的另一個特點是使用上可以串連,例如:

string a = null;
string b = null;
string c = "c";
string d = a ?? b ?? c;

這樣的意思,也就是由左到右,找到第一個非 null 值的變數,指定給 d。

於是,如果有一段程式如下 (抓取系統中的舊程式碼舉個例子):

if (Session["WEB_TYPE"] == null)
{
Session.Add("WEB_TYPE", "");
}

//接收 Web_type
if (!IsPostBack)
{
if (Request.QueryString["WEB_TYPE"] != null)
{
webtype = Request.QueryString["WEB_TYPE"].ToString();
Session["WEB_TYPE"] = webtype;
}
}

可以用 ?? 運算子改寫成以下版本:

Session["WEB_TYPE"] = Session["WEB_TYPE"] as string ?? Request.QueryString["WEB_TYPE"] ?? "";

這樣程式就精簡許多,也更好理解與維護了。

2009/04/22

在麥當勞看了一場戲

一個皮膚黝黑的男子,兩眼無神的呆坐在餐飲區的一角。
一個體格壯碩的男子帶著一個清瘦的女生走了進來,坐在黝
黑男子的身旁,對他交待了幾句,拍拍了那女生的肩,便走
開了。

那男的哭喪著臉,哀求的詢問著那女生,那女生正顏厲色的
反過來詰問那個男的… 那男的無言以對,熱淚涔涔而下,
這種狀況來回幾次,那男的奪門而出。

那個壯碩的男子回來,再度拍拍那女生的肩,安慰她幾句,
再度離去把那女生留在現場。

最後,另一個面容白晰的男生走了進來,拉著那女生的手,
摟了摟那女生的肩,聊了幾句。那女生嘆了口氣,如釋重負
般的站了起來,那男生也跟著站了起來,兩個人牽著手,漫
步離開現場。

2009/03/13

搜尋引擎設定也能帶著走

Foxmarks/Xmarks 這類線上書籤同步工具的出現,讓我們在不同的電腦與瀏覽器之間同步書籤。我習慣叫這種應用叫 "書籤帶著走"。但是,不曉得有沒有人和我一樣,想把瀏覽器上的其他設定也帶著走呢?

對我來講,除了書籤之外,"搜尋引擎設定" 也是我想帶著走的。這裡的搜尋引擎,提的是「搜尋工具列」或是「Smart Search」裡面的搜尋引擎。

「搜尋工具列」大家比較清楚,就是網址列旁的搜尋框。而 Smart Search 知道的人或愛用的人可能比較少,有人叫它「智能搜尋」,或叫 Quick Searches,這裡有篇文章介紹;基本上是讓你把書籤當成搜尋引擎使用的一種方式。

過去我一直覺得奇怪,Firefox 為何要有兩套不同的搜尋機制,也不了解這兩者間如何選擇。後來因為有了 ConQuery 及 Context Search 這類套件,讓我可以在右鍵選單上直接使用搜尋功能,這種方便性讓我一直以「搜尋工具列」裡的搜尋引擎作為我的主要搜尋工具。

但是,搜尋工具列裡的搜尋引擎在使用上卻有一個缺點,那就是,他們無法像書籤一樣,可以帶者走… 因此,在 Firefox 2.x 版的年代,我便透過一套叫 FireUploader 的工具,手動將搜尋引擎設定上傳到某個網站,然後再於別台電腦將它們下載回來,但這終究不是長久之計啊!

後來找到一個就叫做 SmartSearch 的套件,這個套件做的事與 ConQuery 或 Context Search 類似,就是讓你可以在右鍵選單上,選擇你要使用的搜尋引擎進行查詢,但不同的地方在於,它的搜尋引擎選單是來自「智能搜尋」的 Quick Searches Bookmarks,這代表著,你的搜尋引擎清單就是書籤連結,是可以「帶著走」的--只要你同時安裝 SmartSearch 及 Foxmarks/Xmarks 即可。

相關連結:

Technorati : ,

2009/03/04

SQL 設計小技巧--用 ISNULL 或 NVL 達到選擇性條件的下法

透過表單介面查詢資料庫時,常會遇到一種情況,即表單介面上有很多查詢選項,但不一定都要設定。若是不設定,代表 "忽略" 那條查詢選項。

舉個例子,假設畫面上有個欄位是要依照 "學歷" 及 "性別" 取出會員資料,因此,查詢資料庫的 SQL 可能如下:

   select * from member where education = @education and gender = @gender

若此時使用者未設定學歷條件,則查詢語法變成:

   select * from member where education = @education

當條件變動,還有另外兩種可能的查詢語法:

   select * from member where gender = @gender
   select * from member where education

如果你是用 PHP,由前端先組好查詢法語,再傳入後端資料庫處理,這沒有多大問題,只要透過一些 if 的判斷即可。但想像一下,若有 10 個查詢選項,那組合 SQL 語法的過程,不會讓你感到高興吧。

如果你是透過 ORMapping,雖然可以避免自行組合 SQL 查詢語法,卻也免不了要判斷該參數是否已設定,有設定的參數,才需加入 query criteria 中。

透過程式語言組裝查詢條件,再送入資料庫查詢,除了設計上的不便外,也不是所有的情況下都可使用。例如,假設你使用微軟的 Reporting Services,報表資料集的來源,直接就對應到 "靜態的" SQL 語法,你無法在執行期才去組裝它,這時就要使用不同的作法。

一個好用的技巧,是善用 ISNULL (MS SQL/T-SQL) 或 NVL (Oracle/PLSQL) 函式,它們的語法近似:

  • ISNULL(A, B)
  • NVL(A, B)

上面兩個式子,都代表著,若是 A 不等於 NULL,則傳回 A,否則傳回 B。

這個東西的有用之處,讓我們直接透過例子來解釋,把上面的查詢語句改為如下:

   --MS SQL
   select * from member where education = ISNULL(@education, education) and gender = ISNULL(@gender, gander)

   --Oracle
   select * from member where education = NVL(:education, education) and gender = NVL(:gender, gander)

類似這樣的式子,就可以解決我們所有的查詢組合情況。我們取出其中一段做分解說明:

   gender = ISNULL(@gender, gander)

@gender 代表查詢參數,當 @gender 未設定查詢條件時,我們讓他預設值為 NULL,結果 gender = ISNULL(NULL, gander) 就變成 gender = gender,而這個條件總是成立,所以就等於沒下這個條件。反之,或 gender 設定為 'F' 時,gender = ISNULL('F', gander) 就變成 gender = 'F',如此一來,就只會查詢出性別為 F 的會員資料。

基於這一原理,即使你的查詢條件有 20 個、30 個,你也不用再費心思索如何組裝查詢條件了。

2009/02/24

如何取得 Web 應用程式的根目錄 Context URL

我們常常在開發 Web 應用程式時,會有需要取得 Context URL。例如,為了透過這個 URL 取得某些資源。Context URL,也就是 Web 應用程式根目錄的 URL。例如,給定一個 URL 如下:

    http://test.com:8080/AppContext/path/to/your/page

則 Context URL 指的是:

    http://test.com:8080/AppContext   

如果我們都是以相對路徑 (相對於根目錄或或目前路徑) 的方式來存取資源,那直接使用 Context URL 的必要性就比較低了。不過有時候並無法使用相對路徑,例如:

  • 因為配合 API 的呼叫,你不曉得這個 API 會在虛擬路徑的第幾層呼叫,因此就無法提供相對路徑。
  • 因為配合某種 code gen 機制,你要傳入一個 URL。
  • 因為採用了某種 URL dispatch 的機制,導致你網頁相對路徑發生偏差。

當然還有其他原因。而面對這個問題的解法,傳統上是在 web.config 或 web.xml 裡面去定義一個類似 HOST_URL 的參數來解決。但缺點是,每次 deploy 時,就得變更 HOST_URL 的設定,除了顯得不便之外,因會增加 API 與環境變數的相依性,實在不能算是很好的做法。因此透過程式自動抓取 ContextURL 的作法,便有其必要。

現在,我們假設我們的 ContextURL 就是以上的形式,而不是 WebServer 的根目錄 (例如 http://test.com:8080/),那麼取得 Context URL 的作法,在一般情況下並不難。首先看看 ASP.NET/C# 版本:

   int idx = Request.Url.AbsoluteUri.IndexOf(Request.ApplicationPath);
   string serviceUri = Request.Url.AbsoluteUri.Substring(0, idx); // 伺服器 URL
   string applicationUri = serviceUri + Request.ApplicationPath; // 應用程式 Context URL

JSP/Java 版本如下:

   int idx = request.getRequestURL().toString().indexOf(request.getContextPath());
   String serviceUri = request.getRequestURL().substring(0, idx); // 伺服器 URL
   String applicationUri = serviceUri + request.getContextPath(); // 應用程式 Context URL

不過有些情況比較特殊,例果透過某種 URL dispatch 的機制來存取網頁,你會發現用以上方式取得的 URL,與 Client 端發送的 URL 並不相同。我們以 IBM Tivoli Access Manager 為例,它的 URL 組成格式如下:

      http://sso.com/JUNCTION/AppContext/path/to/your/page
  • sso.com: 是一個用來作 singal sign-on 的網站
  • JUNCTION: 用來試別要將 client 端 request dispatch 到後端哪個實發處理的網站
  • /AppContext/path/to/your/page: 後端處理要求的網頁或程式,實際位址可能是 http://192.168.1.111:8080 /AppContext/path/to/your/page 之類的。

也就是說,當 Client 端發送 http://sso.com/JUNCTION/AppContext/path/to/your/page 這個請求時,經過 Tivoli Access Manager 轉送後,Server 端實際取得的服務位址是 http://192.168.1.111:8080/AppContext/path/to/your/page。

為了解決這個問題,我們需要動用到 HTTP Header 裡面的一個參數 -- referer。referer 是指目前存取這個網頁,是透過哪個網頁連結過來的。有了這層認識,我們可以將上面的程式碼改成如下。 ASP.NET/C# 版本:

   if(IsPostBack) {
      idx = Request.UrlReferrer.AbsoluteUri.IndexOf(Request.ApplicationPath);
      serviceUri = Request.UrlReferrer.AbsoluteUri.Substring(0, idx); // 伺服器 URL
      applicationUri = serviceUri + Request.ApplicationPath; // 應用程式 Context URL
   }

JSP/Java 版本如下:

   if(request.getParameter("IsPostBack").equals("true")){
      idx = request.getHeader("referer").indexOf(request.getContextPath());
      serviceUri = request.getHeader("referer").substring(0, idx); // 伺服器 URL
      applicationUri = serviceUri + request.getContextPath(); // 應用程式 Context URL
   }

如果你是直接在瀏覽器的網址列輸入 URL,或是點選書籤/我的最愛直接連結,那 referer 的值會是 null。因此,我們可以透過檢查這個網頁是否是 post back 網頁,來確保 referer 有值。注意一下,JSP 裡面並沒有預設的 IsPostBack 檢查機制,你要自己做。

另外,有些防火牆或防毒軟體也可能會改寫 Client 端發出的 referer,使得 Server 上取得的值為 null。若是這種情況,可以試著透過 client 端的 Javascript,取得 document.location.href 的值,將它指定給某個隱藏表單欄位,在 post 時傳給 Server 端來加以解決。

2009/01/23

Java SE 6 Update 10 裡面的 Nimbus Look and Feel

Java 視窗應用程式在過去給部分人留下著刻版印象,總是認為那是跑得慢,看來醜的應用程式。不過,這真的只是刻版印象。個人在過去曾經以 Swing 套件,開發了 4 年以上的 Java Client 端應用程式,無論是在啟動速度、執行效能及視覺美觀上,Swing 介面均能達到令人滿意的程度。

而事實上不論是 Sun 或是 Java 社群也一直在 Look and Feel (就相當於佈景主題--Theme 的概念) 下了相當心血。在 Java SE 6 Update 10 裡面,就包括了一個 Nimbus Look and Feel,這是一個簡潔、洗練、具現代感的 Look and Feel。我們先來看看 SwingSet3 在 Windows XP 中的呈現樣式:

SwingSet3Nimbus

是不是還不賴呢? 我們展現一下 Metal Look and Feel 比較一下:

SwingSet3Metal

除了視覺上的輕巧之外,Nimbus Look and Feel 在實質上也是相當輕巧的,因為你在畫面上所看到的各個元件,包括按鈕、捲軸、下拉選單及標題的漸層及陰影等效果,全部是用 Java 2D 向量方式 (而不是透過 bitmap 圖檔) 畫出來的,因此才占用了 56 KB 的空間。

設定 Swing 應用程式使用 Nimbus Look and Feel

這樣漂亮的 Look and Feel,並沒有隨著 Java SE 6 Update 10的到來,而成為 Swing 預設的 Look and Feel。開發人員必須加以設定,才能在應用程式中以它作為佈景主題。隨著需求的不同,設定方式至少有三種。

在應用程式中進行設定--透過程式碼指定,你所要使用的 Look and Feel。這種方式只會將主題套用到應用程式本身,這只需要一行程式碼:

UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");

或者,也可以在啟動 Java 程式時,加入以下的 java 啟動參數:

-Dswing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel

如果你想將 Nimbus Look and Feel 設定成整台電腦中,所有 Swing 程式的預設主題,則可以在 /lib/swing.properties 這個檔案中,加入底下這行:

swing.defaultlaf=com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel

如果上述的設定檔不存在,你可以自行新增一個。

在 jEdit 中設定 Nimbus Look and Feel

除了以上方式之外,有些應用程式可以透過 UI 介面,讓你設定應用程式的 Look and Feel。例如 jEdit 的這個以 Java 開發而成的編輯器,就可以在 Utilities/Global Options... 主選單所開啟的 Options 視窗中,選擇 Appearance 設定 Look and Feel。以下是套用 Nimbus 之後的外觀:

jEditNimbus

附上一張預設樣式 (Metal) 作比較:

jEditDefault

設定 NetBeans 使用 Nimbus Look and Feel

一直覺得 Sun 是一家有著偏執傾向的公司,他們似乎有著一種,不管別人如何批評,想要做的,便會全力以赴做到好的企業精神。因此這幾年來在 NetBeans IDE 上的進展是有目共睹的 (雖然,我還是 Eclipse 的愛用者啊! ),而我也因此相當看好 JavaFX 未來的發展…。OK,回歸正題,我想社群裡面 NetBeans 的愛好者應該也不少,要設定 NetBeans 使用 Nimbus Look and Feel 也很簡單,只要找到 NetBeans 的安裝目錄,在 etc 子目錄下的 netbeans.conf 設定檔中,找到 netbeans_default_options 設定,在設定中加上 --laf Nimbus 即可。例如,我的設定是這樣子的:

netbeans_default_options="-J-Dorg.glassfish.v3.installRoot=\"C:\java\glassfish-v3-prelude-b15b\" -J-Dcom.sun.aas.installRoot=\"C:\java\glassfish-v2ur2\" -J-client -J-Xverify:none -J-Xss2m -J-Xms32m -J-XX:PermSize=32m -J-XX:MaxPermSize=200m -J-Dnetbeans.logger.console=true -J-ea -J-Dapple.laf.useScreenMenuBar=true -J-Dsun.java2d.noddraw=true --laf Nimbus"

以下是套用 Nimbus 之後的外觀:

NetBeansNimbus

再附上預設外觀看看:

NetBeansDefault

其實,隨著應用的不同,你很難找到一套一體適用於所有應用情境的 Look and Feel,不過,這又是另一個主題了,我就此打住。