[C#] DataGridView DoubleBuffered

這次再度碰到DataGridView的效能問題
之前雖然實作了Virtual Mode
但是以DataTable為資料來源而實作

而我這次使用Entity Framework + DataGridViewCells
要再套回去Virtual Mode顯得有些麻煩
所以就再度尋找其他解決辦法

最後找到了設定DoubleBuffered的方法
參考資料:

  1. MSDN: Control.DoubleBuffered Property
  2. C# DataGridView DoubleBuffered Property
  3. Set DoubleBuffered Property [C#]

原理我就不贅述了
直接參考第二個網址
Form.cs 的底下加入 Static Class

using System.Reflection;

public static class ExtensionMethods
{
    public static void DoubleBuffered(this DataGridView dgv, bool setting)
    {
        Type dgvType = dgv.GetType();
        PropertyInfo pi = dgvType.GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
        pi.SetValue(dgv, setting, null);
    }
}

然後記得執行DataGridView.DoubleBuffered(true);

搞定!
可以開始享受滾動順暢的DataGridView了。

 

廣告

[C#] Embed Form In TabControl

Progress Flow

  • Add new Form
    1. Project → Add → New Item → Windows Form
    2. Set FormBorderStyle to None
    3. Add a Panel and Dock in Form
    4. Add your Controls, and set Anchor
  • Add TabControl
    1. Select Main Form
    2. Add TabControl
    3. Remove all Tabpages
  • Add Button (Add Page)
    1. Set Event (Click)
        private void btnAdd_Click(object sender, EventArgs e)
        {
            TabPage tp = new TabPage();
            
            Form1 frm = new Form1();
            frm.TopLevel = false;

            tabControl1.TabPages.Add(tp);
            tp.Controls.Add(frm);
            frm.Show();
            frm.WindowState = FormWindowState.Maximized;
    }
  • Issues about Size Changed
        private void Main_SizeChanged(object sender, EventArgs e)
        {
            ResizeTab();
        }

        private void tabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            ResizeTab();
        }

        private void ResizeTab()
        {
            if (tabControl1.TabPages.Count > 0)
            {
                TabPage tp = tabControl1.SelectedTab;
                if (tp.Controls.Count > 0)
                {
                    ((Form)tp.Controls[0]).WindowState = FormWindowState.Normal;
                    ((Form)tp.Controls[0]).WindowState = FormWindowState.Maximized;
                }
            }
    }
  • Demo

2016-3-24 下午 04-31-08.png

 

[CI] Jenkins (六) – PMD_CPD

除了上一篇的CodeCoverage的部分
我也實作以下項目

  • 檢查.NET Framework規範 (FxCop)
  • 分析程式複雜度及區塊深度 (Source Monitor)
  • 偵測重複程式碼 (PMD_CMD)

前兩項請參考:
[料理佳餚] Jenkins 增加 FxCop Plugin
[料理佳餚] Jenkins 增加 SourceMonitor Plugin


偵測重複程式碼的部分
我首先嘗試了Simian
請參考:[料理佳餚] Jenkins 增加 Simian Plugin

不過使用結果會跑出Error: Big 5
猜測是因為我的程式中
部分MessageBox有顯示中文
或是註解有中文,導致Simian無法使用
因此我改用PMDCPD


首先下載PMD:官方網址
放到CI Server上
然後新增建置動作
CPD

執行cpd.bat
  –files:輸入欲分析的SourceCode資料夾位置
  –language:程式語言代號,使用C#則輸入cs
  –format:輸出格式,選擇xml
  >duplicatecode.xml: 輸出的檔案名稱
  || exit 0:加這一小段,避免Jenkins誤認為執行失敗

其他指令請參考:Finding duplicated code


最後要輸出結果
於Jenkins安裝DRY Plug-in

然後新增建置後動作→Publish duplicate code analysis results
CPD2


輸出Report的範例結果
CPD3.png

 

 

 

[CI] Jenkins (五) – Open Cover

這裡承接上一篇[CI] Jenkins (四) – 自動化整合測試
進行Open Cover,程式覆蓋率(Code Coverage)的實作


VS Test的建置
參考:[料理佳餚] 使用 Jenkins 執行自動化單元測試

測試覆蓋率的部分,我另外使用Open Cover來完成
參考:Jenkins Code Coverage and .Net


首先到官方網址進行下載
Releases: OpenCover

新增建置動作
寫入批次指令,如下圖
OpenCover1.png
執行OpenCover.Console.exe
-target:輸入 vstest.console.exe的位置
-targetargs:輸入 測試專案dll的位置
-output:輸出 xml的位置


下一步再把OpenCover的結果xml轉換成html
使用ReportGenerator,如下圖
OpenCover2.png

執行ReportGenerator.exe
-reports:輸入 OpenCover.xml 的位置
-targetDir:輸出資料夾


最後新增建置後動作

OpenCover3.png


完成漂亮的Code Coverage結果報表

OpenCover_Result


下一篇:[CI] Jenkins (六) – PMD_CPD

 

 

[CI] Jenkins (四) – 自動化整合測試

這篇講述我在自動化整合測試所遇到的問題

我進行的是自動化整合測試(Integration Test)
因此不像單元測試(Unit Test),僅驗證方法邏輯
而是需要實際與其他Module一起驗證TestCase的正確性

以下是我所要測試到的Module

  1. Winform
  2. Entity Framework (MS SQL)
  3. Excel 

然後我參考前輩們的文章
使用了三種測試方式

  1. MS Test
  2. VS Test 
  3. SpecRun

參考資料:
小風 – CI Server 06 – 加入單元測試 (MS Test)
軟體主廚 – [料理佳餚] 使用 Jenkins 執行自動化單元測試 (VS Test)
軟體主廚 – [料理佳餚] Jenkins 整合 SpecFlow 執行自動化整合測試


 

首先是1.Winform
在MS Test會產生The application is not running in UserInteractive的失敗
VSTest及SpecRun則可以正常測試

再來是2.Entity Framework (MS SQL)
在MS Test會因為找不到Application config file而無法連線
VSTest及SpecRun則可以正常測試

最後是3.Excel
在Visual Studio中使用VS Test Console都能正常執行
不過在Jenkins中用以上三種測試方法皆會失敗
(因為記憶體或磁碟空間不足,Microsoft Excel 無法再開啟或儲存任何文件。 )

猜測是Jenkins的VM空間不足
所以有嘗試解決Builds failing with OutOfMemoryErrors
不過目前仍然尚未成功
持續尋找解決辦法中

下一篇:[CI] Jenkins (五) – Open Cover

 

[CI] Jenkins (三) – 自動化建置

這篇將繼續參考軟體主廚 – [料理佳餚] 使用 Jenkins 執行自動化建置


接下來要新增自動化建置
選擇首頁→"新增作業"→"建置 Free-Style 軟體專案"

2016-3-7 下午 02-42-01.png

選擇"原始碼管理"→"Git"
輸入Repository的網路位址或本地位置
登入方式則在Credentials設定 (ID & Password, or SSH Key)
2016-3-7 下午 02-47-15.png


再來是觸發程序
我選擇“輪巡SCM"
讓Jenkins自己定時查看Repo有沒有新的Commit進來
排程的寫法,可以參考旁邊的問號
這邊我使用“每小時 hourly"

2016-3-7 下午 03-15-40.png

由於選擇了“輪巡SCM"
前面Git的設定記得要“Additional Behaviours"→"Force polling using workspace"
2016-3-7 下午 03-19-00.png


最後終於要選擇建置步驟了
“新增建置步驟"→"Build a Visual Studio project or solution using MSBuild"
選擇剛剛設定的Version,以及Build File的相對路徑

2016-3-7 下午 03-23-06

儲存後,馬上建置
接下來就揭曉成果
成功會是藍燈,失敗則是紅燈
Build.png

我們點進這次的建置#1
點擊Console Output
看看發生了什麼事
Build-2.png

查看下圖落落長的Log
找到了原因
由於我的範例程式有使用Entity Framework
但Jenkins卻找不到相對應的參考(Reference)

以下是可以使用的解決辦法

  1. 把Source Code的Packages中所需的dll也列入Git Commit
  2. 將需要的dll放入Jenkins會自動尋找的位置

解決Reference的問題後
再次建置
就可以看到成功的藍色燈號囉!

 

下一篇:[CI] Jenkins (四) – 自動化整合測試

[CI] Jenkins (二) – Plugin (使用Git及MS Build)

這篇將安裝Jenkins的Plugin(使用Git及MS Build)
我是參考軟體主廚 – [料理佳餚] 使用 Jenkins 執行自動化建置


順帶一提版本控制
我是使用Git + TortoiseGit (參考:TortoiseGIT入門簡介 )
Server端使用GitLab做出Web介面的呈現 (參考:GitLab使用心得)


回到Jenkins
開始安裝Git及MS Build的外掛程式(Plugin)
首頁→"管理 Jenkins"→"管理外掛程式"→"可用的"
選擇安裝

  1. Git plugin
  2. MSBuild Plugin

安裝後,可以到"管理 Jenkins"→設定全域安全性"
依照自己的需求去設定

然後再到"管理 Jenkins"→設定系統"
(2016.09.05更新,或在"管理 Jenkins"→“Global Tool Configuration")

選定Git及MS Build的路徑
(我有先安裝好Git及Visual Studio 2015)

範例:
2016-3-7 下午 02-33-36.png

到這邊很快速地完成Plugin的安裝與設定


2016.09.30更新:
若要編譯.Net Framework 4.0以上的專案
建議改用MS Build V14 參考連結


下一篇:[CI] Jenkins (三) – 新增作業(自動化建置)

[CI] Jenkins (一) – 簡易安裝

這篇將開始進行Jenkins的使用心得介紹

關於JenkinsCI 持續整合系統 (Continuous integration)的概念
我是從以下前輩們的文章所學習

工具安裝與實際操作
則是參考


首先進行Jenkins的安裝

進入官網:https://jenkins-ci.org/
看到帥氣的管家圖示

在OS的選擇上
我會使用到Visual Studio的許多套件
因此選擇使用Windows系統安裝
2016-3-7 上午 11-29-59.png

安裝後,會自動導向localhost:8080
如果無法順利開啟,可以嘗試以下方法

  1. 重啟Jenkins (系統管理工具→服務→Jenkins)
  2. 確認預設Port:8080是否被占用
    參考:[問題排除] 80PORT被佔用? 如何查出佔用PORT的方法
  3. 更改使用Port
    參考:瓶水相逢 – 艾小克 – 02. 安裝 Jenkins 很簡單

看到以下畫面,就成功了第一步囉!
1.png

下一篇:[CI] Jenkins (二) – Plugin (使用Git及MS Build)

 

 

[C#] 初探Entity Framework (七) – Update, Delete

這篇介紹UpdateDelete
方法其實和Insert大同小異

首先介紹Update,原始Table如下圖
2016-3-4 下午 06-02-33.png

目標嘗試把EmployeeID大於5的人們,將他的City改成Taipei

SQL語法如下
UPDATE Northwind.dbo.Employees
SET City = ‘Taipei’
WHERE EmployeeID > 5

使用EF的範例Code如下

        private void btn_Update_Click(object sender, EventArgs e)
        {
            IQueryable<Employees> result = ef.Employees.Where(x => x.EmployeeID > 5);

            foreach (Employees em in result)
            {
                em.City = "Taipei";
            }
            ef.SaveChanges();
        }

成功的結果如下圖
2016-3-4 下午 06-08-23.png


 

接下來是Delete,就試試把City不是Taipei的人們刪除吧!
範例Code如下

private void btnDelete_Click(object sender, EventArgs e)
        {
            IQueryable<Employees> result = ef.Employees.Where(x => x.City != "Taipei");

            foreach (Employees em in result)
            {
                ef.Employees.Remove(em);
            }
            ef.SaveChanges();
            MessageBox.Show("Finish");
        }

成功結果
2016-3-4 下午 06-14-56

另外這邊做個提醒
如果即將刪除的項目,有被關聯到其他Table作為外來鍵
可能會產生無法刪除的狀況
這邊為了測試方便,有先把Norwind.dbo.Employee的關聯性先拿掉

若想直接連帶刪除的話
可以參考這篇文章
ADO.Net Entity Framework : (十六) 關聯式資料 – 刪除


下一篇:[C#] 初探Entity Framework (八) – Transaction

[C#] 初探Entity Framework (六) – Insert, Bulk Insert

這篇介紹Insert的方法

下圖是範例使用的目標Table: Employees
2016-3-2 下午 05-00-42.png
接著我要Insert “Chris Jheng" 到Employees之中

private void btnInsert_Click(object sender, EventArgs e)
        {
            Employees newOne = new Employees();
            newOne.LastName = "Chris";
            newOne.FirstName = "Jheng";

            ef.Employees.Add(newOne);
            ef.SaveChanges();

            MessageBox.Show("Finish");
        }

成功的結果如下圖
2016-3-2 下午 05-12-56


接下來是關於BulkInsert的問題
由於Entity Framework並沒有MS SQL的DataTable BulkCopy功能
因此必須一個一個Insert進去
而在這樣的狀況下,大量Insert時的效能就很差
範例Code如下

private void btnBulkInsert_Click(object sender, EventArgs e)
        {
            for (int i = 0; i < 2000; i++)
            {
                Employees newOne = new Employees();
                newOne.LastName = "Last_" + i.ToString();
                newOne.FirstName = "First_" + i.ToString();

                ef.Employees.Add(newOne);               
            }

            ef.SaveChanges();

            MessageBox.Show("Finish");
        }

測試Insert 2000筆資料,花了大約30秒,效能分析如下圖
2016-3-2 下午 05-41-15.png
效能分析的使用方法,可以參考這篇Performance Analysis
從上圖可以看到效能瓶頸卡在DetectChanges
再查一下Code可以找到原因,是由於每一次Add新物件,EF都會自動DetectChanges
2016-3-2 下午 05-46-01.png

我們把這個功能關掉看看
加入這行:ef.Configuration.AutoDetectChangesEnabled = false;

        private void btnBulkInsert_Click(object sender, EventArgs e)
        {
            ef.Configuration.AutoDetectChangesEnabled = false;

            for (int i = 0; i < 2000; i++)
            {
                Employees newOne = new Employees();
                newOne.LastName = "Last_" + i.ToString();
                newOne.FirstName = "First_" + i.ToString();

                ef.Employees.Add(newOne);               
            }

            ef.SaveChanges();

            MessageBox.Show("Finish");
        }

結果如下,這次花費約12秒完成,節省一半以上的時間。

2016-3-2 下午 05-49-51.png


小提醒
雖然這個方法可以稍微改良Bulk Insert的效能問題
但和MS SQL的BulkCopy功能相比,速度還是相差太多
因此可以考慮使用其他套件來解決此問題
例如:EntityFramework.BulkInsert
或者是使用組合字串的方式,也可以暫時解決此問題

 

下一篇:[C#] 初探Entity Framework (七) – Update, Delete