2023年12月26日 星期二

ComboBox 繪製模式

 

ComboBox1.Style

csOwnerDrawFixed

  • 當 ComboBox.Style 設定為 csOwnerDrawFixed 時,所有項目將以相同的高度顯示,並且當需要時,控制元件會調用 OnDrawItem 事件來繪製每個項目。
  • 所有項目的高度由 ItemHeight 屬性定義。這表示無論項目的內容有多長,顯示的高度都是相同的。
  • 對於列表中的每個項目,OnDrawItem 事件都會觸發,並且您必須在該事件中進行繪製。

csOwnerDrawVariable

  • 當 ComboBox.Style 設定為 csOwnerDrawVariable 時,可以根據每個項目的實際內容來設置不同的高度,並且在需要時會調用 OnMeasureItem 和 OnDrawItem 事件來繪製每個項目。
  • OnMeasureItem 事件用於指定每個項目的高度,您可以根據項目的內容計算不同的高度。這樣,不同的項目可以具有不同的顯示高度。
  • OnDrawItem 事件用於實際的繪製操作。在該事件中,您可以根據需要來繪製每個項目的外觀。


資料來源 : CharGPT 

2023年12月25日 星期一

PopupMenuLib.pas 記錄


AttachFilePopupMenu

procedure TForm1.FormCreate(Sender: TObject);
begin
  popAttachFile := TAttachFilePopupMenu.Create;
end;

procedure TForm1.Button9Click(Sender: TObject);
begin
  popAttachFile.Popup(Button9);  //參數 nil  會在游標處展開下拉選單
end;

//Property
//  popAttachFile.AttachFiles       //附加的文件清單
//  popAttachFile.Readonly          //唯讀,不可附加、移除
//  popAttachFile.DisableAttachFile //不提供附加
//  popAttachFile.DisableRemove     //不提供移除
//  popAttachFile.DisableOpenFile   //不提供文件開啟


PrinterPopupMenu

Uses Printers;

procedure TForm1.Button9Click(Sender: TObject);
var pmPrinterList: TPrinterPopupMenu;
begin
   pmPrinterList := TPrinterPopupMenu.Create;
   pmPrinterList.Popup(Button9);  //參數 nil  會在游標處展開下拉選單
end;

// Property
//   pmPrinterList.PrinterIndex   //印表機Index
//   pmPrinterList.PrinterName    //印表機名稱



ControlLib.pas 記錄




TColor 調整亮度

procedure pr_BrightenColor(var Color: TColor; Brightness: Integer);
var R, G, B: Byte;
begin
  R := GetRValue(Color);
  G := GetGValue(Color);
  B := GetBValue(Color);

  R := Min(255, R + Brightness);
  G := Min(255, G + Brightness);
  B := Min(255, B + Brightness);

  Color := RGB(R, G, B);
end;


2023年12月14日 星期四

Dataset 查詢介面上被異動過的資料

ADO Dataset

在ADODataset要查看修改過且尚未寫回資料庫的資料列,可以用FilterGroup來查看

Ex:
  with ADataset do
  begin
    if LockType = ltBatchOptimistic then
    begin
      Filtered := True;
      FilterGroup := fgPendingRecords;
    end;
    FilterGroup := fgUnassigned;
    Filtered := False;
  end;


ClientDataset

ClientDataset Delta 記錄了維護畫面上被異動過的資料列,ApplyUpdates將取用Delta記錄的資料狀態回寫到資料庫中。


Ex:


使用CleintDataset、DataSource、DBGrid並設定關連性。

procedure TForm1.Button1Click(Sender:TObject);
begin
  ...
  //cdsDetail異動過的資料列,反應在ClientDataset1
  ClientDataset1.Data := cdsDetail.Delta;
  ...
end;

2023年12月6日 星期三

PageControl ActivePage 做顏色區別,增加閱讀性。


 
PageControl1.OwnerDraw := True;


Procedure TForm1.pr_PageControl1_DrawTab(Control: TCustomTabControl; TabIndex: Integer; const [Ref] Rect: TRect; Active: Boolean);
var iMidWidth, iMidHeight, iTextWidth, iTextHeight:Integer;
  sCaption:String;
begin
  sCaption := TPageControl(Control).Pages[TabIndex].Caption;
  iTextWidth := TPageControl(Control).Canvas.TextWidth(sCaption);
  iTextHeight := TPageControl(Control).Canvas.TextHeight(sCaption);
  iMidWidth := Rect.Left + Ceil((Rect.Width-iTextWidth)/2);
  iMidHeight := Rect.Top + Ceil((Rect.Height-iTextHeight)/2);

  if Active  then
  begin
    TPageControl(Control).Canvas.Brush.Color := clHighlight;
    TPageControl(Control).Canvas.Font.Color := clHighlightText;
  end
  else
    TPageControl(Control).Canvas.Brush.Color := clScrollBar;

  TPageControl(Control).Canvas.FillRect(Rect);
  TPageControl(Control).Canvas.TextOut(iMidWidth, iMidHeight, sCaption);
end;



2023年11月28日 星期二

在表單底下空白處,使用LoopBand填滿表格

報表情境1 (未使用GroupBand)


procedure TForm1.DetailBand1AfterPrint(Sender: TQRCustomBand;  BandPrinted: Boolean);
var iCurrencyY, iPaperLength, iPrintY, iCount, iLoopBandHeight, iBottomMargin,
  iPageFooterBand, iQRGroupFooter:Integer;
begin
  //檢查最後一筆
  if ADOQuery1.RecNo=ADOQuery1.RecordCount then
  begin
    iPaperLength := QuickRep1.QRPrinter.PaperLength;  //報表高度
    iCurrencyY := QuickRep1.CurrentY;  //目前輸出的高度
    iLoopBandHeight := Ceil(QRLoopBand1.Size.Length);  //LoopBand 的高度
    iPageFooterBand := Ceil(PageFooterBand1.Size.Length);  //PageFooterBand 的高度
    iBottomMargin := Ceil(QuickRep1.Page.BottomMargin);  //報表邊界Bottom的高度
    iPrintY := iPaperLength - iCurrencyY - iPageFooterBand - iBottomMargin; //LoopBand 輸出的高度
    iCount := iPrintY div iLoopBandHeight;
    QRLoopBand1.PrintCount := iCount;
end;




 報表情境2 (使用GroupBand)



procedure TForm1.QuickRep2StartPage(Sender: TCustomQuickRep);
begin
  //在報表上放置QRExpr1,借來運算QRGroup.Expression運算後的結果
  QRExpr1.Expression := QRGroup2.Expression;
  QRExpr1.Enabled := False;
end;


procedure TForm1.DetailBand1AfterPrint(Sender: TQRCustomBand; BandPrinted: Boolean);
var iCurrencyY, iPaperLength, iPrintY, iCount, iLoopBandHeight, iBottomMargin,
  iPageFooterBand, iQRGroupFooter:Integer;
  sCurValue, sNextValue:String;
begin
  //取得目前以及下一筆資料的運算結果做比對
  sCurValue := QRExpr1.Value.StringVal;
  adoQuery1.Next;
  sNextValue := sCurValue;
  if not adoQuery1.eof then
  begin
    sNextValue := QRExpr1.Value.StringVal;
    adoquery1.Prior;
  end;

  //如果是最後一筆,或是前後筆資料不吻合,就使用LoopBand填滿
  iCount := 0;
  if (ADOQuery1.RecNo=ADOQuery1.RecordCount) or (sCurValue<>sNextValue) then
  begin
    iPaperLength := QuickRep2.QRPrinter.PaperLength;  //報表高度
    iCurrencyY := QuickRep2.CurrentY;  //目前輸出的高度
    iLoopBandHeight := Ceil(QRLoopBand2.Size.Length);  //LoopBand 的高度
    iQRGroupFooter := Ceil(QRGroupFooter2.Size.Length);  //GroupFooter 的高度
    iPageFooterBand := Ceil(PageFooterBand2.Size.Length);  //PageFooter 的高度
    iBottomMargin := Ceil(QuickRep2.Page.BottomMargin);  //報表邊界Bottom的高度
    iPrintY := iPaperLength - iCurrencyY - iPageFooterBand - iQRGroupFooter - iBottomMargin;
    iCount := iPrintY div iLoopBandHeight;
  end;
  QRLoopBand2.PrintCount := iCount;
end;


2023年11月21日 星期二

連線網路磁碟/目錄

Procedure pr_AddConnection(APath, ALocalDriver, AUserName, APassword:String);
var netSource:TNetResource;
  dwResult:DWORD;
  sMsg:String;
begin
  dwResult := WNetCancelConnection2(PWideChar(APath), 
    CONNECT_UPDATE_PROFILE, 
    True);
  if APath<>'' then
  begin
    with netSource do
    begin
      dwType := RESOURCETYPE_DISK;
      lpLocalName := '';  // or H:
      lpRemoteName := PWideChar(APath);
      lpProvider := '';
      dwResult := WNetAddConnection2(netSource, 
        PWideChar(APassword), 
        PWideChar(AUsername), 
        CONNECT_UPDATE_PROFILE);
      if dwResult<>NO_ERROR then
      begin
        sMsg := SysErrorMessage(dwResult);
        raise Exception.Create(sMsg);
      end;
    end;
  end;
end;

2023年9月27日 星期三

XML Documentation Comments ,自定函式功能註解。

官方功能說明

XML Documentation Comments


官方範例
/// <summary> Removes the specified item from the collection
/// </summary>
/// <param name="Item">The item to remove
/// </param>
/// <param name="Collection">The group containing the item
/// </param>
/// <remarks>
/// If parameter "Item" is null, an exception is raised.
/// <see cref="EArgumentNilException"/>
/// </remarks>
/// <returns>True if the specified item is successfully removed;
/// otherwise False is returned.
/// </returns>
function RemoveItem(Item: Pointer; Collection: Pointer): Boolean;
begin
  // Non-XML DOC comment
  // ...
end;
初始效果


自定函式註解


2023年6月6日 星期二

Delphi 顯示器-縮放與版面配置,影響報表輸出的表單尺寸

 縮放與版面配置  








當Project Options 勾選 Enable High-DPI ,專案的界面會依照縮放比例做調整,Screen.PixelsPerInch會取得與縮放比對應的數值 (Ex: 縮放比125%會取得120)

沒有勾選 Enable High-DPI,Screen.PixelsPerInch會取得 96 (縮放比100%)

Var
  iHorzres, iDesktopHorzres:Integer;
  iScale:Currency;
begin
  iHorzres := GetDeviceCaps(GetDC(0), HORZRES);  //水平像素總數
  iDesktopHorzres := GetDeviceCaps(GetDC(0), DESKTOPHORZRES); // 桌面水平像素總數

  iScale :=  iDesktopHorzres / iHorzres ;
end


影響QuickRep 報表版面配置的處理方式

with QuickRep1 do
begin
  ...
  ...
  ScaleBy(Screen.PixelsPerInch * iScale , Screen.PixelsPerInch);
  Prepare;
  ...
  ...
end;

2023年5月1日 星期一

Delphi 取用系統圖示(Icon)

Uses ShellAPI;
...
  Button1 : TButton;
  ImageList1 : TImageList;
  TListView1 : TListView;
...
...
procedure TForm1.Button1Click(Sender: TObject);
var
  IconIndex: word;
  sFileName:String;
  Buffer: array[0..2048] of char;
  IconHandle: HIcon;
  objIcon:TIcon;
  i, iCount:Integer;
  lvItem:TListItem;
begin
  objIcon := TIcon.Create;
  sFileName := 'Shell32.dll';
  StrCopy(@Buffer, PChar(sFileName));
  iCount := ExtractIcon(HInstance, PChar(sFileName), $FFFFFFFF);
  for i := 0 to iCount-1 do
  begin
    IconIndex := i;
    IconHandle := ExtractAssociatedIcon(HInstance, Buffer, IconIndex);
    if IconHandle <> 0 then
      objIcon.Handle := IconHandle;
    ImageList1.AddIcon(objIcon);
    lvItem := ListView1.Items.Add;
    lvItem.ImageIndex := i;
    lvItem.Caption := IntToStr(i);
  end;
end;

Windows存放系統圖示的文件
shell32.dll:包含許多與 Windows Shell 有關的圖示
imageres.dll: 是一個包含大量 Windows 系統圖示的檔案
user32.dll:包含一些基本的系統圖示 
comctl32.dll:包含一些常用的圖示 

參考:

CharGRT 




Shell32.dll


Imageres.dll


User32.dll


Comctl32.dll


  

2023年3月29日 星期三

Delphi 要如何取得DLL的檔案名稱

 
var
  DLLFileName: array [0..MAX_PATH] of Char;
begin
  GetModuleFileName(HInstance, DLLFileName, Length(DLLFileName));
  ShowMessage(DLLFileName);
end;

CharGPT 的回應

2023年3月28日 星期二

2023年3月23日 星期四

Delphi ClientDataset GetGroupState

GetGroupState

ClientDataset 需先建立IndexDef,GetGroupState代入GroupingLevel,可以取得資料列在Group中所屬的狀態gbFirst、gbMiddle、gbLast


procedure TForm1.FormCreate(Sender: TObject);
var inxIndex:TIndexDef;
  aggAggregate:TAggregate;
begin
  //Create Index
  inxIndex := ClientDataSet1.IndexDefs.AddIndexDef;
  inxIndex.Name := 'Index1';
  inxIndex.Fields := 'Field1;Field2';
  inxIndex.GroupingLevel := 2; //群組的層級,取決於Fields的數量
  ClientDataset1.IndexName := inxIndex.Name;
end;

procedure TForm1.ClientDataSet1Field1GetText(Sender: TField; var Text: string;
  DisplayText: Boolean);
begin
  //TGroupPosInd = (gbFirst, gbMiddle, gbLast);
  if gbFirst in ClientDataset1.GetGroupState(1) then
    Text := Sender.AsString
  else
    Text := '';
end;


原資料


gbFirst in ClientDataset1.GetGroupState(1)

gbMiddle in ClientDataset1.GetGroupState(1)

gbLast in ClientDataset1.GetGroupState(1)


ClientDataset Methods 說明

Datasnap.DBClient.TClientDataSet Methods


Delphi ClientDataset Aggregates & Index 分組合計

 


procedure TForm1.FormCreate(Sender: TObject);
var inxIndex:TIndexDef;
  aggAggregate:TAggregate;
begin
  with ClientDataSet1 do
  begin
    //Create Dataset
    FieldDefs.Add('Field1', ftString, 20);
    FieldDefs.Add('Field2', ftString, 20);
    FieldDefs.Add('Qty', ftInteger);
    CreateDataSet;
    //Append Data
    AppendRecord(['1', '1', 10]);
    AppendRecord(['1', '1', 30]);
    AppendRecord(['1', '2', 50]);
    AppendRecord(['2', '2', 70]);
    AppendRecord(['2', '1', 90]);
  end;

  //Create Index
  inxIndex := ClientDataSet1.IndexDefs.AddIndexDef;
  inxIndex.Name := 'Index1';
  inxIndex.Fields := 'Field1;Field2';
  inxIndex.GroupingLevel := 2; //群組的層級,取決於Fields的數量
  ClientDataset1.IndexName := inxIndex.Name;

  //Create Aggregates
  aggAggregate := ClientDataset1.Aggregates.Add;
  aggAggregate.AggregateName := 'Aggregate0';
  aggAggregate.Expression := 'Sum(Qty)';
  //針對指定的群組層級做統計. 0:全部, Dataset可以不指定IndexName
  aggAggregate.GroupingLevel := 0;
  aggAggregate.IndexName := inxIndex.Name;
  aggAggregate.Active := True;

  aggAggregate := ClientDataset1.Aggregates.Add;
  aggAggregate.AggregateName := 'Aggregate1';
  aggAggregate.Expression := 'Sum(Qty)';
  //針對指定的群組層級做統計, Ex: Gropup by Field1
  aggAggregate.GroupingLevel := 1;
  aggAggregate.IndexName := inxIndex.Name;
  aggAggregate.Active := True;

  aggAggregate := ClientDataset1.Aggregates.Add;
  aggAggregate.AggregateName := 'Aggregate2';
  aggAggregate.Expression := 'Sum(Qty)';
  //針對指定的群組層級做統計, Ex: Group by Field1, Field2
  aggAggregate.GroupingLevel := 2;
  aggAggregate.IndexName := inxIndex.Name;
  aggAggregate.Active := True;

  aggAggregate := ClientDataset1.Aggregates.Add;
  aggAggregate.AggregateName := 'Aggregate3';
  aggAggregate.Expression := 'Max(Qty)';
  //針對指定的群組層級做統計, Ex: Group by Field1
  aggAggregate.GroupingLevel := 1;
  aggAggregate.IndexName := inxIndex.Name;
  aggAggregate.Active := True;

  Memo1.Clear;
  Memo1.Lines.Add('Aggregate0 (Sum(Qty)) = '+VarToStr(ClientDataSet1.Aggregates.Find('Aggregate0').Value));
  Memo1.Lines.Add('Aggregate1 (Sum(Qty) Group by Field1) = '+VarToStr(ClientDataSet1.Aggregates.Find('Aggregate1').Value));
  Memo1.Lines.Add('Aggregate2 (Sum(Qty) Group by Field1, Field2) = '+VarToStr(ClientDataSet1.Aggregates.Find('Aggregate2').Value));
  Memo1.Lines.Add('Aggregate3 (Max(Qty) Group by Field1) = '+VarToStr(ClientDataSet1.Aggregates.Find('Aggregate3').Value));
end;

2023年3月15日 星期三

Delphi ForceDirectories 建立資料夾

 

ForceDirectories     建置到指定的層級目錄 

Ex : 
ForceDirectories ('C:\Temp\Sub1\Sub2');
ForceDirectories ('\\10.10.10.10\Temp\Sub1\Sub2');

 

2023年3月9日 星期四

SQL PIVOT 直向資料內容轉橫向輸出


Select *
  (
    Select Field01, Field02, Field03, Field04
    From Table1
  ) a
Pivot
  ( 
    Sum(Field05) /*合計值*/
    For Field04 in ([Value01], [Value02], [Value03], [Value04])  /*橫向表頭*/
  ) b




2023年3月6日 星期一

ScorllBar 系統寬高

 
ScrollBar High
    GetSystemMetrics(SM_CYHSCROLL)


ScrollBar Width
    GetSystemMetrics(SM_CXVSCROLL)

2023年2月8日 星期三

Delphi TAggregate Row Summary

Ex:

var 
  sFieldName :String;
  fdAggregate:TAggregate;

begin
  cdsTemp := TClientDataset.Create(Self);
  cdsTemp.Aggregates.Clear;
  cdsTemp.AggregatesActive := True;
  cdsTemp.CloneCursor(Master, False, True);

  for i:=0 to Master.FieldCount-1 do
  begin
    sFieldName := Master.Fields[i].FieldName;
    if cdsTemp.FieldByName(sFieldName).DataType in 
      [ftSmallint, ftInteger, ftFloat, ftCurrency, ftLargeint] then
    begin
      fdAggregate := TAggregate.Create(cdsTemp.Aggregates, cdsTemp);
      fdAggregate.AggregateName := 'agg' + sFieldName;
      fdAggregate.Expression := 'sum('+sFieldName+')';
      fdAggregate.Active := True;
    end;
  end;

  //Get Value
  cdsTemp.Aggregates.Find('agg'+sFieldName).Value;
end;