2010年12月28日 星期二

在C#中讀取Excel的試算表

取出試算表的名稱後,就可以針對單一試算表進行讀取

public List LoadExcelSheet(
string excelFile, string sheetName)
{
List dataList = new List();

/// load excel file as an ole database
OleDbConnection excelDatabase =
new OleDbConnection(string.Format(
"Provider = Microsoft.Jet.OLEDB.4.0;" +
"Data Source = {0};" +
"Extended Properties='Excel 8.0;HDR=No'",
excelFile));
excelDatabase.Open();

/// query sheet from excel file
OleDbDataAdapter sheetAdapter =
new OleDbDataAdapter(string.Format(
"SELECT * FROM [{0}]", sheetName),
excelDatabase);
DataTable fileData = new DataTable();
sheetAdapter.Fill(fileData);

/// scan all cells in the sheet
foreach (DataRow row in fileData.Rows)
{
foreach (object item in row.ItemArray)
{
if (!(item is DBNull))
{
dataList.Add(item.ToString());
}
}
}

/// close and release database connection
excelDatabase.Close();

return dataList;
}

這裡值得注意的是,如果試算表中有些欄位是空的
讀出來的資料會是DBNull的型別,其餘則會是string的型別

另外,如果是手動輸入試算表名稱的話
要記得在試算表名稱後面加一個$,例如預設的"Sheet1$"

--
參考資料
轉錄 C# 讀取 Excel
C# 讀取 Excel
DataTable 類別

在C#中讀取Excel的試算表名稱

在將Excel的檔案當做資料庫的存取的時候
可以先取得檔案中每個試算表的名稱,再做處理

public List LoadExcelSheetNameList(
string excelFile)
{
List sheetNameList = new List();

/// load excel file as an ole database
OleDbConnection excelDatabase =
new OleDbConnection(string.Format(
"Provider = Microsoft.Jet.OLEDB.4.0;" +
"Data Source = {0};" +
"Extended Properties='Excel 8.0;HDR=No'",
excelFile));
excelDatabase.Open();

/// load excel sheet as DataRow
DataRow[] sheetList =
excelDatabase.GetSchema("Tables").Select();
foreach (DataRow sheet in sheetList)
{
/// query each sheet name from excel file
sheetNameList.Add(
sheet["TABLE_NAME"] as string);
}

/// close and release database connection
excelDatabase.Close();

return sheetNameList;
}

在得到名稱後,就可以針對單一的試算表進行讀取

--
參考資料
[工作] 取得Excel頁籤名稱
如何取得 Excel 中的 Sheet Name

2010年12月27日 星期一

在C#中使用SQLite資料庫

自己開發程式的時候,想用資料庫又不希望花一大筆錢
常常會使用SQLite,一個輕量級的嵌入式資料庫系統(僅單一檔案)
由於.net原生並不支援SQLite
可以去System.Data.SQLite找到在公開的函式庫
注意如果是x64的程式,則需要使用x64版本的函式庫,無法混用

在每次使用資料庫的時候,都需要建立與相關資料庫的連結

private SQLiteConnection ConnectDatabase()
{
string path = "Test.db";
string password = "Password";
SQLiteConnection connection;

if (!File.Exists(_databasePath))
{
/// for non-exist database, create it
SQLiteConnection.CreateFile(path);
connection = new SQLiteConnection(
"Data Source = " + path);
connection.Open();
connection.ChangePassword(password);
}
else
{
/// for exist database, connect it
connection = new SQLiteConnection(
"Data Source = " + path);
connection.SetPassword(password);
connection.Open();
}

return connection;
}

這邊要注意的是,由於SQLite本身並沒有支援資料庫加密
有關Password的功能(紅字部分)是System.Data.SQLite實做的
所以製作出來的加密資料庫並不能被其他的SQLite應用讀取
(例如Firefox的附加元件SQLite Manager)
所以如果不打算加密,建議將紅字部分拿掉

在連結之後,就可以透過該連結,執行SQL的命令

private void RunCommand(
SQLiteConnection connection, string commandText)
{
SQLiteCommand command =
new SQLiteCommand(connection);
command.CommandText = commandText;
command.ExecuteNonQuery();
}

除了不需要回傳值的命令(例如CREATE TABLE)以外
SQL最重要的當然是Query的指令

private List RunQueryCommand(
SQLiteConnection connection, string commandText)
{
List<string> result = new List<string>();

SQLiteCommand command =
new SQLiteCommand(connection);
command.CommandText = commandText;

using (SQLiteDataReader queryResult =
command.ExecuteReader())
{
/// read one row one time
while (queryResult.Read())
{
/// change index to get different column
result.Add(queryResult.GetValue(0)
.ToString());
}
}

return result;
}

當然,在使用完畢後一定要記得將連結中止,否則資料庫檔案會被鎖住

private void DisconnectDatabase(
SQLiteConnection connection)
{
connection.Close();
}

另外,讀取資料庫的時候,如果不能肯定資料表是否存在
則需要先做一個檢查,否則會跳出例外狀況
(當然也是可以直接用try catch將Command包住,端看習慣)

private bool IsTableExist(
SQLiteConnection connection, string name)
{
return connection.GetSchema("Tables")
.Select("Table_Name = '" + name + "'")
.Length > 0;
}

--
參考資料
SQLite 簡介
System.Data.SQLite
C sharp or .Net 使用sqlite 設定
SQLite Manager
SQL語法教學
Check if table exists

Windows的顯示/隱藏桌面

在Windows XP預設的快速啟動列中會有一個捷徑,可以顯示/隱藏桌面
(隱藏/顯示所有已經開啟的視窗)
在XP的時候有時候會不小心砍掉(Win7中已經被放在視窗的右下角,反正也移除不掉,就不會有這個困擾),這時候可以自己重新做一個捷徑

先開一個純文字檔,內容如下

[Shell]
Command=2
IconFile=explorer.exe,3
[Taskbar]
Command=ToggleDesktop

並將檔案附檔名改為.scf(檔名倒是無所謂,一般習慣設成"ShowDesktop.scf"或"顯示桌面.scf")就可以了

如果想要在程式中做出同樣的功能,則可以呼叫Shell.Application去呼叫ToggleDesktop這個命令

void ShowDesktop()
{
Type shell =
Type.GetTypeFromProgID("Shell.Application");
shell.InvokeMember("ToggleDesktop",
BindingFlags.InvokeMethod, null,
Activator.CreateInstance(shell), null);
}

--
參考資料
How to re-create the Show desktop icon on the Quick Launch toolbar in Windows XP
C#使用系統的「顯示桌面」功能(Shell.Application)

2010年12月21日 星期二

在WPF中發出鍵盤事件

WPF沒有控制鍵盤的API
所以要發出鍵盤事件(例如按下"J")就必須要呼叫Win32的API了

private enum KeyEventFlag
{
KEYEVENTF_KEYDOWN = 0x0000,
KEYEVENTF_EXTENDEDKEY = 0x0001,
KEYEVENTF_KEYUP = 0x0002,
}

[DllImport("user32.dll", SetLastError = true)]
private static extern void keybd_event(
byte bVk, byte bScan, KeyEventFlag dwFlags,
IntPtr dwExtraInfo);

這時發生一個問題,bVk是以Win32中定義的Virtual-Key Codes做為對照
但是在WPF中所採用的是Key 列舉型別
需要使用KeyInterop做轉換,所以實際上發出按下鍵盤的函式如下

private void SendKeyDown(Key key)
{
keybd_event(
(byte)KeyInterop.VirtualKeyFromKey(key), 0,
KeyEventFlag.KEYEVENTF_KEYDOWN, IntPtr.Zero);
}

另外,如果要送出放開鍵盤的事件,那就把KEYEVENTF_KEYDOWN換成KEYEVENTF_KEYUP,而連續送出一個按下加上一個放開,就變成Click了
同理,要發出復合鍵則是再放開前按下所有鍵,再放開即可

--
參考資料
.NET - How can I convert 'System.Windows.Input.Key' to 'System.Windows.Forms.Keys'?