2013/06/06

DBGrid scrolling (滾動條事件) 生成

C++ Builder的DBGrid比較麻煩,可以的話還是從Delphi建一個後再使用帶入使用吧。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/* JDBGrid.h文件内容 */
//-------------------------------------------------------------------------
#ifndef JDBGridH
#define JDBGridH
//-------------------------------------------------------------------------
#include <SysUtils.hpp>
#include <Classes.hpp>
#include <Controls.hpp>
#include <DBGrids.hpp>
#include <Grids.hpp>
//-------------------------------------------------------------------------
class PACKAGE TJDBGrid : public TDBGrid
{
  enum TDBGridOption{ dgEditing, dgAlwaysShowEditor,
                      dgTitles, dgIndicator, dgColumnResize,
                      dgColLines, dgRowLines, dgTabs,
                      dgRowSelect, dgAlwaysShowSelection,
                      dgConfirmDelete, dgCancelOnExit,
                      dgMultiSelect ,dgThumbTracking,dgMouseWheel};
typedef Set<TDBGridOption,dgEditing,goThumbTracking>  TDBGridOptions;
  private:
    TDBGridOptions FOptions;
  protected:
    TWndMethod DBGridProc;
    TWndMethod DBInplaceEditProc;
    virtual void __fastcall JDBInplaceEditProc(Messages::TMessage &Message);
    virtual void __fastcall JDBGridProc(Messages::TMessage& Message);
    virtual Grids::TInplaceEdit* __fastcall CreateEditor(void);
    void __fastcall SetOptions(TDBGridOptions Value);
  public:
    __fastcall TJDBGrid(TComponent* Owner);
    __published:
    __property TDBGridOptions Options = {read=FOptions, write=SetOptions,default=27901};
};
//-------------------------------------------------------------------------
#endif

/* JDBGrid.cpp文件内容 */
//-------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "JDBGrid.h"
#pragma package(smart_init)
//-------------------------------------------------------------------------
static inline void ValidCtrCheck(TJDBGrid *)
{
  new TJDBGrid(NULL);
}
//-------------------------------------------------------------------------
fastcall TJDBGrid::TJDBGrid(TComponent* Owner):TDBGrid(Owner)
{
  short *shortTmp;
  Dbgrids::TDBGridOptions optionsTmp;
  (void*)shortTmp=(void*)&FOptions;
  *shortTmp=27901;
  (void*)shortTmp=(void*)&optionsTmp;
  *shortTmp=3325;
  TDBGrid::Options=optionsTmp;
  DBGridProc=WindowProc;
  WindowProc=JDBGridProc;
}
//-------------------------------------------------------------------------
void __fastcall TJDBGrid::SetOptions(TDBGridOptions Value)
{
   short *pValue,*pOp,*pFOp,saveValue;
   Dbgrids::TDBGridOptions DBGridOptions;
   (void*)pValue=(void*)&Value;
   (void*)pOp=(void*)&DBGridOptions;
   (void*)pFOp=(void*)&FOptions;
   *pOp=*pValue & 0x1fff;
   TDBGrid::Options=DBGridOptions;
   DBGridOptions=TDBGrid::Options;
   *pFOp=*pOp | (*pValue & 0x6000);
}

//-------------------------------------------------------------------------
void __fastcall TJDBGrid::JDBInplaceEditProc(Messages::TMessage &Message)
{
   switch (Message.Msg)
   {
      case WM_MOUSEWHEEL:
         if(Options.Contains(dgMouseWheel))
         {
            if (DataLink->Active)
            {
               if (Message.WParam<0)
               {
                if (DataLink->DataSet->RecNo<DataLink->DataSet->RecordCount)
                DataLink->DataSet->RecNo++;
               }
               else
               {
                  if (DataLink->DataSet->RecNo>1)
                     DataLink->DataSet->RecNo--;
               }
            }
            if (!Options.Contains(dgAlwaysShowEditor))
               EditorMode=false;
            return;
         }
      break;
   }
   DBInplaceEditProc(Message);
}
//-------------------------------------------------------------------------
Grids::TInplaceEdit* __fastcall TJDBGrid::CreateEditor(void)
{
   TInplaceEdit *tmp;
   tmp=TDBGrid::CreateEditor();
   DBInplaceEditProc=tmp->WindowProc;
   tmp->WindowProc=JDBInplaceEditProc;
   return tmp;
}
//-------------------------------------------------------------------------
void __fastcall TJDBGrid::JDBGridProc(Messages::TMessage& Message)
{
   switch (Message.Msg)
   {
      case WM_VSCROLL:
      case WM_HSCROLL:
         if (Options.Contains(dgThumbTracking))
            if (Message.WParamLo==5)
            {
               DBGridProc(Message);
               Message.WParamLo=4;
               DBGridProc(Message);
               Message.WParam=8;
            }
         break;
      case WM_MOUSEWHEEL:
         if(Options.Contains(dgMouseWheel))
         {
            DBGridProc(Message);
            if (DataLink->Active)
            {
               if (Message.WParam<0)
               {
                 if (DataLink->DataSet->RecNo<DataLink->DataSet->RecordCount)
                   DataLink->DataSet->RecNo++;
               }
               else
               {
                 if (DataLink->DataSet->RecNo>1)
                   DataLink->DataSet->RecNo--;
               }
            }
            if (!Options.Contains(dgAlwaysShowEditor))
              EditorMode=false;
            return;
         }
      break;
   }
   DBGridProc(Message);
}
//-------------------------------------------------------------------------
namespace Jdbgrid
{
        void __fastcall PACKAGE Register()
        {
                 TComponentClass classes[1] = {__classid(TJDBGrid)};
                 RegisterComponents("Samples", classes, 0);
        }
}
//-------------------------------------------------------------------------


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/* 技巧三,行列不同颜色时,交叉部位颜色的设置。
   如图:在交叉部位使用行列颜色的alpha混合。
   响应TDBGrid的OnDrawColumnCell事件如下:*/
void __fastcall TForm1::JDBGrid1DrawColumnCell(TObject *Sender,
      const TRect &Rect, int DataCol, TColumn *Column,
      TGridDrawState State)
{
  int recordNo,tmpInt;
  if (JDBGrid1->DataSource->DataSet->Active)
  {
    recordNo=DataCol=JDBGrid1->DataSource->DataSet->RecNo % 10;
    if (recordNo ==0 || recordNo>5 )
       JDBGrid1->Canvas->Brush->Color=0xb0b0ff;
    else
       JDBGrid1->Canvas->Brush->Color=0xffe0e0;
    tmpInt=Column->Color;
    if (tmpInt!=clWindow)
    {
        int R1,G1,B1,R2,G2,B2,rgbTmp;
        int alpha=60;
        rgbTmp=JDBGrid1->Canvas->Brush->Color;
        R1=tmpInt & 0xff;
        G1=(tmpInt & 0xff00)>>8;
        B1=(tmpInt & 0xff0000)>>16;
        R2=rgbTmp & 0xff;
        G2=(rgbTmp & 0xff00)>>8;
        B2=(rgbTmp & 0xff0000)>>16;
        R1=(R1*alpha+R2*(256-alpha))>>8;
        G1=(G1*alpha+G2*(256-alpha))>>8;
        B1=(B1*alpha+B2*(256-alpha))>>8;
        JDBGrid1->Canvas->Brush->Color=(TColor)(R1+(G1<<8)+(B1<<16));
    }
    JDBGrid1->Canvas->FillRect(Rect);
  }
  JDBGrid1->DefaultDrawColumnCell(Rect,DataCol,Column,State);
}


Delphi就相當簡單,直接繼承就可以用了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
uses Message;
type
  TDBGrid = class(DBGrids.TDBGrid)
  private
    procedure WmVScroll(var Message: TWMVScroll); message WM_VSCROLL;
  end;

  TForm1 = class(TForm)
    DBGrid1: TDBGrid;
    ..

implementation

procedure TDBGrid.WmVScroll(var Message: TWMVScroll);
begin
  if Message.ScrollCode = SB_THUMBTRACK then
    Message.ScrollCode := SB_THUMBPOSITION;
  inherited;
end;


如此一來,DBGrid的滾動條在拉動的過程中,DataSet的RecNo也會跟著一起調整,十分好用的小技巧。


參考來源:

TDBGRID一些小技巧
Delphi dbgrid continuous scrolling
Winapi.Messages.TWMVScroll

2013/05/30

Enabling Virtual Space mode use Notepad++

Delphi有個很好用的編輯功能:

我不會解釋……就是不管上下行字數有無相同,在按了上下鍵後游標都會在同一個位置上下跑。

我一直以為好用的Editor都應該會有這個功能,後來才發現……Notepad++沒有!

之後好久好久終於在Visual Studio看到了這個功能,中文名詞是:
啟用虛擬空間
英文是"Enabling Virtual Space mode"

後來在這個討論串中找到解法,節錄內容如下:
Sort of a hack (sends a window message directly to the Scintilla edit control on startup), but works great:
  1. Install NppExec plugin
  2. Go to Plugins -> NppExec -> Execute
  3. Enter the following code:
    // ensure console stays hidden
    NPP_CONSOLE 0
    
    // enable virtual spaces (cursor past end of line) outside column edit mode
    SCI_SENDMSG 2596 3 0
    
    // SCI_SENDMSG == send message to Scintilla edit control
    // 2596 == the message we're sending is SCI_SETVIRTUALSPACEOPTIONS
    // 3 == send the value (SCVS_RECTANGULARSELECTION | SCVS_USERACCESSIBLE)
    // the default value is 1 (just SCVS_RECTANGULARSELECTION) for
    //   virtual spaces in column select mode only
    // you can find these values by poking around the source code a bit, or
    // see http://www.scintilla.org/ScintillaDoc.html
    
  4. Click the Save button at the bottom, and give the script a name
  5. Now go to Plugins -> NppExec -> Advanced Options
  6. On the right, under "Execute this script when Notepad++ starts", select the script name you just saved
  7. Click OK, close/reopen Notepad++, and enjoy ;)

這方面還是外國人厲害啊!讚!

2013/05/29

NotePad++ for MetaEditor

『Expert advisor』は、おもしろい!: Notepad++ de MQL4: 『 秀丸エディタをMQL4エディタ(MetaEditor)にする方法 』と言う記事を読んで、面白そうだったのでNotepad++でやってみました。実施範囲は、自動補完・ハイライト・コンパイル作業です。 初めに Notepad++とは notepad++の導入方法な...


很實用的教學  Notepad++也可以寫+編譯MQL4的檔案了!

整理一下資料,方便自己以後使用:

2013/05/28

Thread開發的三兩事

Thread,顧名思義,是很細的線,千萬不要讓大象踩上去。

讓貓自己去玩Thread,必要的時侯請先保護好自己的東西(Synchronize,TCriticalSection,Lock...etc),
再拿不會壞的玩具(Base Type)和牠玩一下下。

玩太久人(UI)會呆掉,所以主要原則就是讓牠自己玩。

2013/04/17

自製懶人版的DbxDAC

因為SQLClientDataSet已經作廢,取而代之的SimpleDataSet 又搞不清那神奇的「InternalConnection」和詭異的「InternalDataSet」……

今天突發奇想,如果我自己做一個DbxDataSet呢?
製作上很簡單,底下來幾張簡圖:
一、新增VCL專案
二、新增Frame
Delphi XE的物件寶庫,選擇Frame
三、把Dbexpress(ADO亦可)相依的元件放進來並作好綁定工作
記得要把 DataSetProvider.Option的poAllowCommandText 設 True
DataSetProvider.Option的poAllowCommandText 設 True,這樣就可以直接對ClientDataSet下SQL指令。

為了讓它更像DataSet,所以我寫了以下的Code:
unit DbxDACUnit;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, FMTBcd, DBClient, Provider, DB, SqlExpr;

type
  TDbxDataSet = class(TFrame)
    qy: TSQLQuery;
    dsp: TDataSetProvider;
    cds: TClientDataSet;
    procedure cdsAfterPost(DataSet: TDataSet);
    procedure cdsAfterDelete(DataSet: TDataSet);
    procedure cdsAfterCancel(DataSet: TDataSet);
  private
    function GetSQLConnection: TSQLConnection;
    procedure SetSQLConnection(const Value: TSQLConnection);
    procedure ApplyUpdates(DataSet: TDataSet);
    { Private declarations }
  public
    { Public declarations }
    property SQLConnection: TSQLConnection read GetSQLConnection write SetSQLConnection;
  end;

implementation

{$R *.dfm}

{ TDbxDataSet }

procedure TDbxDataSet.ApplyUpdates(DataSet: TDataSet);
begin
  cds.ApplyUpdates(0);
end;

procedure TDbxDataSet.cdsAfterCancel(DataSet: TDataSet);
begin
  cds.CancelUpdates();
end;

procedure TDbxDataSet.cdsAfterDelete(DataSet: TDataSet);
begin
  ApplyUpdates(DataSet);
end;

procedure TDbxDataSet.cdsAfterPost(DataSet: TDataSet);
begin
  ApplyUpdates(DataSet);
end;

function TDbxDataSet.GetSQLConnection: TSQLConnection;
begin
  Result := qy.SQLConnection;
end;

procedure TDbxDataSet.SetSQLConnection(const Value: TSQLConnection);
begin
  qy.SQLConnection := Value;
end;

end.

如此一來,在實作上就可以簡單用以下代碼:
procedure TForm1.Button1Click(Sender: TObject);
begin
  SQLConnection1.Open();
  DbxDataSet1.SQLConnection := SQLConnection1;
  DbxDataSet1.cds.CommandText := 'SELECT * FROM CDS ';
  DbxDataSet1.cds.Open();
end;

以上就是突發奇想的DbxDAC
如果有什麼想法和意見,請盡情地反饋一下吧!

2013/04/12

Devart Dbexpress driver for SQLite + Run-time created Encrypt Database

還記得這個網址嗎?
ID: 18385, SQLite DbExpress driver
當時可是使用內附的Demo時就悲劇了……

2008~2009年時我曾經有拿它來和Firebird作比較
當 C++ Builder 遇上 Firebird
關於dbExpress + SQLite3 怎麼用?



但事實上因為Bug無比多,所以我後來就放棄了Dbexpress + SQLite的方式


一直到Devart的出現才讓我又燃起了使用SQLite的慾望
本次介紹的主角--Devart Dbexpress driver for SQLite (*Photo from Devart)


為什麼要使用SQLite呢?

從綠色的羽毛就可以看出它的輕薄
答案很簡單,因為它能夠在不同的平台上被讀取、運作

iOS、Android上最常被用到的單機資料庫就是SQLite,如果同樣的資料庫架構可以輕鬆移轉到其它的平台上,那是多切愜意的事啊!

那麼,Devart這次要來變什麼魔術呢?

安裝步驟實在太簡單了,我就不再做介紹了

而企業上使用最常見的大概就是加密了,雖然解開後也沒什麼資料,但老闆們就是愛這一味……

而且沒問題的Demo重玩就沒意思了,哈!

所以我們就來介紹Devart的加密資料庫建立及資料表的使用吧

資料庫的建立?對,你沒聽錯,Devart的Dbexpress for SQLite driver是讓Dbexpress擁有自行建立資料庫的強大實力

首先我們要來打開Devart自帶的Demos:「%ProgramsFiles%\Devart\Dbx\SQLite\Demos\Win32\SimpleDataSet」

接下來就是設定SQLConnection.Params
參考Readme.htm的內容,我們得出以下的程式碼:
procedure TfmMain.edDatabaseExit(Sender: TObject);
begin
  if edDatabase.Text <> '' then
  begin
    SQLConnection.ConnectionName := 'Devart SQLite Direct';
    SQLConnection.DriverName := 'DevartSQLiteDirect';
    SQLConnection.LibraryName := 'dbexpsqlite40.dll';
    SQLConnection.GetDriverFunc := 'getSQLDriverSQLiteDirect';
    SQLConnection.Params.Clear;
    SQLConnection.Params.Add('DataBase='+edDatabase.Text);
    //存在時開啟,否則建立加密資料庫
    if FileExists(edDatabase.Text) then
    begin
      SQLConnection.Params.Add('ForceCreateDatabase=False');
      SQLConnection.Params.Add('EncryptionAlgorithm=Blowfish');
      SQLConnection.Params.Add('EncryptionKey=encryption key');
      SQLConnection.Params.Add('NewEncryptionKey=');
    end
    else
    begin
      SQLConnection.Params.Add('ForceCreateDatabase=True');
      SQLConnection.Params.Add('EncryptionAlgorithm=Blowfish');
      SQLConnection.Params.Add('EncryptionKey=');
      SQLConnection.Params.Add('NewEncryptionKey=encryption key');
    end;
  end
  else
    SQLConnection.Params.Values['Database'] := ' ';
end;

Project->Run後指定要建立的資料庫路徑及檔名,最後再按下「Open」,就建好囉!
按下「Open」鍵後,就可以看到資料庫建好而且也有Demo資料了


可是在Design Mode時想連結剛剛建立的加密資料庫,則會出現以下的錯誤視窗,我想可能之後的版本會改進吧。但目前只要Run-Time時期可以連就可以了
Design mode Error when TSQLConnection connect Encrypt database

以上就是 Devart Dbexpress driver for SQLite 的介紹,是不是也和我一樣覺得簡單又強大呢!

快去體驗Devart的Power吧!

2013/04/01

設定Double click間隔時間

procedure TForm1.Button1Click(Sender: TObject);
begin
  SetDoubleClickTime(1500);
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  ShowMessage(IntToStr(GetDoubleClickTime));
end;


資料來源:Get/set the doubleclick time