顯示具有 C# 標籤的文章。 顯示所有文章
顯示具有 C# 標籤的文章。 顯示所有文章

2010年9月16日 星期四

[C#]-利用clipboard轉換office內容物為圖片

最近接了一個case,
在office上選取物件利用ctrl+c複製物件到clipboard內,
並在picturebox控制項上利用ctrl+v將物件從clipboard取出,
並以圖片形式顯示在picturebox上,
最後將picturebox上的image存成圖片檔。

首先先記錄一下如何判斷鍵盤的組合鍵,如下:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
    switch (e.KeyData)
    {
        case (Keys.Control | Keys.V):
            // do something 
            break;
    }
}

接下來的重點是從clipboard內取出物件時必須知道它支援那些格式,
我們可以利用下面的方式知道該物件所支援的格式有那些:
foreach (string format in Clipboard.GetDataObject().GetFormats())
{
    // do something
}
也有一些簡便的方式,例如Clipboard.ContainsText()或Clipboard.ContainsImage()等可以判斷。

再來我們準備一些method來從clipboard中取出office中大部份常見的格式。
Bitmap格式的物件,我們都能用下面的方式取得image:
public void CatchBitmap()
{
    this.pictureBox1.Image = (Bitmap)Clipboard.GetImage();
}

這裡遇到比較麻煩的困難點是EnhancedMetafile格式,
必須要用win32裡的API去取得系統的clipboard來使用,方式如下:
public const uint CF_METAFILEPICT = 3;
public const uint CF_ENHMETAFILE = 14;

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern bool OpenClipboard(IntPtr hWndNewOwner);

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern bool CloseClipboard();

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetClipboardData(uint format);

[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern bool IsClipboardFormatAvailable(uint format);

public void CatchEMF()
{
    if (OpenClipboard(this.Handle)) {
        if (IsClipboardFormatAvailable(CF_ENHMETAFILE))
        {
            IntPtr ptr = GetClipboardData(CF_ENHMETAFILE);
            if (!ptr.Equals(new IntPtr(0)))
            {
                Metafile metafile = new Metafile(ptr, true);
                //Set the Image Property of PictureBox 
                this.pictureBox1.Image = metafile;
            }
        }
        CloseClipboard();
    }
}

再來如果copy放進clipboard內的是純文字物件的話,
就必須要用Graphics來drawing string才能產生圖檔,如下:
public void DrawString()
{
    String sImageText = Clipboard.GetText();
    Bitmap objBmpImage = new Bitmap(1, 1);

    int intWidth = 0;
    int intHeight = 0;

    // Create the Font object for the image text drawing.
    Font objFont = 
        new Font("Arial", 20, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Pixel);

    // Create a graphics object to measure the text's width and height.
    Graphics objGraphics = Graphics.FromImage(objBmpImage);

    // This is where the bitmap size is determined.
    intWidth = (int)objGraphics.MeasureString(sImageText, objFont).Width;
    intHeight = (int)objGraphics.MeasureString(sImageText, objFont).Height;

    // Create the bmpImage again with the correct size for the text and font.
    objBmpImage = new Bitmap(objBmpImage, new Size(intWidth, intHeight));

    // Add the colors to the new bitmap.
    objGraphics = Graphics.FromImage(objBmpImage);

    // Set Background color
    objGraphics.Clear(Color.White);
    objGraphics.SmoothingMode = SmoothingMode.AntiAlias;
    objGraphics.TextRenderingHint = TextRenderingHint.AntiAlias;
    objGraphics.DrawString(sImageText, objFont, 
        new SolidBrush(Color.FromArgb(102, 102, 102)), 0, 0);
    objGraphics.Flush();
    this.pictureBox1.Image = objBmpImage;
}

接下來下面要講解的功能是如何把在excel內選取的cells自動轉換成對應的chart圖,
基本上複製的cells放進clipboard本身就支援Bitmap格式,
但直接取用會直接像剪貼那樣,如果要轉換成對應的chart圖則必須再行判斷,
這裡是利用Microsoft.Office.Interop.Excel來轉換,範例碼如下:
public void Convert()
{
    IDataObject iData = Clipboard.GetDataObject();
    String cellArea = (String)iData.GetData(DataFormats.Text);
    String[] rows = cellArea.Split(new Char[] { '\n' });

    Excel.Application xlApp;
    Excel.Workbook xlWorkBook;
    Excel.Worksheet xlWorkSheet;
    object misValue = System.Reflection.Missing.Value;

    xlApp = new Excel.ApplicationClass();
    xlWorkBook = xlApp.Workbooks.Add(misValue);
    xlWorkSheet = (Excel.Worksheet)xlWorkBook.Worksheets.get_Item(1);

    int rowLength = rows.Length;
    int rowCount = 1;
    int cellCount = 1;
    int maxCellCount = 1;
    
    foreach (String line in rows)
    {
        if (rowCount == rowLength)
        {
            break;
        }
        String[] cells = line.Split(null);
        int cellLength = cells.Length;
        foreach (String cell in cells)
        {
            if (cellCount == cellLength)
            {
                break;
            }
            if (cellCount > maxCellCount)
            {
                maxCellCount++;
            }
            xlWorkSheet.Cells[rowCount, cellCount] = cell;
            cellCount++;
        }
        rowCount++;
        cellCount = 1;
    }

    Excel.Range chartRange;

    Excel.ChartObjects xlCharts = (Excel.ChartObjects)xlWorkSheet.ChartObjects(Type.Missing);
    Excel.ChartObject myChart = (Excel.ChartObject)xlCharts.Add(10, 80, 300, 250);
    Excel.Chart chartPage = myChart.Chart;

    rowCount--;
    maxCellCount--;
    String endCorner = map.ElementAt(maxCellCount).Value;
    endCorner = endCorner + rowCount.ToString();
    
    // endCorner的格式例如:c7,就是cells右下角的範圍邊界點
    chartRange = xlWorkSheet.get_Range("A1", endCorner);
    chartPage.SetSourceData(chartRange, misValue);
    // 設定繪製的chart圖型式
    chartPage.ChartType = Excel.XlChartType.xlColumnClustered;
    chartPage.CopyPicture(Excel.XlPictureAppearance.xlScreen,
                          Excel.XlCopyPictureFormat.xlBitmap,
                          Excel.XlPictureAppearance.xlScreen);
    xlWorkBook.Close(false, false, misValue);
}
產生的chart圖會利用copy放進clipboard內。

使用畫面如下,紅色框部份就是被選取的cells,當在picturebox上按下ctrl+v時,
就會利用office元件繪製chart圖:

最後就是整理一些條件判斷式,來處理當遇到什樣格式時要執行那一個method比較恰當:
// 一般的圖檔
if (this.formatList.Contains("Bitmap") && !this.formatList.Contains("Text"))
{
    CatchBitmap();
}
// excel的cells欄位集合
else if (this.formatList.Contains("Bitmap") && this.formatList.Contains("Text"))
{
    // if want to create chart
    Convert();
                
    // offic 2003
    CatchBitmap();
                   
    // offic 2007
    CatchEMF();
}
// 純粹選取文字
else if (!this.formatList.Contains("Bitmap") && this.formatList.Contains("Text"))
{
    DrawString();
}
// excel2003的chart,只有emf格式,最後再處理
else if (this.formatList.Contains("EnhancedMetafile"))
{
    CatchEMF();
}

2010年9月3日 星期五

[C#]-利用Microsoft.Office.Interop.Outlook讀取outlook msg檔

若要開發 Microsoft Office Outlook 的增益集,您可以與 Outlook 物件模型提供的物件進行互動。此 Outlook 物件模型會提供表示使用者介面中各種項目的類別。例如,Microsoft.Office.Interop.Outlook.Application 類別是表示整個應用程式、Microsoft.Office.Interop.Outlook.MAPIFolder 類別是表示含有電子郵件訊息或其他項目的資料夾,而 Microsoft.Office.Interop.Outlook.MailItem 類別則是表示電子郵件訊息。

Outlook 會提供許多您可以與之互動的類別。下列各節將簡述某些最上層類別,並描述它們彼此的互動關係。這些類別包括:

  • Microsoft.Office.Interop.Outlook.Application
  • Microsoft.Office.Interop.Outlook.Explorer
  • Microsoft.Office.Interop.Outlook.Inspector
  • Microsoft.Office.Interop.Outlook.MAPIFolder
  • Microsoft.Office.Interop.Outlook.MailItem
  • Microsoft.Office.Interop.Outlook.AppointmentItem
  • Microsoft.Office.Interop.Outlook.TaskItem
  • Microsoft.Office.Interop.Outlook.ContactItem

下面是範例程式:
首先在專案參考內先匯入Microsoft.Office.Interop.Outlook
接著是取出msg檔內各欄位的方式
Microsoft.Office.Interop.Outlook.Application app = null;
Microsoft.Office.Interop.Outlook.MailItem item = null;

DialogResult msgFileSelectResult = this.openFileDialog.ShowDialog();
if (msgFileSelectResult == DialogResult.OK)
{
     string msgfile = this.openFileDialog.FileName
     app = new Microsoft.Office.Interop.Outlook.Application();
     item = app.Session.OpenSharedItem(msgfile) as Microsoft.Office.Interop.Outlook.MailItem;
     this.label1.Text = item.Subject;
     this.label2.Text = item.SenderName;
     this.label3.Text = item.To;
     this.label4.Text = item.Body;           
}

2010年7月10日 星期六

[C#]-讀取excel檔案寫入MS SQL Server

最近寫案子有用到C#去讀取excel檔案再寫入MS SQL Server,
這是滿常用到的功能所以在這裡記錄一下。

首先是利用oledb讀取excel檔,再利用OleDbDataAdapter將資料填入DataSet。
OleDbDataAdapter 是 DataSet 和資料來源之間的橋接器,用來擷取和儲存資料。OleDbDataAdapter 會提供這個橋接器,方法是使用 Fill 從資料來源將資料載入 DataSet,並使用 Update 將 DataSet 中所做的變更傳送至資料來源。

OLE DB簡介
OLE DB 是一種以 COM 為基礎、用來存取資料的應用程式發展介面 (Application Programming Interface,API)。OLE DB 可存取以任何格式所儲存的資料 (資料庫、試算表、文字檔等等),只要該格式能夠使用 OLE DB 提供者 (Provider)。每個 OLE DB 提供者都會公開特定資料來源類型 (例如 SQL Server 資料庫、Microsoft Access 資料庫或 Microsoft Excel 試算表) 的資料。

常用的Provider列表
Database                Provider
----------------------------------------------------
Ms SQL server       Provider=SQLOLEDB
Oracle                   Provider=MSDAORA
Ms Access 2003    Provider=Microsoft.jet.OLEDB.4.0
Ms Access 2007    Provider=Microsoft.ACE.OLEDB.12.0
Ms Excel 2003       Provider=Microsoft.jet.OLEDB.4.0
Ms Excel 2007       Provider=Microsoft.ACE.OLEDB.12.0
IBM DB2                Provider=DB2OLEDB

private void button1_Click(object sender, EventArgs e)
{
  // Show the dialog and get result.
  DialogResult result = openFileDialog1.ShowDialog(); 
  if (result == DialogResult.OK) // Test result.
  {
    // get excel file name
    string fileName = openFileDialog1.FileName;
    textBox1.Text = fileName;
    button2.Enabled = true;
    // import excel into datagridview
    string excelConn = "Provider = Microsoft.Jet.OLEDB.4.0 ; 
           Data Source = " + textBox1.Text + ";
           Extended Properties = 'Excel 8.0;HDR=YES'";
    string strExcelSelect = "SELECT * FROM [Sheet1$]";
    OleDbDataAdapter adapter = 
          new OleDbDataAdapter(strExcelSelect, excelConn);
    DataSet dataSet = new DataSet();
    adapter.Fill(dataSet, "ExcelInfo");
    dataGridView1.DataSource = dataSet.Tables["ExcelInfo"].DefaultView;
  }
}
再來是將資料從datagridview中匯入MS SQL Server
雖然 .NET沒有類似JAVA的preparedstatement,但有類似的應用IDbDataParameter。
private void button2_Click(object sender, EventArgs e)
{
  string strDBInsert = "INSERT INTO customers VALUES
       (@name,@hPhone,@oPhone,@addr,@delivery,@midwife,@source)";
  string strConn = "Data Source = 6A-783500-NB\\SQLEXPRESS;
                   Integrated Security = True";
  SqlConnection sqlConn = new SqlConnection(strConn);
  sqlConn.Open();
  SqlCommand sqlCmd = new SqlCommand(strDBInsert,sqlConn);
  progressBar1.Step = 100/dataGridView1.Rows.Count;
  for (int i = 0; i < dataGridView1.Rows.Count; i++)
  {
    try
    {
      sqlCmd.Parameters.Clear();
      sqlCmd.Parameters.AddWithValue
          ("@name", Convert.ToString(dataGridView1[0, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@hPhone", Convert.ToString(dataGridView1[1, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@oPhone", Convert.ToString(dataGridView1[2, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@addr", Convert.ToString(dataGridView1[3, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@delivery", Convert.ToString(dataGridView1[4, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@midwife", Convert.ToString(dataGridView1[5, i].Value));
      sqlCmd.Parameters.AddWithValue
          ("@source", Convert.ToString(dataGridView1[6, i].Value));
      sqlCmd.ExecuteNonQuery();
      progressBar1.PerformStep();
      }
    catch (Exception e1)
    {
      continue;
    }
    dataGridView1.Rows[i].DefaultCellStyle.BackColor =
                                          Color.CornflowerBlue;  
  }
  progressBar1.Value = progressBar1.Maximum;
  sqlConn.Close();
}