2010年11月7日 星期日

ASP.Net GridView 的自訂資料繫結

在論壇上,常常看到有網友提出這樣的問題:如果資料值是 Y/N,要顯示 是/否 在 GridView 上,要如何做?
這個問題有幾種解法

第一種:使用 GridView.RowDataBound 事件,搭配 TemplateField

這種方法是大部分的書上會介紹的方法

RowDataBound 事件,會傳入一個型別為 GridViewRowEventArgs 的參數 e
e.Row 指的是 GridView 剛剛做完資料繫結的列
e.Row.DataItem 指的是用來做這個列的資料繫結的資料來源,型別是 object
如果以 DataTable 或是 DataSet 做資料來源,e.Row.DataItem 實際上是一個 DataRowView
所以,如果以 DataTable 或是 DataSet 做資料來源,(e.Row.DataItem as DataRowView).Row["欄位名稱"] 可以取得欄位的資料值

接下來,就是「數格子」,要知道你的 TemplateField 是第幾欄,還要知道要做自訂資料繫結的控制項是 ItemTemplate 的第幾個控制項


(e.Row.Cells[欄索引].Controls[控制項索引] as 控制項型別).Text =
  (e.Row.DataItem as DataRowView).Row["欄位名稱"].ToString()

或是要知道自訂資料繫結的控制項的 ID

(e.Row.Cells[欄索引].FindControl("控制項ID") as 控制項型別).Text =
  (e.Row.DataItem as DataRowView).Row["欄位名稱"].ToString()

這種方法的缺點是
  1. 如果在這個TemplateField 之前,加入或是刪掉了其他欄,或是修改了 TemplateField 的 ItemTemplate,事件處理都得修改
  2. 如果不是以 DataTable 或是 DataSet 做資料來源,e.Row.DataItem 事實上是什麼型別?

不確定因素太多,既麻煩又複雜!強烈不建議使用這個方法

第二種:使用單純自訂資料繫結,搭配 TemplateField
這種方法直接在 TemplateField 的 ItemTemplate 中放置一個 Label,並設置 Text 屬性的資料繫結為

Text='<%# Eval("欄位名稱").ToString() == "Y" ? "是" : (Eval("欄位名稱").ToString() == "N" ? "否" : "") %>'
Text='<%# Eval("欄位名稱", "{0}") == "Y" ? "是" : (Eval("欄位名稱", "{0}") == "N" ? "否" : "") %>'

簡單明瞭,只是判斷是寫在 .aspx 裡面,而且當資料值是 DbNull 的時候會發生錯誤,如果還要加上判斷是否為 DbNull 的語法就更複雜了
因此,此方法建議使用於簡單判斷以及資料欄位不允許 null 的狀況
PS: Eval("欄位名稱") 與 Eval("欄位名稱", "{0}") 的不同點在於前者回傳的是 Object,後者回傳的是 String

第三種:使用自訂資料繫結方法,搭配 TemplateField
跟第二種方法類似,只是將 Label 的 Text 屬性的資料繫結改為呼叫一個自訂方法 (必須為 public 或 protected 方法)

Text='<%# YesOrNo(Eval("欄位名稱")) %>'

並撰寫後端程式碼如下:
protected string YesOrNo(object o)
{
  string s = o == DbNull.Value ? "" : Convert.ToString(o);
  return s == "Y" ? "是" : (s == "N" ? "否" : "");
}
這樣的好處是

  1. 不用數格子
  2. 沒有複雜的資料繫結語法
  3. 無論從何種資料來源做資料繫結,都可以適用
  4. 可檢查資料是否為 DbNull
提供給大家做參考

2010年11月3日 星期三

Delphi 5 使 DBGrid 支援滑鼠滾輪的程式碼

Delphi 5 的 TDBGrid 無法支援滑鼠滾輪移動資掉指標,而且當有 PickList 的 Column 或是 Lookup Field 下拉時
滾動滑鼠滾輪會有很奇怪的行為。
要解決這個問題,可以繼承 TDBGrid 寫一個新的 DBGrid 類別,並覆寫兩個方法:

interface
type
  TNewDBGrid = class(TDBGrid)
  protected
    function DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean; override;
    function DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean; override;    
  end;

implementation
  function TNewDBGrid.DoMouseWheelDown(Shift: TShiftState; MousePos: TPoint): Boolean;
  begin
    Result := False;
    if Assigned(OnMouseWheelDown) then
      OnMouseWheelDown(Self, Shift, MousePos, Result);
    if (not Result) and (DataLink <> nil) and (DataLink.Active)
       and (DataLink.DataSet <> nil) and (DataLink.DataSet.Active) then
    begin
      DataLink.DataSet.MoveBy(1);
      Result := True;
    end;
  end;

  function TNewDBGrid.DoMouseWheelUp(Shift: TShiftState; MousePos: TPoint): Boolean;
  begin
    Result := False;
    if Assigned(OnMouseWheelDown) then
      OnMouseWheelDown(Self, Shift, MousePos, Result);
    if (not Result) and (DataLink <> nil) and (DataLink.Active)
       and (DataLink.DataSet <> nil) and (DataLink.DataSet.Active) then
    begin
      DataLink.DataSet.MoveBy(-1);
      Result := True;
    end;
  end;

2010年9月8日 星期三

多螢幕環境下,指定螢幕顯示畫面 (C#)

private void ChangeScreen(Form aForm, int screenId)  
{   
  // 先將 Form 設定為 Normal   
  aForm.WindowState = FormWindowState.Normal;   
  
  // 改 Location   
  aForm.StartPosition = FormStartPosition.Manual;   
  if (screenId < Screen.AllScreens.Length)   
  {   
    aForm.Location = Screen.AllScreens[screenId].WorkingArea.Location;   
  }   
  
  // 最後設定成 Maximized   
  aForm.WindowState = FormWindowState.Maximized;   
}

2010年9月2日 星期四

多螢幕環境下,指定螢幕顯示畫面 (Delphi 5)

這個 Procedure 是從 Delphi 2009 的 Forms.pas 中挖出來的,在多螢幕環境之下可以將 Form 顯示在指定的螢幕上,對於要開發多螢幕應用程式的人有一些用處。
procedure MakeFullyVisible(AForm: TForm; AMonitor: TMonitor);   
var   
  ALeft: Integer;   
  ATop: Integer;   
begin   
  if AMonitor = nil then   
    AMonitor := AForm.Monitor;   
  ALeft := AForm.Left;   
  ATop := AForm.Top;   
  if AForm.Left + AForm.Width > AMonitor.Left + AMonitor.Width then   
    ALeft := AMonitor.Left + AMonitor.Width - AForm.Width;   
  if AForm.Left < AMonitor.Left then   
    ALeft := AMonitor.Left;   
  if AForm.Top + AForm.Height > AMonitor.Top + AMonitor.Height then   
    ATop := AMonitor.Top + AMonitor.Height - AForm.Height;   
  if AForm.Top < AMonitor.Top then   
    ATop := AMonitor.Top;   
  AForm.SetBounds(ALeft, ATop, AForm.Width, AForm.Height);   
end;
呼叫方法是:
MakeFullyVisible(Form1, Screen.Monitors[0]);    // 將 Form1 顯示在第一個螢幕   
MakeFullyVisible(Form2, Screen.Monitors[1]);    // 將 Form2 顯示在第二個螢幕

2010年4月15日 星期四

Delphi 取得實體硬碟序號 (非分割區序號)

程式

//自訂型態
type   
  IDEREGS = record   
    bFeaturesReg: Byte;   
    bSectorCountReg: Byte;   
    bSectorNumberReg: Byte;   
    bCylLowReg: Byte;   
    bCylHighReg: Byte;   
    bDriveHeadReg: Byte;   
    bCommandReg: Byte;   
    bReserved: Byte;   
  end; 

DRIVERSTATUS = record bDriveError: Byte; bIDEStatus: Byte; bReserved: array[1..2] of Byte; dwReserved: array [1..2] of LongWord; end;

SENDCMDOUTPARAMS = record cBufferSize: LongWord; DStatus: DRIVERSTATUS; bBuffer: array[1..512] of Byte; end;

SENDCMDINPARAMS = record cBufferSize: LongWord; irDriveRegs: IDEREGS; bDriveNumber: Byte; bReserved: array[1..3] of Byte; dwReserved: array[1..4] of LongWord; end;

THdInfoType = (HD_MODEL_NUMBER, HD_SERIAL_NUMBER, HD_FIRMWARE_REVISION);

// API宣告  
function DeviceIoControl   
  (hDevice: LongWord;   
   dwIoControlCode: LongWord;   
   lpInBuffer: Pointer;   
   nInBufferSize: LongWord;   
   lpOutBuffer: Pointer;   
   nOutBufferSize: LongWord;   
   var lpBytesReturned: LongWord;   
   lpOverlapped: LongWord): LongWord; stdcall; external 'Kernel32';
// 取得硬碟序號  
function Get_HD_Info(DrvIdx: byte; HdInfoType: THdInfoType): String;   
var   
  ParaIn: SENDCMDINPARAMS;   
  ParaOut: SENDCMDOUTPARAMS;   
  Sno: String;   
  h, br: LongWord;   
  i, st, ed: Integer;   
  PlatForm: string;

  procedure GetPlatForm;  
  var OS: OSVERSIONINFO;   
  begin   
    OS.dwOSVersionInfoSize := SizeOf(OS);   
    GetVersionEx(OS);   
    PlatForm := 'UNK';   
    case OS.dwPlatformId of   
      VER_PLATFORM_WIN32S:   
        PlatForm := '32S';                      // Win32S   
      VER_PLATFORM_WIN32_WINDOWS:   
        begin   
          if OS.dwMinorVersion = 0 then   
            PlatForm := 'W95'                   // Win 95   
          else   
            PlatForm := 'W98'                   // Win 98   
        end;   
      VER_PLATFORM_WIN32_NT:   
        PlatForm := 'WNT';                      // Win NT/2000   
    end;   
  end;

begin  
  GetPlatForm;
  if Pos(PlatForm, 'WNT') > 0 then  
    h := CreateFile(PChar('\\.\PhysicalDrive' + IntToStr(DrvIdx)), GENERIC_READ + GENERIC_WRITE,   
                     FILE_SHARE_READ + FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0)   
  else if Pos(PlatForm, 'W95,W98') > 0 then   
    h := CreateFileA('\\.\Smartvsd', 0, 0, 0, CREATE_NEW, 0, 0)   
  else   
    Raise Exception.Create('Platform must be Win95, Win98, WinNT');

  If h = 0 Then Exit;
  ZeroMemory(@ParaIn, SizeOf(ParaIn));  
  ZeroMemory(@ParaOut, SizeOf(ParaOut));
  with ParaIn do  
  begin   
    bDriveNumber := DrvIdx;   
    cBufferSize := 512;   
    With irDriveRegs do   
    begin   
      if (DrvIdx and 1) = 1 then   
        bDriveHeadReg := $B0   
      else   
        bDriveHeadReg := $A0;   
      bCommandReg := $EC;   
      bSectorCountReg := 1;   
      bSectorNumberReg := 1;   
    end;   
  end;

  DeviceIoControl(h, $7C088, @ParaIn, SizeOf(ParaIn), @ParaOut, SizeOf(ParaOut), br, 0);
  case HdInfoType of  
    HD_MODEL_NUMBER:   
      begin   
        st := 55;   
        ed := 94;   
      end;   
    HD_SERIAL_NUMBER:   
      begin   
        st := 21;   
        ed := 40;   
      end;   
    HD_FIRMWARE_REVISION:   
      begin   
        st := 47;   
        ed := 54;   
      end;   
  end;
  i := st;  
  while i <= ed do   
  begin   
    if ParaOut.bBuffer[i + 1] = 0 then   
      break;   
    Sno := Sno + Chr(ParaOut.bBuffer[i + 1]);   
    if ParaOut.bBuffer[i] > 0 then   
      Sno := Sno + Chr(ParaOut.bBuffer[i]);   
    i := i + 2;   
  end; 
  CloseHandle(h);
  Result := Trim(Sno);  
end;   

呼叫範例

Memo1.Lines.Add('型號:' + Get_HD_Info(0, HD_MODEL_NUMBER));  
Memo1.Lines.Add('序號:' + Get_HD_Info(0, HD_SERIAL_NUMBER));   
Memo1.Lines.Add('韌體版本:' + Get_HD_Info(0, HD_FIRMWARE_REVISION)); 

2010年1月31日 星期日

Peopleware 書摘

加班就像衝刺:跑馬拉松要為最後百米保留體力是有道理的,一開始就拔腿狂奔只會浪費時間。經常強迫部屬衝刺的經理人,只會失去部屬的尊重。最優秀的員工早就看穿一切,當經理人吼著要在四月份完工時,他們懂得三緘其口,等著看好戲,只要有機會,就開始補償性的打混摸魚,到頭來每週真正在工作的還是四十小時。

《Peopleware 腦力密集產業的人才管理之道》第三章  P.37

2010年1月22日 星期五

在 Window 2000 之下執行 Delphi 2010 的應用程式

  1. 複製 Delphi 2010 的 *.BPL 檔案至 WINNT\System32
  2. 複製 MIDAS.DLL 至 WINNT\System32
  3. 安裝 MDAC 2.8
  4. 安裝 Windows Installer 3.1
  5. 安裝 SQL Server native Client

2010年1月18日 星期一

使用 SQL Server 開啟 DBF 檔案

dBase 的 DBF 檔,可以使用 Jet 4.0 開啟
所以可以在 SQL Server 的管理工具中這樣下:(假設你的 DBF 檔是 C:\DBF\ABC.DBF)

SELECT * FROM OPENDATASOURCE('Microsoft.Jet.OLEDB.4.0', 'Data Source=C:\DBF;Extended Properties=dBase III;')...ABC

ABC 前面的三個點不可以省略,因為是 server_name.database_name.schema_name.table_name 四部份名稱中間省略了 database_name 和 schame_name 之後留下的三個點

如果SQL 2005/2008 出現
SQL Server 已封鎖元件 'Ad Hoc Distributed Queries' 的 STATEMENT 'OpenRowset/OpenDatasource' 之存取
這樣的錯誤訊息,請先到
SQL Server 介面區組態 >> 功能的介面區組態 >> 特定遠端查詢
去開啟 啟用 OPENROWSET 與 OPENDATASOURCE 支援之後,就可以執行了

2010年1月8日 星期五

X64 系統 BDE merge module 安裝方法

由於 x64 系統已經無法執行 16 bit 的程式,使得原先的 BDE 安裝程式已經無法使用了,解決方法就是重包一個(至少是 x32 的)安裝程式。

重包 BDE 安裝程式,需要 BDE merge module。可以於此處下載:http://support.installshield.com/kb/view.asp?articleid=Q104962