2009年10月18日 星期日

[NASM] BIOS 啟動程序

當電腦的電源開啟後,處理器會進入 reset 狀態,此時:
  1. 所有記憶體的內容會變為 0

  2. CS 的值會被預設為 0FFFF[0]H

  3. IP(Instruction Pointer) 儲存指令的 offset,預設為 0

結合 2 & 3(Code Segment + Offset),可以知道第一個要執行的指令的 memory address 為 0FFFF0H(CS:IP),而這個 memory address 即為 BIOS 的進入點。

BIOS 的常式位於 0FFF0H,他檢查各個 I/O port,並對於存在的設備進行初始化,接著 BIOS 會建立兩個資料區:
  1. Interrupt Vector Table (中斷向量表)
    長度為 1 KB,用來做為當 interrupt(中斷) 發生時,BIOS & OS 用來選擇相對應的 interrupt handler 之用,詳細資料可參考此篇文章

  2. BIOS 資料區
    起始位址為 40[0]H,大小依據設備的多寡而不同。

接著 BIOS 會檢查系統磁碟是否存在,並從系統磁碟下載啟動程式,啟動程式接著從系統磁碟將系統檔案載入 memory,並將控制權交給 OS。

2009年10月9日 星期五

[自動化] 序列埠模擬器(Virtual Serial Port Emulator)

最近開發工作由於沒有實體的設備可以接.....因此要想辦法進行模擬...

而模擬的設備又是走序列埠(serial port),因此只好去找找有沒有免費的模擬器可以用...

上 Google 搜尋「free serial port emulator

果然給我找到一套免費的可以用 => Free Virtual Serial Ports Emulator

目前的版本是 0.936.4.687,希望這好東西可以持續的推陳出新囉!

我使用的情境如下:

  1. 建立的 Device Type 為 pair (假設為 COM2 + COM3)

  2. Modbus Slave 模擬程式連結 COM2

  3. 程式向 COM3 進行 pooling

這樣就很完美的連結起來啦! 

PS. 這套軟體無法作序列埠的相關設定,例如:Baud Rate。是比較可惜的地方,希望未來可以加入這功能囉!

2009年9月20日 星期日

[OS] Computer System Organization

Computer-System Operation

現今電腦系統中包含了一個或數個 CPU,以及多個連接在 common bus 上個多個 device controller,每種不同的 device controller 用來處理不同的設備,如下圖所示:


CPU 與其他 device controller 可以一起執行,並相互競爭取得 memory cycle;而為了確保不同設備可以依序的存取 shared memory,memory controller 必須提供不同設備同步存取記憶體的方法。

當電腦剛開機時,第一個執行的程式稱為「bootstrap」,而 bootstrap 通常存放在硬體的 firmware 中,目的是用來載入 & 啟動 OS kernel。

為了做到上述工作,bootstrap 就必須知道 OS 存放的記憶體位址,才有辦法順利載入執行,而 OS 載入後所執行的第一個 process,一般稱為「init」,並等待其他軟/硬體觸發事件以進行處理。

硬體是透過傳遞訊號給 CPU 來觸發 interrupt,而軟體則是透過呼叫 system call 來觸發 interrupt;當 CPU 收到 interrupt 訊號,會執行以下動作:
  1. 立即停止目前工作

  2. 將所要執行的工作傳到特定的位址執行(此位址即為提供執行工作服務的起始位址)

  3. 當工作執行完畢,CPU 則會恢復中斷前的運作

下圖展示出 CPU 執行與工作執行的時間軸:

Interrupt(中斷) 在電腦架構中是非常重要的一個環節,也許不同的電腦會有不同處理 interrupt 的機制,但總是會有共同的部分,當 CPU 收到 interrupt 的訊號時,就會暫停手邊工作,並將 interrupt 丟給合適的中斷處理程序進行處理。

最直接的實作方式是有一個專門的程序(routine),會針對收到的 interrupt 訊號資訊,將其丟給相對應的中斷處理程序處理。

另外一種不需要專門程序處理中斷的方式,則是有一個存放「指向 interrupt routines 的指標(pointer)的表格」,因此 interrupt routine 可以直接透過此 table 儲存的 pointer 進行呼叫,而不需要有個專門的代理程序來處理這個部分。

每個不同設備所產生的 interrupt 都會有不同的 interrupt routine 來處理,並會以唯一的設備編號作為索引,而這些資訊都會儲存在記憶體最前面的位址。


Storage Structure

主記憶體是 CPU 唯一可以直接存取的區域,而兩者之間的互動是透過 CPU 呼叫 store & load 兩個指令,將資料在 memory & register(暫存區)。

其中 load 的作用是將長度為 1 個 word 的資料從 memory 搬到 register 中;而 store 則是將 1 個 word 的資料從 register 回存到 memory 中。

然而,除了基本資料的搬移外,CPU 也是會從 memory 中載入指令以執行不同的工作,其運作步驟如下:
  1. CPU 從 memory 中取得所要執行的指令,並將其存放於 instruction register

  2. CPU 會將此指令拆解成相對應的 CPU 指令集並執行

  3. 最後將執行結果回存至 memory 中

然而,記憶體中的資料是沒辦法永久儲存的,只要停止供電後就會消失;因此需要其他儲存媒體來協助進行資料的儲存,最常見的當然就是硬碟機 or 光碟片了! 當然還有最近很夯的 USB 隨身碟(屬於 flash memory)。


I/O Structure


OS 中有一大部分是專門處理 I/O 的,原因是因為 I/O 的處理好壞會影響到整體系統的效能 & 穩定度。

I/O 動作再 OS 是按照以下步驟進行的:
  1. device driver 會先讀取在 device control 中 register 的資訊

  2. 接著 device controller 會檢視 register 中的內容,判斷所要執行的動作為何 (例如:從鍵盤讀取一個字元)

  3. device controller 開始從裝置中讀取資料至本地端的緩衝區

  4. 資料傳輸完畢,device controller 會發送 interrupt 告知 device driver

  5. 最後 device driver 會將控制權歸還給 OS

  6. 若是執行的動作為 read,就會傳取得的資料(or 指向資料所在的位置的 pointer);但若執行的是其他動作,則會回傳狀態資訊

看了上面的動作,若是還搞不懂,可以將整個動作想像成:設備要傳遞資料到記憶體中,必須一直發中斷給 CPU,並將資料傳給 CPU 後,由 CPU 把資料放入記憶體中。

顯而易見的,所有資料在傳遞時都必須依賴 CPU 的處理,若是速度很慢的裝置(例如:軟碟機)進行 I/O,就會拖垮整個系統的運作效能。

因此,以上的 I/O 動作在處理量少的資料時還可以,但若是處理量大的資料(例如進行 Disk I/O),就會產生極高的 overhead,而為了解決此問題,就有了 DMA(Direct Memory Access) 的產生,以下有張示意圖:


有了 DMA 之後,device 就可以直接將資料以 block 的方式從本身的緩衝區傳入 memory 中,且不會中斷 CPU 的工作,頂多就是當每個 block 資料傳遞完畢後,發個 interrupt 通知 device driver 動作已經完成。

有些更高階的 OS,使用的是 switch 的機制而非 bus 的架構,在這種 OS 中,不同的元件之間不需要在共用的 bus 上搶奪 cycle,且可以同時相互溝通。


參考資料




2009年8月19日 星期三

[LINQ] 製作分頁功能 - 使用 skip & take

開發網頁程式時,分頁功能總是少不掉的.......

然而不使用 skip 與 take 來過濾資料,全部交給 GridView 控制項處理也是可以......

以下是使用範例:

xxx.aspx

xxx.aspx.cs


然而若是使用 skip & take,就可以僅取得部份資料,讓分頁相關的程式撰寫上更加有彈性,以下是使用方式:

xxx.aspx

xxx.aspx.cs

執行結果

2009年8月16日 星期日

[Silverlight 3.0] 如何讓消失的 ASP.NET Silverlight 控制項重新出現

昨天安裝了 Silverlight 3.0 的開發環境準備來學習一下 Silverlight

結果看書上有 ASP.NET Silverlight 控制項,但我的 VS 2010 就硬是不給我出現,解決方式有兩種:
  1. 安裝 Silverlight 2.0 SDK (沒錯......裝了 3.0 SDK 並不會有 2.0 SDK 有的東西....超怪....)

  2. 若不想安裝 2.0 SDK,則可以參考此篇文章,額外下載並加入 ASP.NET Server Controls for Silverlight Samples 即可

2009年8月13日 星期四

[LINQ] 常用語法介紹

根據特定條件取得資料 - Where

IEnumerable<T>.Where
可以協助將篩選出我們想要的資訊,共有兩種用法,首先介紹一般 where 的用法:

接著是另一種較為特別的用法,其中還將資料的 index 也考慮進來:


取得特定欄位資料 - Select

Enumerable.Select
用來取得我們所要得資料欄位或是物件,使用上除了可用 LINQ 的語法之外,亦可透過 Extension Method 的特性來使用 Select。

首先介紹透過 LINQ 語法來使用 Select:

接著是使用 Extension Method:


此外,LINQ 還提供了一個 Enumerable.SelectMany 的語法,可以將取得的集合物件直接展開使用,以下用範例說明:


去除重複 - Distinct

與 SQL 語法相同,LINQ 中也提供了 Distinct 來過濾掉重複的資料:


將 query 後的資料轉為其他集合型態

在 LINQ 中提供了 ToList()ToArray()、ToDictionary 等方法將 query 後的資料轉為不同的集合型態來使用,但有一點需要注意,.NET 處理的方式是「複製一份資料集合」。

因此,若是 query 得到的資料很龐大,想當然而呼叫 ToList() 或是 ToArray() 這一類的方法就會很耗費系統資源,使用上就必須要注意一下。


聚合函數(Arregation Functions)

LINQ 中也提供了許多聚合函數,例如:Max、Min、Sum、Count ... 等等,以下介紹使用方式:


排序(Sorting)

根據條件取得資料後,也許還會有排序的需求,當然 LINQ 也提供了排序的語法 Enumerable.OrderBy,以下介紹其使用方式:

從上面的 orderby 使用方式可以發現,可以同時進行不同欄位資料的排序,而這一段 query,.NET Framework 是怎麼幫我們達成的呢? 

其實是將這一段語法拆成三段語法而成的:

LINQ 在語法上的使用彈性可見一班!


巢狀搜尋(Nested Query)

從上面的範例到至今,我們都是以 Book 為主的搜尋,根據不同的條件取得不同的資料。

假設我們要改以 Publisher 為主來搜尋呢? 假設要搜尋的資料是不同的 Publisher 所出版的書籍清單,這該怎麼作呢?

由於每個 Publisher 出版的書籍數量不一定是僅有一本,因此搜尋出來的資料就肯定無法用 row by row 的方式來呈現;而是必須取得 Publisher 清單後,再根據每個 Publisher 取得相對應的出版書籍。

因此我們可以將目標設定在取得類似以下資料:
  • Publisher(出版商) -> Books(出版書籍清單)

  • Publisher(出版商) -> Books(出版書籍清單)

  • ..... (etc)

可以用以下語法達成:

當然 aspx 中也要有相對應的 GridView 設定:

執行結果如下:


群組(Grouping)

以上面為例,若是要達到相同效果,也可以改用 Group 的方式,而且透過 Group 的方式取得資料,沒有出版書的 Publisher 就不會出現囉!

aspx 的部分就跟 Nested Query 中的相同!

執行結果如下:

仔細觀察程式碼,其中的 PublisherBooks 為 LINQ 處理後所會回傳的群組資料(根據 book.Publisher 分群),而 PublisherBooks 則會預設實作 IGrouping 介面。

實作 IGrouping 介面即表示這群資料集合中,都擁有相同的 key 值,也就是作為分群條件的 book.Publisher。

跟 Nested query 相比較,使用 Group 的方式取得資料至少有兩樣優點:
  1. 查詢語法更加精簡

  2. 可在查詢語法中對群組命名,以便後續使用(例如:與 aggregation function 進行搭配使用)


Joins

既然 LINQ 是針對資料集合進行處理,那怎麼會少掉 Join 這個強大的語法呢?

以下用 Join 語法來達到上面範例分群組的功能:

aspx 的部分也是跟先前的範例相同!

執行結果如下:

而從這範例結果可以看出,沒有出版書籍的 Publisher 也會一並被列出(跟 Nested Query 的結果相同),而若是要避開這種情況,可以改用其他 Join 的方式。


Inner Join

這是一般最常用的 join 方式,會過濾掉沒有符合資料的紀錄,以下是使用方式:

執行結果如下:


Left Outer Join

若是要留下 join 中沒有比對符合資料的紀錄,就必須改用 Left(Right) Outer Join 了,但 LINQ 中並沒有提供語法可以直接作到,因此要搭配 DefaultIfEmpty 方法來達到此效果:

執行結果如下:


Cross Join

這個就是有名的 Cartesian Product(卡氏積),會將 Join 兩方所有可能的組合通通列出,在 LINQ 中要達到這樣的效果,可以用下列的方式:

以下是程式執行結果:

2009年7月4日 星期六

[JavaScript] 將JavaScript 物件轉為 json 字串

一般將物件轉為 json 字串都是在編譯式的程式語言中進行的! 而這次有需要宣告 JavaScript 物件並轉為 json 字串。

Google 了一下,發現已經有人提供寫好的 JavaScript Library 可以用啦!

以下有一段語法,包含 JavaScript 物件的宣告並搭配 jQuery 一同使用此 library:



參考資料


[jQuery] 引用外部 JS 檔

在使用 jQuery 時,有時會需要額外引用外部的 js 檔案來使用裡面的功能

要怎麼作呢?

以下有一段範例語法:


參考資料

javascript include javascript(使用jQuery) - cloudio™- 點部落


2009年6月25日 星期四

發神經的 VS.NET 2008

平常用 3.0 以後提供的語法 & 功能習慣了.....例如:
public string test { get; set; }

var myVar = "text";

var newObject = new MyObject { Name = "myobject", Age = 12; }
大概就以上這些語法.......

今天用 VS.NET 2008 開啟後,重新編譯卻發現這些語法都死光光了.....例如出現以下訊息:
'xxxxxxx' 不是標記成 abstract 或 extern,因此必須宣告主體
上面就是因為用了第一段程式碼的第一行語法所出現的錯誤.....(但這是 2.0 才會出現的...)

看到這情況真是莫名其妙,於是再專案"屬性頁"中,在"建置"中的目標 Framework 中選擇 ".NET Framework 3.0" 編譯一次(當然會錯誤),再改回 ".NET Framework 3.5" 編譯一次就正常了....

2009年6月23日 星期二

[WPF] 項目控制項

簡介

由於項目控制項繼承自 System.Windows.Controls.ItemsControl 類別,因此可以容納整個集合的物件,不限定單一物件,而且每個項目都可以是任何物件。(UIElement 會呈現出來,非 UIElement 則是顯示 ToString() 的結果)

ItemsControl 有幾個屬性必須要了解:
  1. HasItems:用來檢查集合中有沒有內容

  2. IsGrouping:用來辨識集合中的項目是否有分群

  3. DisplayMemberPath:使用項目物件中的屬性來顯示(也可以是運算式喔!)

而項目控制項大致可分為以下三類:
  1. 選取器

  2. 功能表

  3. 其他項目控制項



選取器

選取器的特色在於可用 index 的方式存取,並且可以選取。

因此選取器繼承 ItemsControl 之後,額外加入了處理選取功能的屬性,例如 SelectedIndex、SelectedItem、SelectedValue .... 等等都是。

而 WPF 中提供了四個選取器控制項,分別是 ComboBoxListBox、ListView、TabControl。


ComboBox

ComboBox 中定義了屬性 IsDropDown 可判斷目前下拉式選單的狀態是展開或是收合;另外還定義了兩個事件,分別是 OnDropDownOpened 以及 OnDropDownClosed,可在有特殊需求時填入相對應的程式碼。

以下為範例:
(IsEditable = false)

(IsEditable = true)

若是要更清晰的表達每個項目,可使用 ComBoxItem 標籤包住每一個項目內容,如此一來,不只清楚,還可以在 ComboBoxItem 標籤上加上 IsSelected 或是 IsHightlighted 等屬性。

但若使用 ComboBoxItem,TextSearch.Text 屬性就無法設定在上面範例的 StackPanel 上,而是要設定在 ComboBoxItem 上囉!


ListBox

基本上 ListBox 跟 ComboBox 是很相似的,只是 ListBox 一次顯示所有項目而已,且有三種不同的選取模式:
  1. Single (預設)

  2. Multiple

  3. Extended:搭配 Shift/Ctrl 進行多選

而 ComboBox 有 ComboBoxItem 標籤,ListBox 也有 ListBoxItem 標籤可以使用囉!


ListView

ListView 繼承自 ListBox,額外增加了 View 的屬性,可以讓開發者自訂更豐富的顯示方式,以下示範使用方式:




TabControl

TabControl 是很基本的控制項,以下為示範:





功能表

這部分有 Menu 以及 ContextMenu 兩個控制項,使用起來的效果與原本 2.0 中其實是差不多的,可以參考以下範例:




其他項目控制項

此部分包含了 TreeView、ToolBar、StatusBar 等等。

TreeView

以下是 TreeView 控制項的使用範例:

另外還有查到 TreeView 的使用範例教學,可以參考看看!

而 TreeViewItem 較為常用的屬性有 IsExpanded、IsSelected 兩個屬性,以及 Expanded、Collapsed、Selected、Unselected 這幾個事件囉!


ToolBar

可以把 ToolBar 想像是功能表的加強版! 可以將許多按鈕群組在一起。

以下找到一個簡單的使用範例教學:



StatusBar

StatusBar 也像是功能表,但其項目是以水平堆疊而成的,上網找到幾個不錯的相關教學:


2009年6月19日 星期五

[WPF] 內容控制項

簡介

內容控制項的 Content 僅能放入一個物件,皆繼承自 System.Windows.Controls.ContentControl 類別,而 Content 中的物件可以是任何型別,放入的 element tree 有多大皆可! 但僅能有一個直接的 child element。

ContentControl 中有個 HasContent 屬性,有此屬性的定義,就可以透過屬性觸發程序很輕易的在 HasContent 屬性改變時進行其他屬性的設定動作。
(上面這一招在 WPF 中使用很多.............)

內容控制項分為三大類:
  1. 按鈕

  2. 簡單容器

  3. 有標題的容器



按鈕

在 WPF 中,按鈕繼承自 System.Windows.Controls.Primitives.ButtonBase 類別,其中以下幾個控制項都繼承自 ButtonBase:
  1. Button

  2. RepeatButton

  3. ToggleButton

  4. CheckBox

  5. RadioButton
 

Button

WPF 中的 Button 類別只有在 ButtonBase 上增加了「取消按鈕」以及「預設按鈕」兩個機制,這兩個機制在對話方塊中很有用:
  1. 當 Button.IsCancel = true,此按鈕屬於取消按鈕,點選之後會關閉按鈕所屬的視窗並設定 DialogResult = false。

  2. 當 Button.IsDefault = true,此按扭屬於預設按鈕,當按鈕取得焦點時,按下 Enter 就等於按下此按鈕。


RepeatButton

RepeatButton 按著不放會持續觸發 Click 事件,但由於直接繼承 ButtonBase 類別,因此沒有取消以及預設兩種機制存在。

而 RepeatButton 持續觸發 Click 事件的頻率取決於 Delay 以及 Interval 兩個屬性,預設即為 SystemParameters.KeyboardDelay 以及 SystemParameters.KeyboardSpeed

看到以上描述有想起什麼嗎? 沒錯! 就是之前提過的 DependencyProperty(相依屬性) 啊~


ToggleButton

ToggleButton 可視為「會粘住」的按鈕,因此點選以後會保持狀態,第一次點選 IsChecked = true,再一次點選 IsChecked = false。

IsThreeState 屬性為 true,則 IsChecked 的值的變化為 true -> null -> false,對應到事件的觸發則是:
  • IsChecked = true 觸發 Checked 事件

  • IsChecked = false 觸發 Unchecked 事件

  • IsChecked = null 觸發 Indeterminate 事件


CheckBox

CheckBox 的特性相當類似於 ToggleButton,因此他是繼承自 ToggleButton 而來的,只是外觀有點不同罷了!


RadioButton

RadioButton 也是繼承自 ToggleButton,另外還支援了單選功能(同一群組內)。



簡單容器

Label

在 WPF 中的容器控制項,幾乎都可以擺入任意型態的物件,但唯讀 Label 僅能放入文字內容。

但雖然如此,Label 控制項卻可以搭配 Alt 進行快捷鍵的設定喔!

以下程式會在按下 Alt + U 時,焦點會自動跳到 TextBox 上:



ToolTip

ToolTip 是用來顯示提示訊息用,原本都是顯示文字訊息而已,但在 WPF 中,已經可以顯示任何的內容了! 以下有個簡單範例:



另外若是要讓 ToolTip 有更多不同的方式進行呈現時(例如:顯示時間長短),可搭配 ToolTipService 靜態類別來達成!


Frame

Frame 也可以放入任意內容,且可以同時支援 HTML & WPF,透過設定 Source 屬性就可以在 Frame 中顯示連結的內容。



有標題的容器

此部分介紹的控制項(GroupBox、Expander),由於繼承自 System.Windows.Controls.HeaderedContentControl 類別,因此多了一個型態為 Object 的 Header 屬性。而這代表什麼? 代表 Header 可以放進任何你想要放的東西.......


GroupBox

我們通常將 GroupBox 用將多個控制項歸納為相同群組時使用,但由於它是內容控制項,因此只能有一個 child element,因此我們要用其他的容器來當中介:




Expander

Expander 類似 GroupBox,但有個按鈕可以讓使用者開啟 & 收合所包含的內容:



此外,Expander 還定義了 IsExpanded 屬性以及 OnExpanded / OnCollapsed 兩個事件,還可以透過 ExpandDirection 來控制內容展開的方向。

2009年6月17日 星期三

[LINQ] 初探 LINQ to Objects

哪些類別支援 LINQ 相關功能 ?

LINQ to Objects 表示要透過 LINQ 與集合型態的物件進行互動,這些資料存在於 memory 中,但是 .NET Framework 提供的類別如此之多,支援 LINQ 存取的是哪些呢?

答案就是有實作 System.Collections.IEnumerable 介面的類別所產生的物件都可以

何以見得? 由於 .NET Framework 3.0 以後加入了 Extension Method(擴充方法) 這個機制,且又在 System.Linq.Enumerable 類別中針對實作 System.Collections.IEnumerable 介面的類別加入了許多 Extension Method,而這些類別(Array、List、Dictionary .... 等等)則可說是「自動擴充」成擁有 LINQ 的相關功能。



使用範例

首先是 LINQ to Object Array:

using System;
using System.Linq;

class Program
{
static void Main(string[] args)
{
//定義各種不同型態的資料
Object[] array = { "String", 12, true, 'a' };

var types = array.Select(item => item.GetType().Name) //選取集合中資料的型態
.OrderBy(type => type); //根據型態進行排序

/* 執行結果:
Boolean
Char
Int32
String */
ObjectDumper.Write(types);
Console.ReadLine();
}
}



接著是 LINQ to 自行定義的物件(Book) Array:

using System;
using System.Linq;
using LinqInAction.LinqBooks.Common;

class Program
{
static void Main(string[] args)
{
Book[] books = { new Book { Title = "LINQ In Action" },
new Book { Title = "LINQ For Fun" },
new Book { Title = "Extreme LINQ" } };

var titles = books.Where(book => book.Title.Contains("Action")) //取得 Title 欄位包含 "Action" 關鍵字的書籍資料
.Select(book => book.Title); //取得 Title 欄位

/* 執行結果:
LINQ In Action */
ObjectDumper.Write(titles);
Console.ReadLine();
}
}



其實,只要是 Array,存放於 Array 中可以是任何型態的物件(因為 Array 有實作 System.Collections.IEnumerable 介面)


接著改用 List,除了型態不同以外,query 的方法可說是完全相同:

using System;
using System.Collections.Generic;
using System.Linq;
using LinqInAction.LinqBooks.Common;

class Program
{
static void Main(string[] args)
{
//即使資料換成是 List,LINQ 的語法也是相同的!
List books = new List() { new Book { Title = "LINQ In Action" },
new Book { Title = "LINQ For Fun" },
new Book { Title = "Extreme LINQ" } };

var titles = books.Where(book => book.Title.Contains("Action")) //取得 Title 欄位包含 "Action" 關鍵字的書籍資料
.Select(book => book.Title); //取得 Title 欄位

/* 執行結果:
LINQ In Action */
ObjectDumper.Write(titles);
Console.ReadLine();
}
}



這就是 LINQ 想要達成的目標 => 讓 query 的語言一致!
目前嘗試 Array 與 List 的確有這種感覺~


最後是 LINQ to Dictionary,並用兩種不同的方式(query operator & query expression)進行查詢:

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
static void Main(string[] args)
{
Dictionary myNumbers = new Dictionary();
myNumbers.Add(0, "zero");
myNumbers.Add(1, "One");
myNumbers.Add(2, "Two");
myNumbers.Add(3, "Three");
myNumbers.Add(4, "Four");

//透過 query operator 的方式取得偶數值
var evenNumbers01 = myNumbers.Where(number => number.Key % 2 == 0)
.Select(number => number.Value);
ObjectDumper.Write(evenNumbers01);

//也可以透過 query expression 的方式取得偶數值
var evenNumbers02 = from number in myNumbers
where (number.Key % 2) == 0
select number.Value;
ObjectDumper.Write(evenNumbers02);

/*
以上兩個執行結果都會是:
Zero
Two
Four
*/
Console.ReadLine();
}
}


由此可見,由於 Extend Method 機制的出現,幾乎讓所有集合型態的資料都可以透過 LINQ 的方式進行存取了,只要有實作 System.Collections.IEnumerable 介面即可。

除此之外,查詢的方式也不僅限於一種,除了 query operator 外也還有 query expression 可以使用,甚至兩種同時搭配使用亦可喔!

[C#] Deferred Query Execution

什麼是 Deferred Query(延遲查詢) Execution 呢?

這是在看 LINQ In Action 的時候發現的,查詢了一下,原來這是 C# 2.0 就已經提供的功能.....

用途在於可以用更少的資源(Resource)使用集合(collection)物件!

為何這麼說呢? 以下用一段程式碼來說明:


using System;
using System.Linq;

namespace DeferredQueryExecution
{
class Program
{
static double Square(double n)
{
//每呼叫 Square 就會印出下列訊息
Console.WriteLine("Computing Square(" + n + ")....");
return Math.Pow(n, 2);
}

static void Main(string[] args)
{
int[] numbers = { 1, 2, 3 };

//取得 numbers 陣列中所有元素的平方值並存入 query 集合物件中
var query =
from n in numbers
select Square(n);

//印出 query 集合物件的內容
foreach (var n in query)
Console.WriteLine(n);

Console.ReadLine();
}
}
}



這一段程式碼執行完的結果會是如何呢? 一般來說,應該是以下這樣....
Computing Square(1)....
Computing Square(2)....
Computing Square(3)....
1
4
9

但實際跑出來的結果並非這樣,而是:
Computing Square(1)....
1
Computing Square(2)....
4
Computing Square(3)....
9

結果令人出乎意料吧!

從結果可以看出,.NET compile 在處理集合物件時,並非一次給定完整結果,而是「真的有需要才一個一個慢慢給出」,因此當集合物件的數量龐大時,可以有效降低系統資源的浪費,提升在操作集合物件時的效能。

當然,這跟 LINQ 好像沒啥關係......因此只要了解一下 .NET 有這種機制存在即可囉!

2009年6月12日 星期五

[C#] delegate(委派) -> Lambda Expression -> LINQ

觀念解說

關於 delegate & Lambda Expression & LINQ 入門,參考以下文章可以得到很清楚的觀念喔!

Huan-Lin 學習筆記: C# 筆記:重訪委派-從 C# 1.0 到 2.0 到 3.0

Huan-Lin 學習筆記: C# 筆記:從 Lambda 表示式到 LINQ

C# 3.0 極簡風 - Lambda Expression - 黑暗執行緒


接著以下只記錄一些瑣碎的筆記........


LINQ 從何而來?

Lambada Expression 大量了使用了之前所提到的 extension method,而 .NET 中也提供了不少的 extension method 以提昇 Lambada Expression 的易用性,藉此讓 LINQ 的語法更加的直覺!

舉例來說,在 System.Linq.Enumerable class 中,就定義了許多 extension method,例如:OrderByDescendingLastMaxMinContainsCount.... 等等。


除了 delegate & Lambda Expression 之外,與 LINQ 有關的特殊功能還有 Implicit typed local variables、Object and collection initializers、Anonymous types 等等。

而除了 delegate 之外,都是在 C# 3.0 中才有提供的(當然 VB.NET 也有),因此看得出來,LINQ 可說是集合所有新功能於一身的查詢技術。


範例說明

以上範例將上述的新功能簡單做了一次完整的結合測試:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace CompleteCode
{
//為了將 Extension Method 定義在此類別, 要加上 static 關鍵字
//否則就要另外定義一個 static class 來放 Extension Method
static class Program
{
static void Main(string[] args)
{
//在呼叫時指定比對用的 function (使用 Lambda Expression)
DisplayProcesses(process => process.WorkingSet64 >= 20 * 1024 * 1024);

Console.ReadLine();
}

//定義 inner class
class ProcessData
{
public Int32 Id { get; set; }
public Int64 Memory { get; set; }
public string Name { get; set; }
}

//此處以委派(delegate)的方式將 match 函式交由呼叫端決定
//傳入的參數型態為 System.Diagnostics.Process
//回傳型態為 Boolean
static void DisplayProcesses(Func match)
{
//此處變數型態會由 compiler 在編譯時決定
var processes = new List();

foreach (var process in Process.GetProcesses())
{
//使用 Object Initializer 的方式宣告物件並給定值
if (match(process))
processes.Add(new ProcessData { Id = process.Id, Name = process.ProcessName, Memory = process.WorkingSet64 });
}

Console.WriteLine("Total memory: {0} MB", processes.TotalMemory() / 1024 / 1024);

//透過 .NET 內建的 Extension Method,取得耗用記憶體最大的兩個 process 所耗費的記憶體總計
var top2Memory = processes.OrderByDescending(process => process.Memory)
.Take(2)
.Sum(process => process.Memory) / 1024 / 1024;
Console.WriteLine("Memory consumed by the two most processes: {0} MB", top2Memory);

//使用 Anonymous Type 宣告物件並顯示
var results = new { TotalMomery = processes.TotalMemory() / 1024 / 1024,
Top2Memory = top2Memory,
Processes = processes };
ObjectDumper.Write(results, 1);
}

//定義 Extension Method(計算記憶體總使用量)
//針對 IEnumerable 型別進行擴充
static Int64 TotalMemory(this IEnumerable processes)
{
Int64 result = 0;

foreach (var process in processes)
result += process.Memory;

return result;
}
}
}

2009年6月9日 星期二

[C#] 3.0 中的新功能 - 擴充方法(Extension Method)

簡介

擴充方法能將方法「加入」至現有型別,但不需要建立新的衍生型別 (Derived Type)、重新編譯,或是修改原始型別。

神奇的是,不僅是自訂的型別可以增加 extension method,連既有的型別都可以增加 extension method !

隨便舉個例,假設要檢查字串是否為數字,以前都是另外寫個 function 來檢查,例如:
public bool IsNumeric(string strVal);

現在若是透過 extension method,只要透過以下方式即可:
strVal.IsNumeric();

如何? 使用起來是不是更加直覺了呢?

[LINQ ] 初探

簡介

LINQ 是 Microsoft 開發出來專門用來處理集合資訊用的技術,什麼叫做集合資訊呢?

舉凡 Database、XML、Object、DataSet、Array、Collection .... 等都可以算是集合資訊

而我們再開發軟體時,資料來源也就差不多是上面這些,但每一種資料來源都有很多額外的知識必須要學..... Database 是一門學問、XML 又是一門學問 ...

想像一下若是可以用同一套語法來存取以上這些資料來源,不是很迷人的事情嗎?

而 LINQ 就是為了這個目的而產生的!

瞭解一下 LINQ 的目標就可以知道他有多酷了:(from LINQ In Action)
  1. Integrated objects, relational data, and XML (這當然是首要目的)

  2. SQL and XQuery-like power in C# and VB (整合進 C# & VB.NET 囉)

  3. Extensibility model for languages (提供其他語言的擴充性)

  4. Extensibility model for multiple data sources (當然也提供了不同資料來源的擴充性)

  5. Type safety (透過 compile-time 檢查)

  6. Extensive IntelliSense support (enabled by strong-typing) (這對開發者肯定是一大福音啊!)

  7. Debug support (這功能沒有怎麼可以呢?)



使用範例

首先來個使用範例,LINQ to Object !



using System;
using System.Linq;

namespace HelloLinq
{
class Program
{
static void Main(string[] args)
{
//string array
string[] words = { "hello", "wonderful", "linq", "beautiful", "world" };

//取得陣列中字串長度小於等於 5 的字串
var shortwords = from word in words
where word.Length <= 5
select word;
/* hello
linq
world */
foreach (var word in shortwords)
Console.WriteLine(word);


//依照字串長度分群組
var groups = from word in words
orderby word ascending
group word by word.Length into lengthGroups
orderby lengthGroups.Key descending
select new { Length = lengthGroups.Key, Words = lengthGroups };
/*
words of length 9
beautiful
wonderful
words of length 5
hello
world
words of length 4
linq
*/
foreach (var group in groups)
{
Console.WriteLine("words of length " + group.Length);
foreach (string word in group.Words)
Console.WriteLine(" " + word);
}

Console.ReadLine();
}
}
}




看到範例的第一個部分,也許有人會覺得沒有 LINQ 也是很好寫吧!

但第二部份,不是用 LINQ 的話,可就很費工囉!

然而,範例雖然簡單,還是可以看出 LINQ 的語法真的是相當直覺......加上 VS.NET 的 IntelliSense....(難怪 MS 的開發工具這麼多人用.....)


接著以下的範例,可以看出 LINQ 如何以幾乎無縫的方式整合進 C#(VB.NET 也行啦) 中,搭配 XML API 來產生 XML 文件:


using System;
using System.Linq;
using System.Xml.Linq;

namespace HelloLinqToXml
{
class Book
{
public string Publisher;
public string Title;
public int Year;

public Book(string title, string publisher, int year)
{
Title = title;
Publisher = publisher;
Year = year;
}
}

class Program
{
static void Main(string[] args)
{
//定義 object collection
Book[] books = new Book[] {
new Book("Ajax in Action", "Manning", 2005),
new Book("Windows Forms in Action", "Manning", 2006),
new Book("RSS and Atom in Action", "Manning", 2006)
};

//從 object collection 中取得資料後,並搭配 XML API 產生 XML 文件
XElement xml = new XElement("books",
from book in books
where book.Year == 2006
select new XElement("book",
new XAttribute("title", book.Title),
new XElement("publisher", book.Publisher)));

Console.WriteLine(xml);
Console.ReadLine();
}
}
}




最後一個部分則是 LINQ to SQL,此處以 NorthWind 為範例資料庫:


using System;
using System.Linq;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Data.SqlClient;

namespace HelloLinqToSql
{
class Program
{
static void Main(string[] args)
{
//設定資料來源
DataContext db = new DataContext(new SqlConnection("Server=CIC-GODLEON\\SQLEXPRESS;Database=northwind;Trusted_Connection=True;"));

//透過 LINQ 來取得資料
var contacts =
from contact in db.GetTable()
where contact.City == "Paris"
select contact;

foreach (var contact in contacts)
Console.WriteLine("Bonjour " + contact.Name);

Console.ReadLine();

}
}


///
/// 必須透過設定 attribute 的方式指定 class 和 DB table 的 mapping
/// 此處指定對應到 Customers 資料表
///

[Table(Name="Customers")]
class Contact
{
///
/// 同樣內部的設定也必須要透過 attribute 的方式設定 mapping
///


//未指定則對應到同名(CustomerID)欄位
[Column(IsPrimaryKey=true)]
public string CustomerID { get; set; }

//指定對應到 ContactName 欄位
[Column(Name="ContactName")]
public string Name { get; set; }

//對應 City 欄位
[Column]
public string City { get; set; }
}
}





參考資料

2009年6月2日 星期二

[SQL Server] Alias 的用法

今天被問到....

怎麼用 SQL Server Management Studio 去連 default port number 不是 1433 的 SQL Server..

直覺就是到 SQL Server Configuration Manager 去修改 default TCP/IP port number

可是後來想到.....其他設定不就沒辦法動了?

因此後來改用 Alias 來設定

Google 了一下找到這一篇「How to setup and use a SQL Server alias

很容易就完成設定囉!

[Oracle] 如何做到 MySQL 中的 LIMIT 效果

用過 MySQL 的人應該知道

若是要取得 Table A 的第 11~20 筆資料,只要透過 LIMIT 關鍵字就可以輕易取得!

但若是在 Oracle 中想要達成這種效果呢?

只要將 ROWNUM 以及 MINUS 搭配使用就可以囉!

以下有幾個參考連結:

新的開始: Oracle的Rownum的使用

丑角的天空: ORACLE : 列數限制查詢,SQL SELECT ROWNUM

2009年5月20日 星期三

[.NET] 自訂屬性

之前在寫 .NET Web Service 的時候就覺得.......

為什麼在 method 上面加個 [WebMethod] 就可以讓 method 變成 web service 提供給外部呼叫呢?

若是要增加額外的描述,還可以使用 [WebMethod(Description = "xxxxxx")] 的方式來增加

這到底是怎麼做到的呢?

答案就是:Custom Attributes (自訂屬性)

搜尋了一下,在 MSDN 上面看到還算詳細的範例,網址如下:

MSDN >> MSDN Library >> 使用屬性擴充中繼資料 (英文版)


了解以後就可以撰寫自己的 attribute class 來用囉!

PS. 其實 attribute class 就是一般的 class 而已,只不過因為繼承了 System.Attribute 類別,因此有了 .NET Framework 所提供的自訂屬性的效果。

2009年5月5日 星期二

[.NET] C# 的新功能 - Automatic Properties & Object Initializers & Collection Initializers

今天在網路上看到這篇文章.......

才發現原來 C# 有提供這麼酷的功能

Automatic Properties 可以讓 setter 與 getter 的宣告更為簡潔便利(前提是 property 沒有做額外的判斷與處理)

Object Initializers 讓程式開發人員不需要一一指定物件的 property 來進行初始化

Collection Initializers 更是可以一次初始化 collection 中的物件

另外還有一篇文章也很值得參考:C# 3.0自動實作屬性的犀利之處 - 黑暗執行緒

2009年4月30日 星期四

[WPF] 相依屬性(Dependency Property) 進階探討

實作 Dependency Property(相依屬性) 的規則與流程

1、首先必須宣告 Dependency Property

Dependency Property 欄位都是 publicstatic,並且名稱以「Property」作為結尾。

例如以下宣告:
public static readonly DependencyProperty IsDefaultProperty;

2、在 static 建構子中註冊 Dependency Property

由於 Dependency Property 必須呼叫 static 方法 DependencyProperty.Register() 來建立

而在註冊時,必須在 Register() method 中指定以下內容:
  • Dependency Property 的名稱

  • Dependency Property 的型態

  • 包含 Dependency Property 的 Class 型態

  • Metadata (型態為 FrameworkPropertyMetadata,而 Callback Function 宣告於此處)

  • 用來檢查 Property 的值是否合法的 function


3、設定 Getter 與 Setter

Dependency Property 宣告了就是用來設定或取值之用的,當然要設定 Getter 與 Setter 囉!

但比較特別的是,Dependency Property 值的設定或取得,是透過 System.Windows.DependencyObject 中的 SetValue()GetValue() 來處理的。

而比較需要注意的是,若是有任何的程式邏輯或是檢查要加入,請記得加到 Callback Function 中,別加在 Getter 與 Setter 中,因為那只是用來取值或設定值之用。
(所有 WPF 內建的屬性封裝都遵守此規則,因此別亂寫囉!)



使用 Dependency Property 的優點 & 所提供的功能

1、節省記憶體

想像一下,Button 類別有將近 100 個欄位值,每個 Label 也將近有 90 個欄位值,若是畫面中有一堆 Button 與 Label 所產生的物件,記憶體的耗用量豈不是很可觀?

沒錯,這就是 Dependency Property 所要解決的問題,透過 static 的方式先產生 Dependency Property,在將 Button 與 Label 物件的屬性「依附上去」。

當每個 Button 或是 Label 都依附到同一個 Dependency Property,如此一來,記憶體的用量就會大大地減少囉!


2、變更告知 (Change Notification)

WPF 可以根據 Dependency Property 的 Metadata 自動觸法許多動作,例如:重新整理元素、更新畫面配置、重新整理資料繫結...等等。

範例:以下程式實作滑鼠移到按鈕上後,更改文字顏色(Button 的 IsMouseOver 屬性被更改,自動觸發更改文字顏色)

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="About WPF Unleashed" SizeToContent="WidthAndHeight" Background="OrangeRed" FontSize="30" FontStyle="Italic">





Chapter 1
Chapter 2








You have seccessfully registered this product.





3、屬性值繼承 (Property Value Inheritance)

元素樹中,父節點的控制項屬性會向下套用到子系的控制項屬性。

但也是有些例外的情況,原因可能會有兩種:
  1. 在 DependencyProperty.Register() 裡,透過 FrameworkPropertyMetadata.Options.Inherits 設定為不繼承父系控制項的屬性值。

  2. 子系控制項覆寫了繼承來的屬性值。


4、支援多重提供者

WPF 中有許多屬性提供者可用不同的方式設定 Dependency Property,而屬性值的決定過程會經過五個步驟:
  1. 判斷基底值

    這個部分會由多種屬性提供者來提供值,優先順序為:

    Local Value -> Style Trigger -> Template Trigger -> Style Setter -> Theme Style Trigger -> Theme Style Setters -> Property Value Inheritance -> Default Value

  2. 驗算

    此部份進行屬性值的驗算,為運算式。

  3. 套用動畫

    若有一個以上的動畫正在執行,它們可以改變目前的屬性值,甚至完全置換掉。

  4. 轉換

    若有註冊 CoerceValueCallback 委派,則屬性值會被傳遞到此 function 進行轉換,以取得更新後的值。
    (例如 ProgressBar 就是利用 callback function 來保持屬性值不會超過最大或最小值)

  5. 驗證

    最後還會進行屬性值的驗證,決定該值是否合法。(若有設定 ValidateValueCallback 委派)


5、附加屬性 (Attached Property)


附加屬性(attached property)是種特殊的 Dependency Property,可以附加到任何物件上,以下直接用範例說明:

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="About WPF Unleashed" Background="OrangeRed">





Chapter 1
Chapter 2








You have seccessfully registered this product




從程式碼可以看出,透過附加屬性的使用,StackPanel 中的控制項就不用一一加上 FontSize & FontStyle 兩個屬性了。


參考資料

Anita 的.NET 世界: 什麼是Dependency Property

design studio: Dependency Property

WPF Tutorial | Introduction to DependencyProperties

2009年4月21日 星期二

[.NET] SqlConnectionStringBuilder & SQL Express

想必應該有人使用 SqlConnectionStringBuilder 來產生 SQL 字串吧!

一般連到 SQL Server 時,在 DataSource 的部分都是指定 server 的 domain name 或是 IP address

但若是連到 SQL Express 呢??

以下以 localhost 為例,應該要輸入:
DataSource = "(local)\SQLEXPRESS";
如此一來就會產生正確的資料庫連線字串了~

2009年4月16日 星期四

[Windows] 使用 .reg 檔案新增 & 刪除登錄檔機碼

今天剛好遇到這個需求(使用 .reg 檔案新增 & 刪除登錄檔機碼),所以在網路上找到了蠻不錯的參考資料

如何使用登錄檔(.reg)進行新增、修改或刪除登錄機碼和值

利用登錄檔來刪除機碼 - 教學文章 - 卡拉娛樂網 論壇|娛樂|影音|分享|下載


新增比較容易,而刪除的話可以開啟登錄檔編輯程式(開始 -> 執行 -> regedit),將要刪除的機碼匯出,並進行編輯,在每個機碼前面加上「-」即可。

2009年4月7日 星期二

[Oracle] 如何使用流水號

比起 MS SQL Server 或是 MySQL,Oracle 使用流水號可以說是相當麻煩阿.......

怎麼說呢?

因為 int 欄位無法直接透過遞增的方式產生流水號,而是必須使用 sequence 搭配 trigger 來進行

因此整個過程必須分為三個步驟:
  1. 建立 table

  2. 建立 sequence,用來取得新的遞增值

  3. 建立 trigger,在新的資料存入 table 時,將新的遞增值存入 table 中

以下直接用範例來說明:


-- 建立 sequence 作流水號之用
CREATE SEQUENCE seq_user_group
INCREMENT BY 1
START WITH 1
ORDER;


-- 組別
CREATE TABLE user_group (
group_id NUMBER NOT NULL, -- 群組 ID
group_name NVARCHAR2(100), -- 組別名稱
remark NVARCHAR2(200) -- 備註
parent_group_id NUMBER, --父組別 ID

CONSTRAINT pk_user_group PRIMARY KEY(group_id)
);
COMMENT ON TABLE user_group is '組別';
COMMENT ON COLUMN user_group.group_id is '群組 ID';
COMMENT ON COLUMN user_group.group_name is '組別名稱';
COMMENT ON COLUMN user_group.remark is '備註';


-- 建立 trigger 以產生遞增欄位 (for user_group)
CREATE TRIGGER tri_auto_increment_user_group
BEFORE INSERT ON user_group
FOR EACH ROW
BEGIN
SELECT seq_user_group.NEXTVAL INTO :NEW.group_id FROM DUAL;
END;

[WPF] Routed 輸入事件

Routed Event

假設 element tree 如下:

Window
|
Grid
|
---------------------------
| | |
Button TextBlock ScrollViewer
| |
TextBlock StackPanel
|
TextBlock


而 Routed Event Handler 大概長以下這樣:
void functionName(object sender, RoutedEventArgs args)
{ ....(事件處理)... }
其中參數的部份有幾個重點:
  • sender:此物件將 event 送到 application 中

  • args.Source:真正導致 event 發生的 element


若是在 element tree 中左下角的 TextBlock 按一下滑鼠右鍵,分別會產生出以下的 event:
PreviewMouseDown -> MouseDown
其中 sender 的部份,觸發 event 順序如下:
  • PreviewMouseDown:Window -> Grid -> Button -> TextBlock

  • MouseDown:TextBlock -> Button -> Grid -> Window

因此從上面可以知道,PreviewMouseDown 是屬於 tunneling eventMouseDown 屬於 bubbling event

而透過 Event Routing 的特性,若是要讓較上層的 element 先處理 mouse down 的動作,可以撰寫在 PreviewMouseDown 中。



Mouse Event

MouseDown 與 MouseUp 不一定會同時出現

例如:在視窗中按下按鈕,會發生 MouseDown event,但如果持續按著不放,再離開視窗的範圍後再放開滑鼠,不會出現 MouseUp event。

因此,若要解決此問題,可以透過在 UIElementContentElement 中所定義的 CaptureMouse 方法來解決。

當呼叫了 CaptureMouse 方法後,即使滑鼠離開視窗,視窗也會持續的接收 MouseMove 與 MouseUp 事件。

最後當 MouseUp 事件處理完成後,可以呼叫 ReleaseMouseCapture 或是設定 Mouse.Capture 為 null 來釋放所捕捉的滑鼠。



Keyboard Event

在 WPF 中,keyboard event 都會下放到 TextBox、RichTextBox、ScrollViewer 這一類的控制項中作處理。

需要注意的是,只有當 element 或是控制項具有鍵盤焦點時,才會是 keyboard event 的來源。

此外,keyboard event 亦屬於 routed event,因此具有焦點的 element 的所有上層 element,都可以參與 keyboard event,且其 IsKeyboardFocusWithin 屬性都會是 true。

當 element 獲得 or 失去焦點時,會觸發 GotKeyboardFocusLostKeyboardfocus 事件,也都是 routed event;此外,由於兩個事件屬於 tunneling event,因此在 element tree 中上方的 element 可以先察覺到焦點的改變。

而 Keyboard 的輸入包含了三種 event,發生順序如下:
  1. KeyDown (參數型態為 KeyEventArgs)

  2. TextInput (參數型態為 TextCompositionEventArgs)

  3. KeyUp (參數型態為 KeyEventArgs)

但比較需要注意的是,若按下的是「Shift」、方向鍵、或是其他功能鍵,並不會產生 TextInput 事件。舉例來說,或先按下 Shift 再按 A,一共會觸發兩次 KeyDown 事件、一次 TextInput 事件、兩次 KeyUp 事件。

2009年4月1日 星期三

[WPF] 相依屬性(Dependency Property) 初探

在傳統的 Window Form 中的設計中,每個控制項都各自擁有自己的 property,而這些 property 大多都是相同的(例如:FontSize),因此不僅浪費記憶體來儲存這些 property 的值,有時在程式中還需要撰寫多餘程式來設定這些 property 的值。

因此,在 WPF 中提出了Dependency Property,用來解決各控制項中重複的 property 過多因此造成浪費記憶體的現象。

以下用範例程式來說明:(節錄自 Application = Code + Markup 一書)

SpaceButton.cs

using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace CH08.SetSpaceProperty
{
public class SpaceButton : Button
{
//傳統 .NET 做法:私有欄位搭配公開 property
string _txt;

//透過 get & set 設定 property 的處理方式
public string Text
{
set
{
_txt = value;
this.Content = SpaceOutText(_txt);
}
get { return _txt; }
}

//Dependecy Property 的宣告
public static readonly DependencyProperty SpaceProperty;
public int Space
{
//不同與以往 property 的設定方式
//Dependency Property 必須透過 DependencyObject 中的 SetValue() 與 GetValue() 進行 value 的處理
//*** SetValue() 與 GetValue() 皆為 static method ***
set { SetValue(SpaceProperty, value); }
get { return (int)GetValue(SpaceProperty); }
}

//處理 Dependency Property 必須使用 static constructor
static SpaceButton()
{
//設定 metadata (可以想像成是 Dependency Property 的相關特性)
FrameworkPropertyMetadata meta = new FrameworkPropertyMetadata();
meta.DefaultValue = 1;
meta.AffectsMeasure = true;
meta.Inherits = true;
meta.PropertyChangedCallback += OnSpacePropertyChanged;

//註冊 DependecyProperty
SpaceProperty = DependencyProperty.Register("Space", typeof(int), typeof(SpaceButton), meta, ValidateSpaceValue);
}

//value 檢驗時呼叫方法
static bool ValidateSpaceValue(object obj)
{
int i = (int)obj;
return i >= 0;
}


//當 property 改變時,呼叫此方法
static void OnSpacePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
SpaceButton btn = obj as SpaceButton;
btn.Content = btn.SpaceOutText(btn._txt);
}

//在 Button Content 中每個字元間塞指定數量(Space)的空白
string SpaceOutText(string str)
{
if (str == null)
return null;

StringBuilder build = new StringBuilder();
foreach (char ch in str)
build.Append(ch + new string(' ', Space));

return build.ToString();
}
}
}



SpaceWindow.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace CH08.SetSpaceProperty
{
public class SpaceWindow : Window
{
//設定 Dependency Property
public static readonly DependencyProperty SpaceProperty;

public int Space
{
//同樣的,這邊也要透過 DependencyObject 中的 SetValue() 與 GetValue() 進行 value 的處理
set { SetValue(SpaceProperty, value); }
get { return (int)GetValue(SpaceProperty); }
}

//處理 Dependency Property 必須使用 static constructor
static SpaceWindow()
{
//設定 metadata
FrameworkPropertyMetadata meta = new FrameworkPropertyMetadata();
meta.Inherits = true;

//由於 SpaceProperty 在 SpaceButton 中已經宣告過
//因此這邊直接將 SpaceProperty 指到 SpaceButton 中的 SpaceProperty
//只要透過 AddOwner() 將自己也設定為該 Dependency Property 的擁有者即可
SpaceProperty = SpaceButton.SpaceProperty.AddOwner(typeof(SpaceWindow));

//唯一需要注意的是 metadata 並不會一同被採用
//因此需要另外的宣告與設定,並 override
SpaceProperty.OverrideMetadata(typeof(SpaceWindow), meta);
}
}
}



SetSpaceProperty.cs

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace CH08.SetSpaceProperty
{
public class SetSpaceProperty : SpaceWindow
{
[STAThread]
public static void Main()
{
Application app = new Application();
app.Run(new SetSpaceProperty());
}

public SetSpaceProperty()
{
this.Title = "Set Space Property";
this.SizeToContent = SizeToContent.WidthAndHeight;
this.ResizeMode = ResizeMode.CanMinimize;

int[] iSpaces = { 0, 1, 2 };
Grid grid = new Grid();
this.Content = grid;

//設定 Grid 的 RowDefinition
for (int i = 0; i < 2; i++)
{
RowDefinition row = new RowDefinition();
row.Height = GridLength.Auto;
grid.RowDefinitions.Add(row);
}

//設定 Grid 的 ColumnDefinition
for (int i = 0; i < iSpaces.Length; i++)
{
ColumnDefinition col = new ColumnDefinition();
col.Width = GridLength.Auto;
grid.ColumnDefinitions.Add(col);
}

for (int i = 0; i < iSpaces.Length; i++)
{
SpaceButton btn = new SpaceButton();
btn.Text = "Set window Space to " + iSpaces[i];
btn.Tag = iSpaces[i];
btn.HorizontalAlignment = HorizontalAlignment.Center;
btn.VerticalAlignment = VerticalAlignment.Center;
btn.Click += WindowPropertyOnClick;
grid.Children.Add(btn);
Grid.SetRow(btn, 0);
Grid.SetColumn(btn, i);

btn = new SpaceButton();
btn.Text = "Set button Space to " + iSpaces[i];
btn.Tag = iSpaces[i];
btn.HorizontalAlignment = HorizontalAlignment.Center;
btn.VerticalAlignment = VerticalAlignment.Center;
btn.Click += ButtonPropertyOnClick;
grid.Children.Add(btn);
Grid.SetRow(btn, 1);
Grid.SetColumn(btn, i);
}
}

//更改 Window 中的 Dependency Property
void WindowPropertyOnClick(object sender, RoutedEventArgs args)
{
SpaceButton btn = args.Source as SpaceButton;

//修改 Window 的 Space Dependency Property
//而此修改會影響所有在 SpaceWindow 中且擁有 SpaceProperty 屬性的控制項
//唯獨已經設定 Space 值的 SpaceButton 控制項例外
Space = (int)btn.Tag;
}

//更改 Button 中的 Dependency Property
void ButtonPropertyOnClick(object sender, RoutedEventArgs args)
{
SpaceButton btn = args.Source as SpaceButton;

//僅單獨修改 SpaceButton 中的 Space 值
//不會影響到 SpaceWindow 中其他擁有 SpaceProperty 的控制項
btn.Space = (int)btn.Tag;
}
}
}



參考資料

[WPF] Layout

Panel

ContentControl 中衍生出來的 class 都有 Content 屬性,幾乎可以被設定為任何物件,但問題在於「Content 只能設定一個物件」。

然而一個視窗中怎麼可能只放一個物件呢? 因此在 WPF 中使用了 Panel 作為解決此問題的方式,而 Panel 的階層與衍生類別如下:

UIElement

每種不同的 panel 有其自訂的 layout model,例如:

StackPanel 用水平或垂直 stack 的方式安排子元素

WrapPanel 類似 StackPanel,但可以將自動將元素擺到下一個 columm 或是 row 的位置

DockPanel 則會根據 parent container 的內部邊界來進行自動擺設

Grid 則是把所有子元素以格子狀的方式擺設

UniformGrid 則是所有的行都等寬,列都等高

Canvas 則是最貼近原本的做法,每個元素都有自己的座標位置


Dock

重點就在於 dock 的位置了,分別是 Top、Bottom、Left、Right 四種,如果將控制項的 HorizontalAlignment 設定為 Center,整個版面會變得很奇怪,因此在撰寫上要注意一下。

element 的定位必須使用 DockPanel.SetDock() 來處理。


Grid

蠻龜毛的一個 layout panel,必須要先定義 RowDefinition 以及 ColumnDefinition 並加入到 Grid 中,才能將控制項加入到 Grid 中。

而控制項加入時還要透過 Grid.SetRow() 以及 Grid.SetColumn() 來設定控制項在 Grid 中的位置。

element 的定位必須使用 Grid.SetRow()Grid.SetColumn()Grid.SetRowSpan()Grid.SetColumnSpan() 來處理。


Canvas


element 的定位必須使用 Canvas.SetLeft()Canvas.SetTop()Canvas.SetRight()Canvas.SetBottom() 等方法來處理。


事件處理

若將 event 處理指定給特定的控制項,例如:

Button.Click += ButtonOnClick;

void ButtonOnClick(object sender, RoutedEventArgs args)
{
MessageBox.Show("Hello");
}


當然,也可以把 event 透過 this.AddHandler 註冊給 Window 物件(包含 Button),但是就要在 function 中透過 args.Source 來判斷控制項的型態了,設定方式如下:

this.AddHandler(Button.ClickEvent, new RoutedEventHandler(ButtonOnClick));


既然可以將 event 註冊給 Window 物件,當然也可以註冊給 Panel 物件囉!

2009年3月12日 星期四

[C#] 在 Oracle 上一次執行多個 SQL 命令

一般在 ADO.NET 的批次處理部份,都是在 DataSet 進行離線的處理完後,透過 SqlDataAdapter(或是 OracleDataAdapter) 搭配 SqlCommandBuilder(或是 OracleCommandBuilder) 來做!

若是對同一個 table 進行大量的 insert,也可以改用 bind parameter array 的方式進行批次新增

但如果要處理的多個指令都不太一樣呢? 有 update、insert、delete 這時要怎麼辦?

每個 SQL 都要重新產生 connection 物件或是 command 物件真是成本太大了。因此,若是使用的資料庫為 Oracle (MS SQL Server 不確定能不能這樣做),可以將所有的 SQL 語法組合成以下的樣子:
BEGIN

第一個 SQL 命令;

第二個 SQL 命令;

第三個 SQL 命令;

.......

.......

END;

最後在 END 之後也是要補一個分號。

當所有 SQL 語法組合而成上面的格式後,就可以透過一個 command 與一個 connection 一次將 SQL 命令丟給資料庫去作囉!


參考資料

MSDN Library >> Performing Batch Operations Using DataAdapters (ADO.NET) (中文版)

ADO.Net實現Oracle大批量數據的更新優化 - 中國自學編程網

oracle 一次執行多個sql的問題 - 白虎的地盤 - CSDNBlog

2009年2月25日 星期三

資料庫連線字串大全

在網路上偶然看到以下這個網站:

ConnectionStrings.com - Forgot that connection string? Get it here!

大概想的到的資料庫都在裡面了......(當然,想不到的也有一大堆)

以後忘記連線字串怎麼寫就可以連進去參考囉!

當然,也可以透過 ConnectionStringBuilder 類別來產生......

2009年2月20日 星期五

[JavaScript] expando 屬性的定義與使用

最近在讀 Learning Jquery 時,看見 JavaScript expando,是個蠻有意思的東西,可以自行擴充 JavaScript object 的屬性。

而由於這些屬性是放置於 memory 中,因此使用起來速度較快,不過也要記得釋放,免得出現 memory leak 的問題。

在網路上查詢到以下的範例(內容有稍作修改),說明 JavaScript expando 的使用方式:
<html>
<head>
<script type="text/javascript">
<!--
var myObj = new Object();
alert(myObj.toString()); //出現 "[object Object]"

//設定 expando 屬性
myObj.myExpando = "Hello Expando";
myObj.toString = function(){ //override toString() method
return this.myExpando;
}
alert(myObj.toString()); //出現 "Hello Expando"

//設定 expando 屬性
myObj.reportTime = function(){
alert(new Date());
}
myObj.reportTime(); //出現目前的日期與時間

//刪除 expando 屬性
delete myObj.reportTime;

//再一次嘗試呼叫已經刪除的 expando 屬性
try {
myObj.reportTime();
} catch(e) {
alert(e.message); //出現 "myObj.reportTime is not a function"
}
-->
</script>
</head>
<body>
</body>
</html>



參考資料

2009年2月16日 星期一

[SQL Server] 減少 Transaction Log 所佔的硬碟容量

今天同事在問,之前也沒做過,所以上網找了一下資料,發現以下兩個不錯的連結說明:

Brad's MIS Note: 交易紀錄檔 Transaction Log 管理

數據庫日常維護(參考)

2009年2月12日 星期四

[jQuery] jQuery + AJAX

AJAX

AJAX,這個技術是透過 JavaScript 中的 XMLHttpRequest 物件去呼叫後端 server code 並動態改變前端的網頁,讓網頁不需要 refresh。

AJAX 的技術並不複雜,大概掌握幾個重點即可:
  1. XMLHttpRequest 物件將資料傳遞到後端 server 的方式,可分為 POST GET

  2. 從 server 回傳的資料可以是 plain text 或是 XML

  3. 前端網頁 DOM 的處理........(這其實跟 AJAX 已經沒有太大關係了)



jQuery 與 AJAX 的搭配


直接寫 pure XMLHttpRequest 的程式碼真的太累了,光是 cross browser 的問題就可以搞死人了......不過還好 jQuery 都幫大家處理好啦!

以下用的範例程式來說明:(demo)

ajax01.html
<html>
<head>
<script type="text/javascript" src="js/jquery-1.3.1.js"></script>
<script type="text/javascript">
<!--
$(document).ready(function(){
//取得 button 物件並註冊 click 事件
$("#btnAJAX").click(function(){
//取得使用者輸入的值
var intValue = $("#txtValue").val();

/* 以 POST 的方式送出 request
傳入參數 : value
參數值 = intValue
callback function 為 function(result){...} */

$.post("server01.php", {value : intValue}, function(result){
$("#spnMain").html("server 端回傳結果 = " + result);
});
});
});
-->
</script>
</head>
<body>
請輸入整數值,server 會計算其平方值並回傳:<p />
<input type="text" id="txtValue" /> <input type="button" id="btnAJAX" value="從 server 端取得結果" /><p />
<span id="spnMain"></span>
</body>
</html>

server01.php
<?php
if(isset($_POST["value"]))
echo $_POST["value"] * $_POST["value"];
else
echo "0";
?>

從上面的範例可以看出,透過 jQuery 物件的 post() 方法就可以直接享受到 AJAX 的好處囉! 當然,也可以用 get() 的方式來達到 AJAX 的效果。

此外,callback function 的部份比較需要注意的就是參數的傳遞了,在 jQuery 中可以允許使用一般的方式或是 context 的方式傳入參數,以下用範例說明:(demo)

ajax02.html
<html>
<head>
<script type="text/javascript" src="js/jquery-1.3.1.js"></script>
<script type="text/javascript">
<!--
//測試函式
function testFunc(intVal) {
alert("final value is " + (intVal * 2 + 10));
} //end function


$(document).ready(function(){
//取得 button 物件並註冊 click 事件
$("#btnAJAX").click(function(){
//取得使用者輸入的值
var intValue = $("#txtValue").val();

/* 以 POST 的方式送出 request
傳入參數 : value
參數值 = intValue
callback function 為 function(result){...} */

$.post("server.php", {value : intValue}, function(result){
//直接呼叫 function
testFunc(result);

//使用 jQuery context 的方式呼叫 function
testFunc.apply(this, [result]);
});
});
});
-->
</script>
</head>
<body>
請輸入整數值,server 會計算其平方值並回傳:<p />
<input type="text" id="txtValue" /> <input type="button" id="btnAJAX" value="從 server 端取得結果" /><p />
<span id="spnMain"></span>
</body>
</html>


參考資料