星期五, 3月 25, 2016

[C#] OfType()、Cast()

之前在撰寫 WinForm 篩選控件時,很自然都會使用 OfType() 來做,一直都是把它當成 WHERE 來看待,閱讀 極意之道次世代 .NET Framework 3.5資料庫開發聖典 才發現到其內部運作模式
  • OfType() 和 Cast() 都是針對集合元素來進行轉型
  • OfType:利用 [as] 運算子進行轉型,轉型失敗就忽略該元素
  • Cast():利用 Convert.ChangeType() 進行轉型,轉型失敗拋出 InValidCast Exception

簡單範例

在 WinForm 內放置一個 Panel,並在 Panel 內放不同的控件,利用 OfType()、Cast() 來篩選 Panel 內 TextBox 控件

[C#] OfType()、Cast()-1

namespace LINQ_OfType_Cast
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnOfType_Click(object sender, EventArgs e)
        {
            string CtlNames = string.Empty;
            foreach (TextBox txt in panel1.Controls.OfType<TextBox>())
            {
                CtlNames += txt.Name + Environment.NewLine;
            }
            MessageBox.Show(CtlNames , "OfType() 結果" , MessageBoxButtons.OK , MessageBoxIcon.Information);
        }

        private void btnCast_Click(object sender, EventArgs e)
        {
           // 預期會丟出 InvlidCastException
           foreach (TextBox txt in panel1.Controls.Cast<TextBox>())
           {
 
           }
        }
    }
}
OfType() 結果

[C#] OfType()、Cast()-2

Cast() 結果

[C#] OfType()、Cast()-3

這樣更清楚使用 OfType()、Cast() 使用時機

星期四, 3月 24, 2016

[ADO.NET] 利用 SqlDataAdapter 產生預存程序

閱讀 極意之道次世代 .NET Framework 3.5資料庫開發聖典 發現,原來 SqlDataAdapter Wizard 可以根據 SelectCommand 產生對應的 INSERT、UPDATE 和 DELETE 預存程序,雖然說 Wizard 產生的 TSQL 語法慘不忍賭,但總比重頭開始,一個字一個字打來的好

SqlDataAdapter 選擇命令類型,選擇 [建立新的預存程序],之前都直接手打預存程序,然後選擇 [使用現有的預存程序],完全忽視第二個選項,Orz
輸入 SelectCommand,拿 AdventureWorks2014 Employee 來當成目標
命名 Select、Insert、Update 和 Delete 預存程序的名稱
預覽 SQL 指令碼,就可以看見要在 SQL Server 內建立的預存程序
產生預存程序
在 SQL Server 內可以看見預存程序已經被建立啦

以 Update 語法來看,Wizard 產生的 TSQL 真的是很慘,尤其是那串 WHERE 條件
USE [AdventureWorks2014]
GO

/****** Object:  StoredProcedure [dbo].[NewUpdateCommand]    Script Date: 2016/3/24 下午 01:32:21 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[NewUpdateCommand]
(
    @BusinessEntityID int,
    @NationalIDNumber nvarchar(15),
    @LoginID nvarchar(256),
    @OrganizationNode hierarchyid,
    @JobTitle nvarchar(50),
    @BirthDate date,
    @MaritalStatus nchar(1),
    @Gender nchar(1),
    @HireDate date,
    @SalariedFlag Flag,
    @VacationHours smallint,
    @SickLeaveHours smallint,
    @CurrentFlag Flag,
    @rowguid uniqueidentifier,
    @ModifiedDate datetime,
    @Original_BusinessEntityID int,
    @Original_NationalIDNumber nvarchar(15),
    @Original_LoginID nvarchar(256),
    @IsNull_OrganizationNode Int,
    @Original_OrganizationNode hierarchyid,
    @IsNull_OrganizationLevel Int,
    @Original_OrganizationLevel smallint,
    @Original_JobTitle nvarchar(50),
    @Original_BirthDate date,
    @Original_MaritalStatus nchar(1),
    @Original_Gender nchar(1),
    @Original_HireDate date,
    @Original_SalariedFlag Flag,
    @Original_VacationHours smallint,
    @Original_SickLeaveHours smallint,
    @Original_CurrentFlag Flag,
    @Original_rowguid uniqueidentifier,
    @Original_ModifiedDate datetime
)
AS

SET NOCOUNT OFF;

UPDATE [HumanResources].[Employee]
SET [BusinessEntityID] = @BusinessEntityID
    ,[NationalIDNumber] = @NationalIDNumber
    ,[LoginID] = @LoginID
    ,[OrganizationNode] = @OrganizationNode
    ,[JobTitle] = @JobTitle
    ,[BirthDate] = @BirthDate
    ,[MaritalStatus] = @MaritalStatus
    ,[Gender] = @Gender
    ,[HireDate] = @HireDate
    ,[SalariedFlag] = @SalariedFlag
    ,[VacationHours] = @VacationHours
    ,[SickLeaveHours] = @SickLeaveHours
    ,[CurrentFlag] = @CurrentFlag
    ,[rowguid] = @rowguid
    ,[ModifiedDate] = @ModifiedDate
WHERE (([BusinessEntityID] = @Original_BusinessEntityID)
    AND ([NationalIDNumber] = @Original_NationalIDNumber)
    AND ([LoginID] = @Original_LoginID)
    AND ((@IsNull_OrganizationNode = 1
    AND [OrganizationNode] IS NULL)
    OR ([OrganizationNode] = @Original_OrganizationNode))
    AND ((@IsNull_OrganizationLevel = 1
    AND [OrganizationLevel] IS NULL)
    OR ([OrganizationLevel] = @Original_OrganizationLevel))
    AND ([JobTitle] = @Original_JobTitle)
    AND ([BirthDate] = @Original_BirthDate)
    AND ([MaritalStatus] = @Original_MaritalStatus)
    AND ([Gender] = @Original_Gender)
    AND ([HireDate] = @Original_HireDate)
    AND ([SalariedFlag] = @Original_SalariedFlag)
    AND ([VacationHours] = @Original_VacationHours)
    AND ([SickLeaveHours] = @Original_SickLeaveHours)
    AND ([CurrentFlag] = @Original_CurrentFlag)
    AND ([rowguid] = @Original_rowguid)
    AND ([ModifiedDate] = @Original_ModifiedDate));

SELECT
    BusinessEntityID
    ,NationalIDNumber
    ,LoginID
    ,OrganizationNode
    ,OrganizationLevel
    ,JobTitle
    ,BirthDate
    ,MaritalStatus
    ,Gender
    ,HireDate
    ,SalariedFlag
    ,VacationHours
    ,SickLeaveHours
    ,CurrentFlag
    ,rowguid
    ,ModifiedDate
FROM HumanResources.Employee
WHERE (BusinessEntityID = @BusinessEntityID)
GO
產生預存程序後,要調整 TSQL 語法,千萬不要直接拿來用,另外在 使用預存程序修改資料 有特別提到
如果您要使用 SQL Server 預存程序搭配 SqlDataAdapter 來編輯或刪除資料,請務必不要在預存程序定義中使用 SET NOCOUNT ON。因為這樣會讓傳回的受影響資料列計數成為零,而 DataAdapter 會將它解譯為並行衝突。在此事件中,系統會擲回 DBConcurrencyException。
要特別注意喔

星期四, 3月 17, 2016

[RV] 列印本機報表而不進行預覽

實務上有列印報表要直接輸出至印表機,不需要預覽、也不需要出現列印視窗,找到這篇文章 逐步解說:列印本機報表而不進行預覽 來學習,基本上就是 LocalReport 搭配 PrintDocument 來進行列印

該練習需要參考下面三個 dll
  • System.Drawing
  • System.Windows.Forms
  • Microsoft.ReportViewer.Winforms
[RV] 列印本機報表而不進行預覽-2

該練習中使用的報表(Report.rdlc)請參考這篇 [RV] 使用 WinForms ReportViewer 控制項 內容,資料來源為 AdventureWorks2014 內的員工資料表,只抓出 10 筆來列印,避免實際練習要印出一堆紙
CREATE PROCEDURE [dbo].[GetEmployData]
AS
SELECT TOP 10
    E.BusinessEntityID ,
    P.LastName ,
    P.FirstName ,
    E.JobTitle ,
    E.Birthdate ,
    E.HireDate
FROM [HumanResources].[Employee] AS E
    JOIN Person.Person AS P ON E.BusinessEntityID = P.BusinessEntityID

星期三, 3月 16, 2016

[RV] 部屬 ReportViewer 控制項

把寫好的 WinForm 丟到 Client 端執行時,要開啟該 Form 時出現下面錯誤訊息

[RV] 部屬 ReportViewer 控制項-1
System.IO.FileNotFoundException:無法載入檔案或組件 'Microsoft.ReportViewer.WinForms, Version = 11.0.0.0' , Culture=neutral, PublicKeyToken = 89845dcd8080cc91' 或其相依性的其中之一。系統找不到指定的檔案
查到 部署報表和 ReportViewer 控制項 這篇文章提到,需要這 5 個 dll 檔案
  • Microsoft.ReportViewer.Common.dll
  • Microsoft.ReportViewer.ProcessingObjectModel.dll
  • Microsoft.ReportViewer.WebForms.dll
  • Microsoft.ReportViewer.WinForms.dll
  • Microsoft.ReportViewer.DataVisualization.dll
在專案中加入參考後,可以正常進入 Form,但沒想到 ReportViewer Render 時,又跑出下面錯誤訊息

[RV] 部屬 ReportViewer 控制項-2
System.IO.FileNotFoundException:無法載入檔案或組件 'Microsoft.SqlServer.Types, Version = 11.0.0.0' , Culture=neutral, PublicKeyToken = 89845dcd8080cc91' 或其相依性的其中之一。系統找不到指定的檔案
依樣畫葫蘆把 Microsoft.SqlServer.Types 也加入專案參考,ReportViewer 終於可以正常顯示,在 8 x 3 的標籤紙上列印出 3 張 QRCode,Orz

[RV] 部屬 ReportViewer 控制項-5


星期二, 3月 15, 2016

[C#] txt 資料放進 TextBox 內

論壇問題,要把下面的 txt 內容塞進 WinForm 畫面上 18 個 TextBox 內

[C#] txt 資料放進 TextBox 內-1

思考邏輯:讀每一行資料時,計算是屬於哪一個 TextBox 的資料,並透過 Controls.Find() 來尋找控件,並把值塞進去;TextBox 控件命名,要從 1 - 18 依序命名

星期三, 3月 09, 2016

[SQL] 分頁還原

紀錄當 DB 毀損時,利用分頁還原的步驟,利用 AdventureWorks2014 的 HumanResources.Employee Table 當成練習目標

事先準備
-- AdventureWorks2014 預設是簡單模式,分頁還原無法在該模式下進行,
-- 因此轉為完整模式
ALTER DATABASE AdventureWorks2014 SET Recovery FULL

-- 要進行分頁還原前,當然要有備份檔案存在
BACKUP DATABASE AdventureWorks2014 TO DISK = N'D:\PageRestoreDemo.bak' 
BACKUP LOG AdventureWorks2014 TO DISK = N'D:\PageRestoreDemo.bak' 

-- 對 DB 進行破壞,請參考 [SQL] 模擬資料庫毀損 該篇文章
-- 使用 DBCC WRITEPAGE 必須是單一使用者模式
ALTER DATABASE AdventureWorks2014 SET SINGLE_USER WITH ROLLBACK IMMEDIATE
-- 毀損 PK_Employee_BusinessEntityID 叢集索引,所在的 Page 733 => Data Page
DBCC WRITEPAGE('AdventureWorks2014', 1, 733, 60, 1, 0x00, 1)
-- 切回多人模式
ALTER DATABASE AdventureWorks2014 SET multi_user WITH ROLLBACK IMMEDIATE

星期一, 3月 07, 2016

[RV] 顯示外部圖片

實務上有該需求,了解一下要如何做到,下面為顯示外部圖片的簡單紀錄

建立報表

新增報表 ExternalPhoto.rdlc 並進行下列設定,報表資料 => 參數 => 滑鼠右鍵 => 加入參數
參數名稱:PhotoPath
設定完後的報表資料視窗
在報表內插入影像控件,影像屬性內的影像來源設定為外部
使用此影像運算式,一定要有 "file:///" 字樣在變數前面
= "file:///" & Parameters!PhotoPath.Value

ReportViewer 控件

拖曳 ReportViewer 控件到 Form 中,並選擇報表
設定變數來源

利用 Code 把報表中所需的 @PhotoPath 變數丟進報表內
using Microsoft.Reporting.WinForms;

namespace RSPhoto
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // 欲連結外部圖片必須設定該屬性
            reportViewer1.LocalReport.EnableExternalImages = true;
            // 設定 Report 內的 @PhoroPath 參數資料
            ReportParameter PhotoPath = new ReportParameter("PhotoPath", @"D:\Demo.jpg");
            reportViewer1.LocalReport.SetParameters(new ReportParameter[] { PhotoPath });
            // 更新 ReportViewer
            reportViewer1.RefreshReport();
        }
    }
}
顯示設定結果

星期五, 3月 04, 2016

[RV] 使用 WinForms ReportViewer 控制項

網路上找到這篇文章 Using ReportViewer control in Windows Forms (WinForms) Application using C# and VB.Net,覺得有幫助就拿來練習,資料抓 AdventureWorks2014 內的員工資料

建立 DataSet

新增資料集:Employee.xsd

[RV] 使用 WinForms ReportViewer 控制項-1

在 Employee.xsd 內新增 DataTable:dtEmploy,並建立 DataColumn

[RV] 使用 WinForms ReportViewer 控制項-2

HireDate 和 BirthDate 的 DataType 要改為 System.DateTime,預設為 System.String

[RV] 使用 WinForms ReportViewer 控制項-3

完成後就可以在資料來源視窗內看見該 DataSet

[RV] 使用 WinForms ReportViewer 控制項-4

星期三, 3月 02, 2016

[SQL] Linked Server 讀取 VFP 檔案

看見這篇 論壇討論,想說來試看看,畢竟 VFP 現在還是公司主力,雖然已經從純 VFP 改為 VFP + SQL Server,實務上也不會有這樣的需求和應用,加減學吧

測試環境:
  • Win10 64bit
  • SQL Server 2005 Express 32bit
  • VFP 9.0

星期二, 3月 01, 2016

[VFP] Automation 應用 - 儲存為 xls 格式

配合 HR 提出的需求,HR PC 上是 Office 2016 希望轉出來的 Excel 格式為 xls 格式,這樣才能匯入政府網站,原是想說把副檔名改為 xls 就行,沒想到打開 Excel 時,會出現下面的錯誤

SaveAs 語法
loExcel.ActiveWorkbook.SaveAs("D:\Excel.XLS")
打開 Excel 時出現的錯誤訊息

[VFP] Automation 應用 - 儲存為 xls 格式-1

利用 Excel 錄製 VBA 來觀察,看看轉為 xls 到底還需要甚麼參數,FileFormat:=xlExcel8 就是你啦
Sub 巨集1()
'
' 巨集1 巨集
'

'
    ChDir "C:\Users\jengting\Desktop"
    ActiveWorkbook.SaveAs Filename:="C:\Users\jengting\Desktop\活頁簿1.xls", _
        FileFormat:=xlExcel8, 
        Password:="", 
        WriteResPassword:="", _
        ReadOnlyRecommended:=False, 
        CreateBackup:=False
End Sub
把 VFP Automation 語法改為下述,xlExcel8 參數值為 56,就可以順利轉為 xls 格式啦
loExcel.ActiveWorkbook.SaveAs("D:\Excel.XLS" , 56)