有網友留言詢問如何在Excel將資料寫入多個工作表,
我們可以使用foreach的方式取得每個工作表,
或是利用Worksheets[index]的方式指定要存取那一張工作表。

Workbook在新增,預設會有三張工作表。
如果要新增工作表,使用Worksheets.Add方法新增。

底下是範例程式。

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

using System.IO;
using System.Reflection;
using Excel = Microsoft.Office.Interop.Excel;

namespace ExcelMultiSheet
{
    class Program
    {
        static void Main(string[] args)
        {
            GenerateExcel(5);
        }

        ///<summary>
        /// 產生多個工作表的Excel
        /// 工作表的數量一定要大於三,目前沒檢查
        /// </summary>
        /// 工作表數量
        static void GenerateExcel(int numberOfSheet)
        {
            string fileName = @"D:\MultiSheet.XLSX";

            Excel.Application oXL = new Excel.Application();
            Excel.Workbook oWB;
            Excel.Worksheet oSheet;

            oXL.Visible = false;
            oXL.UserControl = false;

            oWB = oXL.Workbooks.Add(Missing.Value);
            //目前工作表的數量
            int currentNumOfSheet = oWB.Worksheets.Count;
            //補足工作表
            for (; currentNumOfSheet < numberOfSheet; currentNumOfSheet++)
                oWB.Worksheets.Add(Missing.Value, Missing.Value, Missing.Value, Missing.Value);

            //對於每個工作表寫入一行測試資料
            foreach (Excel.Worksheet wSheet in oWB.Worksheets)
                wSheet.Cells[1, 1] =  wSheet.Name + " This is a test line before changing the sheet name. ";

            //使用Worksheets[index]的方式寫入第二行資料
            for (int counter = 1; counter <= currentNumOfSheet; counter++)
            {
                oSheet = oWB.Worksheets[counter];
                oSheet.Name = "Sheet " + counter;
                oSheet.Cells[2, 1] = "A test line after changing the sheet name. ";
            }

            //存檔
            oWB.SaveAs(fileName, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing,
                Excel.XlSaveAsAccessMode.xlShared, Type.Missing, Type.Missing, Type.Missing, Type.Missing, Type.Missing);

            oWB.Close();
            oWB = null;

            oXL.Quit();
            oXL = null;
        }
    }
}

以上,提供參考。

使用Report Viewer呈現報表時,
會發現螢幕上顯示的結果和列印成品不相同。

例如螢幕顯示報表沒有分頁,看起來只有一頁,
但印出來的報表卻有兩頁。

主要的原因是Report Viewer預設的報表呈現方式並不是「預覽列印」的樣式。
這裡記錄設定預覽的方式,如下程式碼。

//省略宣告部份
//Report Viewer元件名稱為reportViewer1

//設定Report Viewer的連線方式及細節
reportViewer1.ProcessingMode = Microsoft.Reporting.WinForms.ProcessingMode.Remote;
//....略過細節

//設定報表顯示方式 
// DisplayMode.Normal 是原本的預設值
// DisplayMode.PrintLayout 是我要的預覽效果 [1]
//以預覽方式呈現
reportViewer1.SetDisplayMode(DisplayMode.PrintLayout);

//設定顯示報表時,頁面大小 [2]
//這裡是設定為75%
reportViewer1.ZoomMode = ZoomMode.Percent;
reportViewer1.ZoomPercent = 75;

this.reportViewer1.RefreshReport();

參考資料
1. http://msdn.microsoft.com/en-us/library/microsoft.reporting.winforms.displaymode(v=VS.90).aspx
2. http://msdn.microsoft.com/en-US/library/microsoft.reporting.winforms.reportviewer_members(v=VS.90).aspx

如果是需要由資料庫取得特定日期,
同時要求特定格式,可以使用CONVERT搭配GETDATE。[1]
轉換的格式請直接看範例程式。

--先看GETDATE給的日期樣式
SELECT GETDATE()
--結果如下
--2011-11-01 17:54:24.157

--需求是取得年-月-日
--不要時:分:秒
--102 -> ANSI格式
SELECT CONVERT(NVARCHAR, GETDATE(), 102)
--結果如下
--2011.11.01

--110 US格式
SELECT CONVERT(NVARCHAR, GETDATE(), 110)
--結果如下
--11-01-2011

--112 ISO格式 只有數字 年月日
SELECT CONVERT(NVARCHAR, GETDATE(), 112)
--結果如下
--20111101

原則上是以CONVERT(資料型態, 字串, 轉換樣式)將日期做格式化。
以上,做為筆記。

參考資料
1. http://msdn.microsoft.com/en-us/library/ms187928.aspx

利用程式將資料寫入Excel已經不是第一次了,
如果有時間,應該把這個功能寫成可重復使用的類別。
底下是記錄使用Microsoft.Office.Interop.Excel的過程。

繼續閱讀 »

LTRIM和RTRIM分別可以去掉資料左邊和右邊的空白字元,
如下述的範例,因為已知firstName和lastName可能右邊有空白字元,
所以使用RTRIM把空白字元去掉。

繼續閱讀 »

需要把DateTime.Now輸出特定格式,
將用到的東西記錄一下。

日期輸出的格式文件,非常詳細。

http://msdn.microsoft.com/en-us/library/8kb3ddd4.aspx

可以針對年月日和時間的輸出格式做設定。
簡單說明帶入的參數設定,

yyyy = 四位數的西元年,
M (大寫) = 一或兩位數的月份,
MM (大寫) = 兩位數的月份,1 ~ 9月的數字自動補零,
MMM (大寫) = 月份的英文簡稱 (例 Jan),這邊是以CultureInfo為en-US為預設值,
MMMM (大寫) = 月份的英文全名 (例 October),這邊是以CultureInfo為en-US為預設值,

d = 一或兩位數的日,
dd = 兩位數的日,1~9的天數自動補零,
ddd = 星期幾的英文簡稱(例 Mon),這邊是以CultureInfo為en-US為預設值,
dddd = 星期幾的英文全名,這邊是以CultureInfo為en-US為預設值。

參數字串可以使用自訂的分隔字元,
例如「/」、「-」。

以下是使用的範例,
在中文的環境中,如果沒有指定CultureInfo(『en-US』),
則會抓取系統語系值。

using System.Globalization;

DateTime dtObj = DateTime.Now;

//以 月份 日, 年 的格式輸出
string outputDate = dtObj.ToString("MMMM dd, yyyy", new CultureInfo("en-US"));
//結果為 March 11, 2010

//也可以直接使用帶參數ToString()
//沒有指定CultureInfo
string paraDate = DateTime.Now.ToString("MMM dd, yyyy");
//結果為 十月 01, 2011

//指定CultureInfo
string paraCulDate = DateTime.Now.ToString("MMM dd, yyyy", CultureInfo.CreateSpecificCulture("en-US"));
//結果為 Oct 02, 2011

//變換分隔字元
outputDate = DateTime.Now.ToString("dd-MM-yyyy");
//結果為 02-10-2011

outputDate = DateTime.Now.ToString("dd/MM/yyyy");
//結果為 02/10/2011

編輯記錄:
第一版:2010.03.16
第二版:2011.10.02

在使用t-sql的LEN function時,發現了一個有趣的現象。
如下所示,

DECLARE @str1 NVARCHAR(20), @str2 NVARCHAR(20)
SET @str1 = '123456789   '
SET @str2 = '  123456789  '

SELECT LEN(@str1) --結果為9
SELECT LEN(@str2) --結果為11

找了文件(http://msdn.microsoft.com/en-us/library/ms190329.aspx)
之後發現,LEN在計算字元數目時,會將尾端的空白去掉。

但我需要知道是整個字串包含空白的字元數,
於是找到了一個名為DATALENGTH的函式。

http://msdn.microsoft.com/en-us/library/ms173486(v=SQL.90).aspx

在使用的時候需要注意,因為它是回傳BYTES的數目,
所以如果字元是UNICODE,需要把結果除以2。

DECLARE @str1 NVARCHAR(20), @str2 NVARCHAR(20)
SET @str1 = '123456789   '
SET @str2 = '  123456789  '

SELECT DATALENGTH(@str1) --結果為24
SELECT DATALENGTH(@str2) --結果為26

SELECT DATALENGTH(@str1) / 2 --結果為12
SELECT DATALENGTH(@str2) / 2 --結果為13

以上,做為參考。

在寫Windows Form的時候,需要一個背景程式從特定網址(HTTP)下載某些檔案。
透過同事了解到可以使用WebClient類別,有點出乎意料之外的簡潔。

以下測試程式做為記錄。

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

using System.Net;
using System.IO;

//namespace 和 class 宣告略過

static void Main(string[] args)
{
       //設定目標檔案為Yahoo的logo
       string yahooImage = @"http://l.yimg.com/f/i/tw/hp/mh/09purple.gif";
       string localImage = @"C:\Yahoo.gif";

       WebClient client = new WebClient();
       //直接使用DownloadFile方法下載檔案
       client.DownloadFile(yahooImage, localImage);

       //設定目標檔案為道奇MLB頁面
       string laMlbPage = @"http://losangeles.dodgers.mlb.com/index.jsp?c_id=la";
       string localLaMlbPage = @"C:\page.txt";
       //設定存在本地端的檔案為C:\page.txt
       FileStream fs = new FileStream(localLaMlbPage, FileMode.Create, FileAccess.Write);
       StreamWriter sw = new StreamWriter(fs);

       //使用DownloadData讀取,此方法會回傳byte陣列
       byte[] dataBuffer = client.DownloadData(laMlbPage);
       //寫入本地端檔案
       sw.Write(Encoding.UTF8.GetString(dataBuffer));
       sw.Close();
       fs.Close();
}

遇到需要使用到四摠五入的情況,
在MSDN的說明中找到適用的方法。

主要是使用Math.Round和MidpointRounding參數,
用法是Math.Round(要處理的數字, 傳回數字的小數點位數, 四捨五入的方式)
程式碼如下,

double d1 = 80.12345;
double d2 = 80.567;

//小數點後兩位四捨五入
d1 = Math.Round(d1, 2, MidpointRounding.AwayFromZero);
// => 80.12

//小數點後兩位四捨五入
d2 = Math.Round(d2, 2, MidpointRounding.AwayFromZero);
// => 80.57

MidpointRounding.AwayFromZero是我們一般熟知的四捨五入方式,
如果需要符合 IEEE Standard 754 ,可以使用MidpointRounding.ToEven。

以上,做為記錄。

在.NET 3.5使用Dictionary時,遇到了一個問題。
程式碼如下,

//定義Dictionary的內容
Dictionary<string, int> dict = new Dictionary<string, int>()
{
       {"first", 1}, {"second", 2}, {"third", 3},
       {"fourth", 4}, {"fifth", 5}
};

//把Dictionary內,所有的值改為數字0
foreach (KeyValuePair<string, int> kvp in dict)
       dict[kvp.Key] = 0;

上面這套程式會出現以下的錯誤訊息,
InvalidOperationException 集合已修改; 列舉作業可能尚未執行。

查看MSDN的說明後後發現,使用foreach走訪Dictionary時,只能讀取,不能改變內容。

The foreach statement is a wrapper around the enumerator, which allows only reading from the collection, not writing to it.

於是把程式碼改寫如下,
先將Dictionary的key全部取出,再用for loop修改Dictionary的內容。

Dictionary<string, int> dict = new Dictionary<string, int>()
{
      {"first", 1}, {"second", 2}, {"third", 3},
      {"fourth", 4}, {"fifth", 5}
};

//把Dictionary的所有key放到陣列中
string[] keyArr = new string[dict.Keys.Count];

int counter = 0;
foreach (string s in dict.Keys)
{
      keyArr[counter] = s;
      counter++;
}

//利用陣列改變Dictionary的內容
for (counter = 0; counter < keyArr.Length; counter++)
{
      dict[keyArr[counter]] = 0;
      System.Console.WriteLine("Key = " + keyArr[counter] + " Value = " + dict[keyArr[counter]].ToString());
]

以上,做為記錄。

後一頁 »

Follow

Get every new post delivered to your Inbox.