2012년 10월 30일 화요일

서브클래싱

http://code.p-ark.co.kr/trackback/59 에서 갖어왔습니다.
1. 일반적으로 메세지 가로채기를 하려면 WinProc에 콜백을 걸어서 하는데
var
  Form1: TForm1; OldWindProc: Integer;
implementation
{$R *.dfm}
function NewWinProc(hWnd:HWND;Msg:Integer;wParam:Integer;lParam:Integer):LONGINT; StdCall;
var rect: TRect;
begin
  case Msg of
    WM_SIZE : begin
      GetClientRect(hWnd, rect); with rect do Form1.lst1.Items.Add('left:'
        +IntToStr(Left)+'top:' +IntToStr(Top)+'right:'+IntToStr(rect)
        +'bottom:'+IntToStr(rect));
      Form1.lst1.ItemIndex := Form1.lst1.Items.Count + 1; end; end;
  Result:=CallWindowProc(Pointer(OldWindProc),hWnd,Msg,wParam,lParam)
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  OldWindProc:=SetWindowLong(Form1.handle,GWL_WNDPROC,LongInt(@NewWinProc));
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
  SetWindowLong(Form1.handle, GWL_WNDPROC, OldWindProc);
end;
2. 클래스 내부에 포함 할때는 다른 방법으로 합니다. 아래와 같이 글로벌 영역에서는
unit Unit1;
interface
uses
  Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,Dialogs,StdCtrls;
type
  TForm1 = class(TForm)
    lst1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FTargetWnd: THandle; //서브클래싱 할 핸들 보관
    FOldProc: Pointer; //원본 윈도우프로시져 보관
    FNewProc: Pointer; //재구성 윈도우프로시져
  public
    procedure SubClassCreate(hWnd: THandle);
    procedure SubClassWndproc(var Message: TMessage);
  end;
var
  Form1: TForm1;
implementation
{$R *.dfm}
// 서브 클래싱 걸기
procedure TForm1.SubClassCreate(hWnd: THandle);
begin
  if FTargetWnd <> 0 then Exit;
  FTargetWnd := hWnd;
  FOldProc := Pointer(GetWindowLong(FTargetWnd, GWL_WNDPROC));
  FNewProc := MakeObjectInstance(SubClassWndproc);
  SetWindowLong(FTargetWnd, GWL_WNDPROC, LongInt(FNewProc));
end;
// 서브클래싱 윈도우프로시져 재구성
procedure TForm1.SubClassWndproc(var Message: TMessage);
begin
  with Message do begin //Message 객체의 내부 메서드들로 윈도우 프로시져 원본을 돌려줌
    Result:=CallWindowProc(FOldProc,FTargetWnd,Msg,WParam,LParam);
    Case Msg of // Message 객체의 WM_SIZE 메세지를 받아옴
      WM_SIZE : begin
        lst1.Items.Add(Format('%d,%d',[LOWORD(Message.LParam),HiWord(Message.LParam)]));
        lst1.ItemIndex := lst1.Items.Count - 1;
    end;
  end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
  //서브클래싱을 걸어 준다.예제 이므로 현재 윈도우의 핸들에
  SubClassCreate(Self.Handle);
end;
//서브클래싱 해제
procedure TForm1.FormDestroy(Sender: TObject);
begin
  SetWindowLong(FTargetWnd, GWL_WNDPROC, LongInt(FOldProc));
end;
end. 

MDI 관련한 팁모음

SDI 로만 작업해 오다 MDI 가 필요하여 웹서핑하여 자료를 모아 보도록 하겠습니다.

1. MDI 폼에서 SDI 가 크라이언트 영역을 벗어나면 자동으로 스크롤 바가 생깁니다. 이 스크롤 바를 없애고 싶으면
- 메인폼에
function ClientWindowProc(wnd: HWND; msg: Cardinal; wparam, lparam: Integer): Integer; stdcall;
var
  f: Pointer;
Begin
  f := Pointer(GetWindowLong(wnd, GWL_USERDATA));
  case msg of
    WM_NCCALCSIZE: begin
        if (GetWindowLong(wnd, GWL_STYLE) and (WS_HSCROLL or WS_VSCROLL)) <> 0 Then
            SetWindowLong(wnd, GWL_STYLE, GetWindowLong(wnd, GWL_STYLE) and not(WS_HSCROLL or WS_VSCROLL));
      end;
  end;
  Result := CallWindowProc(f, wnd, msg, wparam, lparam);
end;
- 폼 생성 시
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  if ClientHandle <> 0 Then begin
    if GetWindowLong(ClientHandle, GWL_USERDATA) <> 0 then Exit;
    SetWindowLong(ClientHandle, GWL_USERDATA, SetWindowLong(ClientHandle, GWL_WNDPROC, Integer(@ClientWindowProc)));
  end;
end;

2. 차일드폼이 미니마이즈 될때 엠디아이 크라이언트 영역 아래쪽에 네모로 보입니다. 이게  보기 싫어 안보이게 하려면..
- 베이직 폼을 하나 만들고
type
  TfrmBase = class(TForm)
  private
    { Private declarations }
  public
    procedure WMSize(var M: TWMSIZE); message WM_Size;
  end;

- 구현(Implementation) 영역에
procedure TfrmBase.WMSize(var M: TWMSIZE);
begin
  if M.SizeType = Size_Minimized then begin
    ShowWindow(Handle, Sw_Hide);
    M.Result := 0;
  end
  else inherited;
end;
- 차일드 폼이 이 베이직 폼을 승계 하면 됩니다.
   TfrmChildForm = class(TfrmBase)  // 그냥 만든 후 수정해주어도 됩니다.
  private
  public
  end;

2012년 10월 28일 일요일

인터페이스(Interface) 이해(1)


델파이는 XE2 까지 오면서 몇번의 큰 변화가 있었다고 합니다. 그중 하나가 Interface 라고 하네요. 그런데 에전의 제 수준에서 인터페이스를 이해하기가 참 어렵더라고요.
그도 그럴것이 인터페이스를 처음 접하게 된게 COM이었기에, COM도 어려운데..
2번에 걸쳐 델파이의 인터페이스를 이해해 보도록 하겠습니다.
인터페이스를 사용하는 목적은 메모리 절약(?), 다중상속, COM 지원이 가능해 집니다.
우선 클래스만으로 구현 해보도록하겠습니다.

1. 먼저 콘솔 프로젝트를 생성합니다.
2. 유니트를 하나 추가 하고 클래스를 선언합니다.
unit Unit2;
interface
type
  TMyClass=class
    fQuestion: string;
  public
    function GetQuestion: string;
    procedure SetQuestion(const Value: string);
    function Answer: string;
    property Question: string read GetQuestion write SetQuestion;
  end;
implementation
{ TMyClass }
function TMyClass.Answer: string;
begin
  Result:= 'Your Question is ' + FQuestion + '.';
end;
function TMyClass.GetQuestion: string;
begin
  Result:= fQuestion;
end;
procedure TMyClass.SetQuestion(const Value: string);
begin
  FQuestion:= Value;
end;
end.

3. 유니트 하나를 다시 추가하고 객체 생성과 소멸을 하게 합니다.
unit Unit1;
interface
uses Unit2;
function CreateObj: TMyClass;
procedure FreeObj(Obj: TMyClass);
implementation
function CreateObj: TMyClass;
begin
  Result:= TMyClass.Create;
end;
procedure FreeObj(Obj: TMyClass);
begin
  Obj.Free;
end;
end.

4. 프로젝트에서 결과를 구현합니다.
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils,
  Unit2 in 'Unit2.pas',
  Unit1 in 'Unit1.pas',
var
  MyObj: TMyClass;
begin
  MyObj:= CreateObj;
  MyObj.Question:= 'hello';
  Writeln(MyObj.Answer);
  Readln;
  FreeObj(MyObj)
end.

이상이 우리가 통상 해오던 OOP 코딩입니다.
살펴보면 Project2 의 구현 부분이 Unit2 의 구현 부분에 종속이 되어있습니다.
즉, Unit2 의 개발이 끝나야 Project2를 구현 할 수 있다는 것입니다.

5. 클래스 구현 방식을 약간 바꾸기 위하여 새로운 Unit를 추가합니다.
unit Unit3;
interface
type
  TMyBaseClass=class
  public
    function GetQuestion: string; virtual; abstract;
    procedure SetQuestion(const Value: string); virtual; abstract;
    function Answer: string; virtual; Abstract;
    property Question: string read GetQuestion write SetQuestion;
  end;
implementation
end.
구현 부분이 없고 virtual; abstract; 지시 되어 있습니다.
이렇게 하고

6. Project2 를 아래와 같이 바꾸어 주면
program Project2;
{$APPTYPE CONSOLE}
{$R *.res}
uses
  System.SysUtils,
  // Unit2 in 'Unit2.pas', 구현 부분이 빠집니다.
  Unit1 in 'Unit1.pas',
  Unit3 in 'Unit3.pas';
var
  MyObj: TMyBaseClass;
begin
  MyObj:= CreateObj;
  MyObj.Question:= 'hello';
  Writeln(MyObj.Answer);
  Readln;
  // FreeObj();
  MyObj.Free;
end.
Project2 부분은 Unit2 구현 부분과 독립적으로 코딩이 가능하게 됩니다.

6.  Unit2에는  Uses 에 Unit3을 추가하고
unit Unit2;
interface
uses Unit3;
type
  TMyClass=class(TMyBaseClass)  // 수정됨
    fQuestion: string;
  public
    function GetQuestion: string; override;  // 수정됨
    procedure SetQuestion(const Value: string); override; //수정됨
    function Answer: string; override; // 수정됨
    property Question: string read GetQuestion write SetQuestion;
  end;
implementation
{ TMyClass }
function TMyClass.Answer: string;
begin
  Result:= 'Your Question is ' + FQuestion + '.';
end;
function TMyClass.GetQuestion: string;
begin
  Result:= fQuestion;
end;
procedure TMyClass.SetQuestion(const Value: string);
begin
  FQuestion:= Value;
end;
end.

7. 그러나 Unit1 은 Unit2 에 종속이 되어 있습니다.

2012년 10월 25일 목요일

폼 이름으로 폼 생성하기(한개만)

MDI 또는 SDI 에서 새로운 폼을 생성 할때 폼의 이름으로 생성하면 편리 할 때가 있습니다.

1. 우선 생성 될 폼을  [Project > Options... >  forms] 에서 해당 폼을 Avaiable forms: 에 등록합니다.

2. 해당 폼 Unit 에서
initialization
  RegisterClasses([TfrmChild]);
를 아래 쪽에 추가합니다.

3. 생성되는 폼의 OnClose 에서
procedure TfrmChild.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action:= caFree;
end;

3. 폼을 생성 하는 메인 폼에서
function TfrmMain.CreateChild(iClass: string): Boolean;
var
  iMyFormClass: TFormClass;
  iWorkform: TForm;
  iHnd: THandle;
  idx: integer;
begin
  result := True;
  iHnd := 0;
  for idx := 0 to Screen.FormCount - 1 do begin
    if UpperCase('T' + Screen.Forms[idx].Name) = UpperCase(iClass) then begin
      iHnd := Screen.Forms[idx].Handle;
      Break;
    end;
  end;
  if iHnd = 0 then begin
    iMyFormClass := TFormClass(GetClass(iClass));
    if iMyFormClass <> nil then begin
      iWorkform := iMyFormClass.Create(Application.MainForm)
    end
    else result := False;
  end
  else begin
    if IsIconic(iHnd) then ShowWindow(iHnd, SW_SHOWNORMAL)
    else BringWindowToTop(iHnd);
  end;
end;
을 선언 하여 주고

4. 폼 생성시 frmChild:= TfrmChild.Create(Self); 대신
CreateChild('TFrmChild');
을 사용하여 생성하거나 보여주거나, 최상위 위치로 올려 주면 됩니다.

델파이 기본 교육 YouTube 동영상 강좌 (21개)

링크 연결이 잘 안되네요.. 나중에 수정하겠습니다.

우선 YouTube 에 가셔서 아래 제목으로 검색하세요.

[데브기어] 델파이 기본 교육

1.프로젝트 시작하기
2.프로젝트소스와프로젝트관리
3.Unit 구조와 코드작성1
4.코드작성2(프로시저와함수)
5.Unit 구조와 코드작성3(클래스)
6.개체와컴포넌트사용방법1 
7.개체와 컴포넌트 사용방법2
8.개체와 컴포넌트 사용방법(수동)
9.폼띄우기
10.폼의 속성과 이벤트
11.메뉴 작성 컴포넌트 설명
12.메모장만들기
13.메모장만들기2
14.메모장만들기3(스프래쉬화면과 리포지토리
15.델파이와 DB연결과 dbExpress
17.인사관리(편집과차)
18.부서관리
19.예외(Exception)처리
20.스토어드 프로시저
21.트랜잭션처리(최종)

2012년 10월 23일 화요일

TStringBuilder

2005 버전 부턴가(?) 델파이에도 TStringBuilder라는 클래스가 포함되었다.
당초에 델파이는 string 이라는 Type이 있고 이 타입은 아주 잘 설계되어 있었으며, Integer 나 Char 처럼 문자를 아주 간단하게 사용 할 수 있게 하였으며, 관련 함수들도 엄청 많이 준비되었습니다.

그후, 254 문자 이상을 처리하기 위하여, 기존의 string 을 AnsiString 이라고 바꾸고 새로운 방식의 string 타입을 사용케 하였으며, 포인트 처리를 돕기위하여 Pchar 연산이 보강되었습니다.

또, 델파이가 unicode를 지원하기 위하여 또한번의 변동이 이루어 졌습니다.
유니코드를 지원하기 위하여 기존의 2바이트 문자 처리용 Widestring 을 String으로 하고, 이전 1 바이트 쳬계의 문자 string을 AnsiString으로 바꾸고, 이전의 AnsiString 을 ShortString으로 바꾸었습니다.

극기야, 타언어와의 호환성 또는 속도 개선을 위하여 TStringBuilder라는 클래스가 만들어 졌습니다. 당초 파스칼과 베이직을  제외한 포인터 방식의 언어에서는 String을 기본 타입으로 가지고 있지 못 했기에 델파이와 같은 string 처리를 클래스화 하게 된게 아닌가 생각합니다(틀린 추측일 수 있습니다).

일단 살펴보면 속성은 Capacity, Chars, Length, MaxCapacity  가 있으며,
메소드로는 Append, Appendformat, AppendLine, Clear, CopyTo, Create, Equals, Free,
  Insert, Remove Replace, ToString
등의 유용한 기능이 추가 되었습니다.

사용예)

procedure TForm3.Button2Click(Sender: TObject);
var iSb: TStringBuilder;
begin
  iSb:= TStringBuilder.Create;
  try
    isb.Append('내이름은 %이름입니다.').Append(#13#10).Append('좋은 하루 되세요.');
    isb.Replace('%이름', '이정귀');
    memo1.Text:= isb.ToString;
  finally
    isb.Free;
  end;
end;

Append 사용 방법이 좀 특이하지요. 계속하여 추가할 수 있고, InttoStr 사용하지 않아도 되고...


2012년 10월 22일 월요일

Class Helper

Delphi 2006 부터 추가된 기능으로 개체를 상속하지 않고 클래스를 확장 할 수 있는 방법입니다.
간단한 예제를 위하여
  TEdit.Text 값을 Integer 값으로 불러 올 수 있는 기능을 구현 해 보렵니다.
1. Edit 컨트롤을 2개 올려 놓고 Edit1.text:= '2100'; Edit2.Text:= '3200';을 넣습니다.

2. type  문 가장 상단에 아래와 같은 형식으로 추가합니다.

type
 TMyEditor = class helper for TEdit
    function Value: integer;
  end;


3. Ctrl + Shift + 'C"를 누르면

{ TMyEditor }
function TMyEditor.Value: integer;
begin

end;
이 만들어 집니다. begin end 사이에 아래 문장을 추가합니다.

  if text <> '' then
    result:= StrtoIntdef(text,0);

4. 그리고 버튼을 하나 올려 놓고 아래와 같이 추가합니다.

procedure TForm3.Button1Click(Sender: TObject);
begin
  ShowMessageFmt('%d',[Edit1.Value + Edit2.Value]);
end;

5. TEdit 에 Value 라는 메소드가 추가 되었고, 디자인 타임에서도 적용 됨을 알 수 있습니다.
6. 다른 폼에서도 interface 의 uses 에 추가만 해준다면 그곳에서도 사용이 가능합니다.

7. Xe3 버전 부터는 Record Helper 라는 것이 추가 되었네요.


2012년 10월 19일 금요일

제네릭 이해를 돕기 위한 간단한 사용(TDictionary)

Generics 를 이해하기 위하여 Generics.collections 에 있는 클래스를 사용하면서 익히고 있습니다.
TDictionary 는 사전이라는 의미로 보면 좋겠습니다. 표제어(keys)가 있고 그 내용 설명(Values)이 있는 형식이요. 아래그림으로 이해에 도움이되시기를...
선언하고
var
  Form3: TForm3;
  iDictionary: TDictionary<string, integer>;
implementation

{$R *.dfm}

임의의 값으로 채워 출력하고
procedure TForm3.Button1Click(Sender: TObject);
var
  idx: integer;
  iStr: string;
  iKey: string;
  iValue: integer;
begin
  for idx := 0 to 19 do begin
    iValue := Random(65000);
    iKey := 'Key' + IntToStr(iValue);
    // if not iDictionary.ContainsKey(iKey) then iDictionary.Add(iKey, iValue);
    iDictionary.AddOrSetValue(iKey, iValue);
  end;
  for iStr in iDictionary.Keys do begin
    Memo1.Lines.Add(iStr + '=' + IntToStr(iDictionary.Items[iStr]));
  end;
end;

소팅하고 결과를 출력(소팅이 약간 이상하다?)
procedure TForm3.Button2Click(Sender: TObject);
var
  iStr: string;
  iArray: Tarray<string>;
begin
  iArray := iDictionary.Keys.ToArray;
  Tarray.Sort<string>(iArray);
  memo1.Lines.Add('Sorted');
  for iStr in iArray do begin  // iArray 임에 주의
    Memo1.Lines.Add(iStr+'='+IntToStr(iDictionary.Items[iStr]));
  end;
end;

생성하고 해제하고
procedure TForm3.FormCreate(Sender: TObject);
begin
  iDictionary := TDictionary<string, integer>.Create;
end;

procedure TForm3.FormDestroy(Sender: TObject);
begin
  iDictionary.Free;
end;

2012년 10월 18일 목요일

제네릭 이해를 돕기 위한 간단한 사용(TList)


우선 얼마전에  MyRecord 를 TList에 구겨 넣었던 것을 기억하실 것입니다.
그때 선언 방법과 비교해 보세요..
  TMyClass = class
    no: integer;
    fname: string;
    lname: string;
    phone: string;
    addr: string;
  private
    function GetName: string;
  public
    property name: string read GetName;
    constructor create(ino:integer; ifname,ilname,iphone,iaddr: string);
  end;
OOP는 여기에서 처럼 name을 얻을 수 있다는 것이 묘미가 아닐까요.
데이터와 메소드를 갖이 가지고 있으니까요?

1. 우선 객체(인스턴스)를 선언 하여야 겠지요.
  private
    MyList: TList<TMyClass>;
이게 제네릭문법입니다. TList<T>라는 클라스는 델파이가 만들어 놓았고, 어떠한 타입(형식)의 자료도 처리 할 수 있게 하여 주었습니다.

2. 폼 생성시 인스턴스를 만들고 폼이 닫일때 날려주어야 겠지요.
procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);
var
  idx: TMyClass;
begin
  for idx in MyList do begin
    idx.Free;
  end;
  MyList.Free;
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
  MyList:= TList<TMyClass>.Create;
end;

3. 자료를 TList에 넣습니다.
procedure TForm3.Button1Click(Sender: TObject);
var
  idx: Integer;
  iStrings: Tstrings;
begin
  iStrings:= TstringList.Create;
  try
    for idx := 0 to StrHolder1.Strings.count - 1 do begin
        iStrings.CommaText:= StrHolder1.Strings[idx];
        if istrings.Count < 5 then continue;
        MyList.Add(TMyClass.create( StrToInt(iStrings[0]), iStrings[1], iStrings[2], iStrings[3], iStrings[4]));
    end;
  finally
    iStrings.Free;
  end;
end

여기에 StrHolder 라는 RXLIB 컴포넌트를 사용하였는데 설치해 두변 편리 할 것입니다.
기본 컴포넌트 이외에 많이 설치하는 컴포넌트일 것입니다. 없으면 LIstBox를 폼에 올려 놓고 사용해서 되며, 파싱은 commaText 메소드를 이용합니다. 데이터는
  1,이,정귀,515-5513,광주광역시 이런 형태로 만들어져 있습니다.

4. 넣어진 자료가 정상으로 잘 들어 갔는지 확인하기 위하여
procedure TForm3.Button2Click(Sender: TObject);
var
  idx: TMyClass;
begin
  ListBox2.Items.Clear;
  for idx in MyList do
  begin
    listbox2.Items.Add(idx.GetName)
  end;
end;

5. 당연히
function TMyClass.GetName: string;
begin
  result:= fname + lname;
end;
도 있어야 겠지요...

6. 소팅을 해보겠습니다.
procedure TForm3.Button3Click(Sender: TObject);
begin
  MyList.Sort(TComparer<TMyClass>.Construct(
    function(const Item1, Item2: TMyClass): integer
    begin
      result := CompareText(Item1.fname, Item2.fname); // 다른 내용으로 해도 되겠지요
    end));
end;

7. 당연히 Uses 절에 Generics.defaults, Generics.collections 을 추가합니다.
8. XE2 부턴 가는 Record 에서도 메소드를 포함 할 수 있습니다. Class 와 Record는 계승의 차이만 있나?

type
  {
  pMyRecord = ^MyRecord;
  MyRecord = record
    no: integer;
    fname: string;
    lname: string;
    phone: string;
    addr: string;
    function GetName:string;
  end;
  }

9. GetName 을 구현하고...

function TMyRecord.GetName: string;
begin
  Result:= FName + ' ' + LName;
end;


10. 리스트에 데이터 저장시
procedure TForm3.Button1Click(Sender: TObject);
var
  idx: integer;
  iStrings: Tstrings;
  iRecord: TMyRecord;
begin
  iStrings := TstringList.create;
  try
    for idx := 0 to StrHolder1.Strings.count - 1 do begin
      iStrings.CommaText := StrHolder1.Strings[idx];
      if iStrings.count < 5 then continue;
      New(iRecord);
      iRecord.no:= StrToInt(iStrings[0]);
      iRecord.fname:= iStrings[1];
      iRecord.lname:= iStrings[2];
      iRecord.phone:= iStrings[3];
      iRecord.addr:= iStrings[4];
      iList.Add(iRecord);
    end;
  finally
    iStrings.free;
  end;
end;

11. 내용 확인은 
procedure TForm3.Button2Click(Sender: TObject);
var
  idx: TMyRecord;
begin
  for idx in iList do
    ListBox1.Items.Add(idx.GetName);
end;
하면 됩니다.

제네릭 이해를 돕기 위한 간단한 사용(TCompare)

제네릭에는 TCompare 가 준비 되어 있습니다.

앞의 TArray 에서 소팅을 할때 기본적으로 올림차순으로 하였습니다.

자신만의 소팅을 하기위하여

1. 실제 비교 할 방법을 선언하고

  private
    function iThisCompare(const Left, Right: string): integer;

2. 구현하고

function TForm3.iThisCompare(const Left, Right: string): integer;
begin
  Result:= CompareText(Left,Right);
end;

3. 소팅을 합니다.

procedure TForm3.Button4Click(Sender: TObject);
begin
  TArray.Sort<string>(iArray,TComparer<string>.construct(iThisCompare))
end;

4. XE 버전 부턴가 익명메소드가 가능해 졌습니다.
위의 2에서 3 단계를 한방에 코딩하면

procedure TForm3.Button5Click(Sender: TObject);
begin
  TArray.Sort<string>(iArray,TComparer<string>.construct(
  function(const left, right: string): integer
  begin
    Result:= CompareText(Left, right);
  end
  ));
end;

예전에 TList 에서는 @iCompareName 방법으로 불러 왔었지요.
참고 :   MyList.Sort(@CompareName);




제네릭 이해를 돕기 위한 간단한 사용(TArray)

델파이가 2009 버전부터 Generic을 지원한다고하는데 아마추어 입장에서는 별로 변화를 못 느꼈습니다. 제네릭에 대한 일반적인 것은 도서를 참고하시고, 저는 Generics.Collectons 에 있는 델파이가 만들어 놓은 제네릭 클라스를 살펴보도록 하겠습니다.

당연 최상위는 Tobject 가 있고 그아래 TArray 가 있으며 그로부터 여러가지 클래스등이 있네요. 우선 TArray 를 살펴 보겠습니다. 이것은 class 메소드만으로 이루어 졌다고 보면 됩니다. 그중 중요한 것이 Create, Sort, BinarySearch 입니다.

uses Generics.Collections, Generics.Defaults;
 
{Test the Sort method of TArray}
procedure TForm1.Button1Click(Sender: TObject);
var
     Arr: TArray<string>; //same as array of string
     s: string;
begin
  SetLength(arr, 5);
  arr[0] := 'aaa';
  arr[1] := 'AAA';
  arr[2] := '111';
  arr[3] := '333';
  arr[4] := '222';
 
  TArray.Sort<string>(arr);
  Memo1.Clear;
  for s in arr do Memo1.Lines.Add(s); //111 222 333 AAA aaa
end;
 
{Test TArray's BinarySearch method}
procedure TForm1.Button2Click(Sender: TObject);
var
     Arr: TArray<Integer>; //same as array of Integer
  i,n: Integer;
begin
  SetLength(arr, 5);
  for i := 0 to Length(arr) - 1 do arr[i] := Integer(Sqr(i));
  Memo1.Clear;
  for i := Low(arr) to High(arr) do Memo1.Lines.Add(IntToStr(arr[i]));
     If TArray.BinarySearch<Integer>(arr, 4, n) then ShowMessage(IntToStr(n)); //2, which is the third
     If TArray.BinarySearch<Integer>(arr, 5, n) then ShowMessage(IntToStr(n)); //When not found, can't judge according to the value of n
end;
 
{custom sorter}
procedure TForm1.Button3Click(Sender: TObject);
var
  arr: TArray<Integer>;
  num: Integer;
begin
  SetLength(arr, 5);
  arr[0] := 2;
  arr[1] := 4;
  arr[2] := 3;
  arr[3] := 1;
  arr[4] := 5; 
  TArray.Sort<Integer>(arr, TComparer<Integer>.Construct(
    function (const a,b: Integer): Integer
    begin
      Result := b - a;
    end
  ));
  Memo1.Clear;
  for num in arr do Memo1.Lines.Add(IntToStr(num)); //5 4 3 2 1
end;







2012년 10월 17일 수요일

VirtualTreeView 에 레코드 자료 넣기

VirtualTreeView에 Integer 자료를 넣어 보았는데, 만약 String 자료를 넣고 싶다면

1. 중간 매개자
var
  iPData : PString;  //Integer 일경우에는 PInteger 이었습니다.
2, 데이터 사이즈
NodeDataSize:= SizeOf(String) //Integer일 경우에는 SizeOf(Integer) 이었습니다.
3. 화면 표시
CellText:= iPData^;  //Integer 일경우에는 InttoStr(iPData^); 이었습니다.

레코드 자료도 같은 방법으로 사용하면됩니다.
레코드 선언은

  pMyRecord = ^MyREcord;
  myRecord =record
    no: integer;
    name: string;
    addr: string;
  end;
와 같이하고, 예전에 List에 저장 할 경우에는 New 와 Dispose를 사용하였는데,
VirtualTreeView에서는 자체에서 메모리를 관리하므로 하지 않아도 됩니다.

1. 중간 매개자
var
  iPData: pMyRecord;
2. 데이터 사이즈
NodeDataSize:= SizeOf(myRecord);  //pmyRecord 가 아님
3. 화면 표시
 화면에 표시 할 자료를 선정합니다. 여기서는 3개(no, name, addr) 다 표시합니다.
VTV 의 Header > Column... 을 크릭하여 Column 을 3개 추가 하고
각 Column 의 Text 와 Width 를 (순서: 50) , (이름: 100), (주소:100) 으로 설정합니다.
또한, Header > Options > hoVisible := True 로 합니다. (오브젝트 인스펙터에서).
그러면 VTV 에 해더가 나타납니다.


procedure TForm3.VirtualStringTree1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  TextType: TVSTTextType; var CellText: string);
var iPData: pMyRecord;
begin
  iPData:= Sender.GetNodeData(Node);
  case Column of
     0:  CellText:= IntToStr(iPData.no);
     1:  CellText:= iPData.name;
     2:  CellText:= iPData.name;
  end;
end;

각 걸럼의 CellText 를 Case 문으로 지정하여 주면 됩니다.

VST 의 TreeOptions > MiscOptions > toEditable := True 로 하면 F2 키로 편집이 되는데
다른 노드로 이동하면 원상이 되버립니다.
표시되는 값만 바뀌고 원 데이터는 아래와 같이 수정하여 주어야 합니다.
Events > OnNewText를 더블 크릭하여


procedure TForm3.VirtualStringTree1NewText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex; NewText: string);
var iPData: pMyRecord;
begin
  iPData:= Sender.GetNodeData(Node);
  case Column of
    0: iPData^.no:=  StrToInt(NewText);
    1: iPData^.name:= NewText;
    2: iPData^.addr:= NewText;
  end;
end;
하면 됩니다.

초 간단 버추얼트리뷰 예제 (개념잡기)

VirtualTreeView 는 공개된 컴포넌트 중에서 가장 쓸모 있는 것중 하나라고 할 수 있겠네요.
http://www.soft-gems.net/ 에가서 다운 받아 설치하면 됩니다.

기본 개념을 잡기 위하여 초 간단예제를 만들어 봅니다.
폼에 VirtualStringTree(이하 VST) 와 버튼을 올려 놓습니다.
랜덤 한 숫자 6500개를 생성하여 VST에 집어 넣어 보겠습니다.

1. 데이타는 노드에 집어 넣는데, 이 데이터의 길이를 지정해 줍니다.
  - 여기서는 Integer로 넣으니 Integer 가 되겠네요.
  - VST의 Event 에서 OnGetNodeDataSize를 더블크릭하여
procedure TForm3.VirtualStringTree1GetNodeDataSize(Sender: TBaseVirtualTree; var     NodeDataSize: Integer);
begin
  NodeDataSize:= SizeOf(Integer); // PInteger 가 아닙니다.
end;  

2. VST가 저장된 데이터를 화면에 뿌려 주게 지정합니다.
  - VST의 Event 에서 OnGetText를 더블 크릭하여
procedure TForm3.VirtualStringTree1GetText(Sender: TBaseVirtualTree; Node: PVirtualNode; Column: TColumnIndex;
  TextType: TVSTTextType; var CellText: string);
var iPData: PInteger;
begin
  iPData:= Sender.GetNodeData(Node);
  CellText:= IntToStr(iPData^);
end;

3. 데이터를 VST에 넣습니다.
  - Button을 더블크릭하여
procedure TForm3.Button1Click(Sender: TObject);
var idx: integer;
    iNode: PVirtualNode;
    iPData: PInteger;
begin
  VirtualStringTree1.BeginUpdate;
  for idx := 0 to 65000 do begin
    iNode:= VirtualStringTree1.AddChild(nil);
    iPData:= VirtualStringTree1.GetNodeData(iNode);
    iPData^:= Random(65000);
  end;
  VirtualStringTree1.EndUpdate;
end;

순식간에 자료가 들어가 집니다.

2012년 10월 16일 화요일

TreeView Menu

트리뷰로 메뉴를 구현 해보겠습니다.

폼에 트리뷰와 액션리스트 와 이미지리스트를 올려 놓고 이미지 리스트에 적당한 이미지를 읽어 옵니다.
저는
C:\Users\Public\Documents\RAD Studio 9.0 Samples\Delphi\VCL\ActionBands
에 있는 main.pas 에서 이미지 리스트를 복사해옵니다.


ActionList 에 적당하게 액션을 만듭니다.
그리고 액션을 더블 크릭하여 실행문을 만듭니다.

트리뷰에 올릴 액션을 트리뷰를 더블 크릭하여 추가 합니다.

1. 액션을 액션리스트에 읽어와 트리뷰의 노트와 캡션을 비교하여 일치하면 액션을 DATA에  저장합니다.

2. 트리뷰의 OnChange 에서 선택된 액션이 실행 되게 합니다.

3. 액션이 실행 되면 실행된 액션의 캡션과  트리뷰의 노드의 캡션(text)이 일치하는 노드를 선택되게 합니다.

1 단계를 구현하기 위해서 트리뷰에서 캡션으로 찾는 루틴을 이용합니다( 재귀 호출)
이하 소스 갑니다.

unit MainUnit;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ImgList, Vcl.ComCtrls, Vcl.ActnList;

type
  TfrmMain = class(TForm)
    ActionList1: TActionList;
    acOpen: TAction;
    acSave: TAction;
    acShow: TAction;
    acHide: TAction;
    acExit: TAction;
    TreeView1: TTreeView;
    ImageList1: TImageList;
    procedure FormCreate(Sender: TObject);
    procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
    procedure ActionList1Execute(Action: TBasicAction; var Handled: Boolean);
    procedure acOpenExecute(Sender: TObject);
    procedure acSaveExecute(Sender: TObject);
    procedure acShowExecute(Sender: TObject);
    procedure acHideExecute(Sender: TObject);
    procedure acExitExecute(Sender: TObject);
  private
    iActiveNode: TTreeNode;
    procedure MakeTreeMenu;
    function GetTVNodeByCaption(TV: TTreeView; const iCaption: string): TTreeNode;
    function GetNodeByCaption(iTreeNode: TTreeNode; iCaption: string): TTreeNode;
  public
    { Public declarations }
  end;

var
  frmMain: TfrmMain;

implementation

{$R *.dfm}

procedure TfrmMain.acExitExecute(Sender: TObject);
begin
  close;
end;

procedure TfrmMain.acHideExecute(Sender: TObject);
begin
  caption:= 'Selected menu  is Hide';
end;

procedure TfrmMain.acOpenExecute(Sender: TObject);
begin
  caption:= 'Selected menu  is Open';
end;

procedure TfrmMain.acSaveExecute(Sender: TObject);
begin
  caption:= 'Selected menu  is Save';
end;

procedure TfrmMain.acShowExecute(Sender: TObject);
begin
  caption:= 'Selected menu  is Show';
end;

procedure TfrmMain.ActionList1Execute(Action: TBasicAction; var Handled: Boolean);
var
  iTreeNode: TTreeNode;
begin
  if Action <> acExit then
    if (Action is TAction) then
      if (TreeView1.Selected <> nil)  and (CompareText(TreeView1.Selected.Text, TAction(Action).Caption)= 0) then begin
         iTreeNode:= GetTVNodeByCaption(TreeView1,TAction(Action).Caption);
         if (iTreeNode <> nil)  and (iTreeNode <> iActiveNode) then
            iTreeNode.Selected:= True;
      end;
end;

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  iActiveNode:= nil;
  MakeTreeMenu;
  TreeView1.Items.GetFirstNode.Selected:= True;
end;

function TfrmMain.GetNodeByCaption(iTreeNode: TTreeNode; iCaption: string): TTreeNode;
var
  jTreeNode: TTreeNode;
begin
  Result:= nil;
  jTreeNode:= iTreeNode;
  while jTreeNode<> nil do begin
    if CompareText(jTreeNode.Text, iCaption)=0 then begin
      Result:= jTreeNode;
      Exit;
    end else begin
      jTreeNode:= jTreeNode.getFirstChild;
      while jTreeNode <> nil do begin
        Result:= GetNodeByCaption(jTreeNode, iCaption); // 재귀호출
        if Result <> nil then exit;
        jTreeNode:= jTreeNode.GetNextChild(jTreeNode);
      end;
    end;
  end;

end;

function TfrmMain.GetTVNodeByCaption(TV: TTreeView; const iCaption: string): TTreeNode;
var
  iTreeNode: TTreeNode;
begin
  Result:= nil;
  iTreeNode:= TV.Items.GetFirstNode;
  while iTreeNode <> nil do begin
    Result:= GetNodeByCaption(iTreeNode, iCaption); // 자손이 있으면 그 자손에서
    if Result <> nil then exit;
    iTreeNode:= iTreeNode.getNextSibling;  // 다음 node 레벨에 관계없이
  end;
end;

procedure TfrmMain.MakeTreeMenu;
var
  idx: Integer;
  iTreeNode: TTreeNode;
  iAction: TAction;
begin
  // ActionList 에서 액션을 읽어 옵니다.
  for idx := 0 to ActionList1.ActionCount -1 do begin
    iAction:= (ActionList1.Actions[idx] as TAction);
    if iAction is TAction then begin
      iTreeNode:= GetTVNodeByCaption(TreeView1, iAction.Caption);
      if iTreeNode <> nil then begin
        if not iAction.Visible then
          iTreeNode.Delete
        else
          if iAction.ImageIndex <> -1 then begin
            iTreeNode.ImageIndex:= iAction.ImageIndex;
            iTreeNode.SelectedIndex:= iAction.ImageIndex;
          end;
        iTreeNode.Data:= ActionList1.Actions[idx];
      end;
    end;
  end;
end;

procedure TfrmMain.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
  if (Node <> nil) and (Node <> iActiveNode) then
    if Assigned(Node.Data) and Assigned(TAction(Node.Data).OnExecute) then
      TAction(Node.Data).Execute;
  iActiveNode:= Node;
end;

end.



2012년 10월 15일 월요일

TreeView 에 데이터 저장하기

트리뷰에 캡션을 집어 넣기 위해서는
  TreeView1.Items.add( ParrentNode, '캡션') 이렇게 하면 되고
트리뷰에 Object를 집어 넣기 위해서는
  TreeView1.Items.AddObject( ParrentNode, '캡션', iMyObject) ; 이렇게 하면 됩니다.
현재 선택된  노드의 캡션을  읽어 오려면
var
  iTreeNode: TTreeNode;
begin
  if TreeView1.Selected <> nil then begin
    iTreeNode := TreeView1.Selected;
    Edit1.Text := iTreeNode.Text;
  end;
end;
또는 객체의 데이터를 읽어 오려면
var
  iTreeNode: TTreeNode;
begin
  if TreeView1.Selected <> nil then begin
    iTreeNode := TreeView1.Selected;
    Edit1.Text := TMyObject(iTreeNode.Data).addr;
  end;
end;
하면 됩니다.
그러나 트리뷰에서 가장 중요한 것은 특정 캡션의 자료를 찾는 것입니다.
재귀적 검색 방법에 의해서 찾아 보겠습니다.
function TForm3.GetNodeByCaption(ACurrNode: TTreeNode; const ACaption: string): TtreeNode;
var
  jTreeNode: TTreeNode;
begin
  Result:= nil;
  jTreeNode:= ACurrNode;
  while jTreeNode <> nil do begin
    // edit2.Text:= jTreeNode.Text; 디버깅용
    if CompareText(ACaption, jTreeNode.Text) = 0 then begin
      Result:= jTreeNode;
      exit;
    end else begin
      jTreeNode:= jTreeNode.getFirstChild;
      while jTreeNode <> nil do begin
        Result:= GetNodebyCaption(jTreeNode, ACaption);
        if Result <> nil then exit;
        jTreeNode:= jTreeNode.GetNextChild(jTreeNode);
      end;
    end;
  end;
end;

function TForm3.GetTVNodeByCaption(iTreeView: TTreeView; const ACaption: string): TTreeNode;
var
  iTreeNode : TTreeNode;
begin
  Result:= nil;
  iTreeNode:= iTreeView.Items.GetFirstNode;
  while iTreeNode <> nil do begin
    Result:= GetNodeByCaption(iTreeNode, ACaption);
    if Result <> nil then exit;
    iTreeNode:= iTreeNode.getNextSibling;
  end;
end;
를 먼저 선언 해놓고

procedure TForm3.Button3Click(Sender: TObject);
var
  iTreeNode: TTreeNode;
begin
  iTreeNode:= GetTVNodeByCaption(TreeView1, Edit1.Text);
  if iTreeNode <> nil then begin
     iTreeNode.Selected:= True;
     edit2.Text:= iTreeNode.Text;
  end;
end;
하면 됩니다.

2012년 10월 12일 금요일

리스트 뷰에 데이터 저장하기

리스트 뷰도 리스트 박스와 같이 스트링을 저장하되 여러 컬럼을 보이게 할 수 있습니다.
스트링을 저장하기 위하여는 먼저 컬럼을 생성하여 주고 ViewStyle 을 bsReport로 바꾸고
(이해가 쉽도록), 컬럼을 생성합니다.

var
  iColumn: TListColumn;
begin
  iColumn:= ListView1.Columns.Add;
  iColumn.Caption:= '번호';
  iColumn:= ListView1.Columns.Add;
  icolumn.Caption:= '이름';
  iColumn.Width:= 200;
end;

스트링 값을 저장하기 위하여

var
  iListItem: TListItem;
begin
  iListItem:= ListView1.Items.Add;
  iListItem.Caption:= edit2.Text;
  iListItem.SubItems.Add(Edit1.Text);
end;

와 같이하고, 현재 선택된 리스트의 값을 읽어오기 위하여
var
  iListItem : TListItem;
begin
   if ListView1.Selected <> nil then begin
     iListItem := ListView1.Selected;
     edit1.Text:= iListItem.SubItems[0];
     edit2.Text:= iListItem.Caption;
  end;   
end;

그럼 이전과 같은 방법으로 객체를 저장하여 봅시다.

var iMyObject: TMyObject;
begin
  iMyObject:= TMyObject.Create(StrToInt(Edit1.Text), Edit2.Text);
  ListView1.AddItem(iMyObject.Name, iMyObject);
end;

읽어 오기 위하여 TListItem 의 Caption, Data Property 를 사용합니다.
ListBox 에서는 Strings.Object 를 사용하였던것을 상기 하십시요.

var
  iListView: TListItem;
begin
  if  ListView1.Selected <> nil then begin
     iListView:= ListView1.Items[ListView1.ItemIndex];
     Edit1.Text:= IntToStr(TMyObject(iListView.Data).no);
     Edit2.Text:= TMyObject(iListView.Data).Name;
  end;
end;

Listview.SortTYpe := stText 로 하면 자동 정열이 됩니다.

멀티 컬럼 형식에서 저장하려면 ?

var iMyObject: TMyObject;
  iListItem: TListItem;
begin
  iMyObject:= TMyObject.Create(StrToInt(Edit1.Text), Edit2.Text);
  iListItem:= ListView1.Items.Add;
  iListItem.Caption:= IntToStr(iMyObject.No);
  iListItem.SubItems.AddObject(iMyObject.Name, iMyObject);
end;

과 같이하면 되겠습니다.

이말은 객체의 보여 주는 부분은 iListItem.Caption 과 SubUtems.Add 하고
마지막 보여 지는 컬럼에서 SubItems.AddObject() 하면 되겠습니다.

// 종료시 object를 Free 해주어야 하는데...

LIstBox 에 데이터 저장하기

리스트박스는 보여 지는 리스트이기에 통상 많이 사용합니다.
일반적으로 리스트박스에 스트링을 저장하기 / 읽어오기를 할 수 있습니다.
즉, ListBox1.Items.Add('할머니');
     Edit1.Text:= ListBox1.Items[ListBox1.ItemIndex];
과 같은 방법으로 사용합니다 만, Object 도 저장 할 수 있습니다.

  TMyObject = class
    No: integer;
    Name: string;
    constructor Create(const iNo: integer; iName: string);    
  end;

위와 같은 객체를  저장하기는

var
  iMyObject: TMyObject;
begin
  iMyObject:= TMyObject.Create(StrToInt(Edit1.text), Edit2.Text);
  ListBox1.AddItem(iMyObject.Name, iMyObject);
end;
리스트 박스에는 iMyObject.Name 이 보여지며, 검색하거나 소팅은 기본 적으로 이것으로 할 수 있습니다.

읽어 오기는

var
  iMyObject: TMyObject;
begin
  if ListBox1.ItemIndex <> -1 then begin
     iMyObject:=  TMyObject(ListBox1.Items.Objects[ListBox1.ItemIndex]);
     Edit1.Text:= IntToStr(iMyObject.No);
     Edit2.Text:= iMyObject.Name;
  end;
end;

오브젝트로 TStrings에 저장 하였을 경우에는 해당되는 오브젝트를 지워줘야 합니다.

procedure TForm3.FreeObjects(const Strings: TStrings);
var  idx: integer;
begin
  for idx := 0 to Strings.Count -1 do begin
    Strings.Objects[idx].Free;  
    Strings.Objects[idx]:= nil;
  end;
end;

procedure TForm3.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeObjects(ListBox1.Items);
end;

리스트박스를 폼에 올려 놓고 크기를 줄이고, Visible 을 False 로 한다음
var iStrings: TStrings; 선언을 해주고
iStrings:= ListBox4.Items; 로 지정해 주고 사용하면 편리 할때도 있습니다.
iStrings[idx] 나 iStrings.Objects[idx] 형식으로 사용하면 되기도 하겠지요...

2012년 10월 11일 목요일

ZeosLib 7에서 한글 사용 성공하였습니다.

Zconnection > PrepareSQL = True 로 바꾸어 주면
아래 게시물에서 한글 처리시 오류가 나던 .AsString 이 즉
ZQuery1.ParamByName('ijijum').AsString:= '북광주';
가 정상으로 작동됩니다.

살펴 본바에 의하면 PrepareSQL 이 False 로 되어 있을때  .AsAnsiString로 저장하면
데이터는 QuatedString 으로 바뀌어 저장이 되더군요
즉 "북광주" 가 "'북광주'"로 저장이 됩니다.
한글의 특정 문자로 시작하는 경우 한글2바이트  중 일부 코드에 #34 가 들어 가면 "'" 문자가 있는 것으로 인정하여 문자 변환을 하지 않아 디비에는 "'" 문자가 앞뒤로 들어 가지 않고 저장이 되더군요. 엉망!!!!
ZConnect unit 의 보면 AnisiQuatedStr(Value, #34) 라는 문장이 있습니다.

어떻든 현재 까지 사용해본 결과는 한글 문제가 명쾌하게 해결되었습니다.
많이 사용하세요.

2012년 10월 10일 수요일

XE2(3) 에서 ZeosLib7 사용할때

MySql 4.1을 처음 설치 할때 문자세트를 데폴트로 euckr 로 설정하여 사용하던 데이터 베이스를 ZeosLib 5.5 버전 이하를 사용하여 데이터를 가져오기 위하여 Zconnection 의 Properties 속성에 codepage=euckr 로 지정하여 사용해 오다.

델파이 2010 버전에서 델파이가 Unitcode를 지원한다 하여 Unicode 지원 XeosLib 6.5 등의 버전을 이용하여 역시 같은 방법으로 codepage=euckr로 지정하여 사용 하였다.
물론 Mysql도 unicode를 지원 할 수 있는 5.1 버전으로 바꾸었다.

쿤자 코드 체계를 일원화 하기 위하여 FrontMySql 5.0 버전을 이용하여 디비의 문자 속성을 전부 utf8 로 바꾼 상태에서 데이터를 ZeosLib를 써서 데이터를 가져 오면 부분적으로 한글이 깨어져 읽혀 지더군요. 해서 특정 테이블만 euckr 로 바꾸어서 사용하고 있었다.

ZeosLib7 버전을 다운 받아 설치하여, 예전에 운용하던 프로그램에 적용 하였다,
데이터 읽어오거나 한글 깨어 짐은 없으나, 문자의 길이 쪽에 문제가 발생하여 숫자나 기호를 잘 불러오지 못하는 현상과 ParamByName에 오류가 발생하여 정리하여 봤습니다.

MtSql 서버측    크라이언트측
euckr               euckr : 숫자/기호 오류, ParamByName('').Asstring 은 정상
euckr               utf8 : 정상, ParamByName('').Asstring 오류

utf8                  euckr : 숫자/기호 오류, ParamByName('').Asstring 은 정상
utf8                  utf8 : 정상,  ParamByName('').Asstring 오류 (주1)

주1 방법에서 해결 방법을 찾다가
ZQuery1.ParamByName('ijijum').AsString :=  '북광주' 를
ZQuery1.ParamByName('ijijum').AsString :=  '북광주%' 으로 바꾸어 해결하였으나,
뭔가 찜찜하더군요.

다른 방법을 찾아보다가
  ZQuery1.SQL.Text:= 'select * from cmaster where (ijijum like :ijijum)';
  ZQuery1.ParamByName('ijijum').AsString := edit1.text;

  ZQuery1.ParamByName('ijijum').AsAnsiString := edit1.text;
으로 바꾸어서 해결하였습니다.

ZeosLib의 AsString 에서 문제가 있는 것 처럼 보이더군요.
위 방법은 뭐가 편법인것 같아서 다른 방법을 찾아보다가

  ZQuery1.SQL.Text:= 'select * from carecallmaster where (ijijum like :ijijum)';
  //ZQuery1.ParamByName('ijijum').AsString := edit1.text;
  ZQuery1.Params.ParamByName('ijijum').DataType:= ftString;
  ZQuery1.Params.ParamByName('ijijum').Value:= Edit1.Text;
방법을 사용하였더니 정상이더군요, 참고하세요.

수정합니다: 중요! 소스를 살펴 보다 혹시나 해서 시험해본 결과
Zconnection 에 보면 PrepareSQL 이라는 옵션이 있습니다.
이게 디폴트가 False 인데 이걸 True로 바꾸어 주니 AsString 이 정상으로 작동됩니다.
(ㅎㅎㅎ 이것 때문에 엄청 고민 많이 했는데.........)




데이터

2012년 10월 5일 금요일

XE3 Lite 6.0


이곳에 직접 올리리는 못하였지만 아래 파일을 구굴링해보세요.

Embarcadero Delphi XE3 RTM v17.0.4625.53395 Lite v6.0

386 Mb 정도의 크기이고 설치는 잘되더군요. 헬프 파일이 없으니 평가판의 헬프 폴더를 복사해 오거나 든지 한다음, H2Reg.exe -r 하여 활성화 하면 됩니다.



델파이 Xe2 이상에서는 폼 themes를 적용 할 수 있습니다.




참고 할만한 곳 => http://theroadtodelphi.wordpress.com/2011/09/01/exploring-delphi-xe2-vcl-styles-part-i/





간단하게 적용 시켜 보면
uinit MainUnit;
interface
uses VCL.Themes, VCL.Styles;
implementation
{$R *.dfm}
const sStyleName: array [0..4] of string = ( 'Auric', 'Cyan Night','Silver','Slate Classico','Windows');
procedure TForm2.Button1Click(Sender: TObject);
begin
  if (Sender as TButton).tag = 4 then
    TStyleManager.SetStyle(FBasicStyle)
  else
    TStyleManager.SetStyle(sStyleName[(Sender as TButton).tag]);
end;
procedure TForm2.Button6Click(Sender: TObject);
begin
  if OpenDialog1.Execute then begin
      TStyleManager.SetStyle(TStyleManager.LoadFromFile(OpenDialog1.FileName));
  end;
end;
procedure TForm2.FormCreate(Sender: TObject);
begin
  FBasicStyle:= TStyleManager.ActiveStyle
end;
end.




델파이에서 ZeosLib 사용하기

델파이 버전이 Xe3 가 되면서 ZeosLib를
https://zeoslib.svn.sourceforge.net/svnroot/zeoslib/branches/testing
에서 받은 파일로 설치하였더니 빌드와 설치는 되는데 데이터 값을 읽어 못 읽어 옴다.
그외 다른 기능은 정상인데....

몇가지 이유를 조사하여 보았더니 Xe2 가 Xe3 로 되면서 TwideDataSet 이 없어져버렸습니다. 헬프 파일에서도 안보임다.

그부분을 수정하고도 안되서 살펴보니 GetFieldData SetFieldData 의 Buffer 가 이전 버전에서는 pointer 형식이었는데, Xe3에서는 TValueBuffer 형식으로 바뀌였슴다.

data.db.pas 파일에서 보면

function GetFieldData(Field: TField; Buffer: Pointer): Boolean; overload; virtual;
function GetFieldData(Field: TField; Buffer: TValueBuffer ): Boolean; overload; virtual;

형식으로 바귀였슴다.

아마도 Livebounding 처리를 하려고 했던것 같습슴다.
TValuebuffer = Tarray 이더군요.

아뭇튼 성공하지 못하고 구굴링하여 다른 곳에서 답을 찾았슴다.

http://svn.code.sf.net/p/zeoslib/code-0/trunk

또는
http://svn.code.sf.net/p/zeoslib/code-0/branches/testing

에서 다운 받아 설치하면 됨다.

물론 [Tools > options > Environment Options > Delphi Options > Library >Directory > Library Path: 에
ZeosLib\src\component
ZeosLib\src\core
ZeosLib\src\dbc
ZeosLib\src\parsesql
ZeosLib\src\plain

을 추가하여 주어야 함다.

Object를 TObkectList에 저장(레코드와 비교하면서 사용)

역시 같은 개념이다

  TMyObject=class(TObject)
  private
    FNo: integer;
    FName: string;
  public
    function Calc2x(idx: integer): integer;
    property Name: string read FName write FName;
    property No: integer read FNo write FNo;
  end;

비슷한 형식이지만 자료로의 접근 방식이 조금다르다.

델파이에서는 interface 부분에서 위와같이 선언하고나서 Ctrl+Shift+C 하면 실제 구현(Implementation) 부분에 기본 틀이 만들어 집니다.

Record를 쌓아두는 곳으로 TList가 있었다면
Object 자료를 샇아두는 곳은 TObjectList가 준비되어 있습니다.

procedure TForm2.FormCreate(Sender: TObject);
begin
  MyObjectList:= TObjectList.Create;
end;

물론 uses Contnrs 를 삽이하여야합니다.

실제 자료를 생성하고 자료를 접근하기 위하여는 레코드에서 New를 사용하던 식으로
procedure TForm2.btObjectAddClick(Sender: TObject);
var
  iMyObject: TMyObject;
begin
  iMyObject:= TMyObject.Create;
  iMyObject.Name:= Edit1.Text;
  iMyObject.No:= Random(65000);
  MyObjectList.Add(iMyObject);
end;

하면 됩니다.

사용이 끝난 서재는 간단하게 정리됩니다.
procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(MyObjectList);
end;

또는 MyObjectList.Clear 해도 되겠지요.

자동으로 해제 되지 않게 하기위하여는 
   MyObjectList.OwnsObjects := False; 하면 됩니다. 기정치는 True 입니다.
다른 방법으로는 생성시
  iMyObject:= TMyObject.Create(False); 또는
  iMyObject:= TMyObject.Create(True); 하면 됩니다.

Record를 TList에 저장

데이터를 분류하여 보관하는  측면에서 보면 단순 변수, 배열, 레코드, 오브젝트 형식이 있다.
단순 변수와 배열은 굳이 이야기 할 필요가 없지만 레코드 부터의 자료 관리 방법을 살펴 보면

TMyRecord = record
No: integer; // 책번호
Name: string; // 책의 제목
end;
PMyREcord = ^TMyRecord;

로 자료가 들어갈 형태를 선언해주고, 실제 자료가 들어 갈 수 있는 변수를 만들어 자료를 보관 한다.
Myrecord : ^TMyRecord;
  또는
MyRecord: PMyRecord;
방법을 사용하여 자료를 보관한다.
자료의 안으로의 접근은 
MyRecord.No;  MyRecord.Name; 또는 PMyRecord^.No, PMyRecord^.Name 형식으로 한다.

예를 들어 실제 값을 넣어보자
procedure TForm2.btRecordAddClick(Sender: TObject);
var
iMyRecord: ^TMyRecord; // iMyRecord: PMyRecord
begin
New(iMyRecord);
iMyRecord.No:= Random(65000);
iMyRecord.Name:= Edit1.Text;
MyList.Add(iMyRecord);
end;

위에서 iMyRecord 는 New() 에 의하여 저장 할 메모리를 확보하고 dispose(iMyRecord)에 의하여 메모리를 돌려 준다.

여러개의 자료를 관리하기 쉽게 샇기 위해서는 샇는 선반 같은 곳이 필요하다. 배열을 이용하여  샇아 둘 수도 있겠지만,
델파이는 TList 라는 클래스가 준비 되어 있다.  //TList<T>하고는 다르다.

MyList : TList
MyList:= TList.create;
하여 MyList 라는 만들어져 있는 보관 장소를 이용하면 된다.

TList 에는 Add, Clear, Delete, IndexOf, Move, Sort, Count 등의 멤버가 이미 만들어져 있으니 상속 받아 사용하면 된다.
주의 할 점은 TList는 Clear 하더라도 그곳에 보관 된 New(iMyRecord)로 만들어져 있는 자료의 메모리 영역은 시스템에 반환이 안된다.
자동 반환이 되게 하기 위히여 TObjectList 가 준비 되어 있다.(차후 설명)
아래와 같이 미리 준비하고 필요하면 없애 줄때 각각의 메모리 공간을 시스템에 돌려 준다.

procedure TForm2.FormCreate(Sender: TObject);
begin
MyList:= TList.Create;
end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
for var idx in MyList do //2020년 수정
dispose(idx);
FreeAndNil(MyList); //MyList.free
end;

참고로 소팅을 이용하기 위하여는 익명메소드를 사용하여아래와 같이한다.

procedure TForm2.btRecordSortClick(Sender: TObject);
begin
MyList.SortList(functon(Item1, Item2: Pointer): integer //2020년 수정
begin
Result:= CompareText(PMyRecord(Item1).Name, PMyRecord(Item2).Name,)
end
);
end;

이상 레코드를 이용한  자료 저장 방법을 살펴보았다. 다음은 Object를 레코드 처럼 사용하여 보자.





델파이를 접한지 오래되었으나...

델파이를 접한지 수십년이 되었으나, 그때 그때 이해와 활용 만 하고 넘어 오다보니
점점 나이가 먹어감에 따라 기억도 가물 가물해져서, 필요한 개념과 팁 그리고 기본들이
자꾸 햇갈리고 또 기억이 나지 않아 앞으로는 관련 자료를 모아 놓기로 하였습니다.

이곳에 있는 자료는 정확한 자료가 아닐 수도 있으니, 참고 하실분은 살펴서 참고하여 주십시요.

델파이는 이전에 애플컴퓨터에 CPM 카드를 장착하여 터보파스칼이라는 프로그램 부터 시작하였는데, 제 기억으로는 1989 년 일 것 같습니다.

그때, 메뉴얼은 누렇게 변색이 되어 지금도 책장에 보관되어 있습니다.
그후, 16비트 컴퓨터의 MS도스상에서 터보 파스칼을 돌려 보았고,
볼랜드가 VCL 이라는 개념을 처음 구현하는 과정을 지켜 보았었습니다.

그때 국내에도 메뉴와 창 개념을 텍스트로 구현하는 기술들이 성황이었고,
윈도우 3,1이 도입되면서, 그래픽 환경에 기본 창을 띄울 수 있는 툴들이 나타났고,

볼랜드는 이것을 OOP(Object Oriented Program)라는 개념으로 정립하여 오늘 날의 VCL이
자리 하게 된게 아닌가 생각합니다.

비주얼 베이직과 비슷한 시기에 유행한 델파이는 그후 엄청난 컴파일 속도로 인하여 타의 추종을 불허 할만큼 많은 호응을 얻었던 것으로 기억합니다.

그후, IT에 대한 붐, 특히나 개인이 프로그램을 작성하는 것에 대한 기피 또는 귀찮아함등의
이유로 프로그램은 특별 한 사람이 하고, 일반인은 사용자로 전락하게 되는 시대적 흐름이
되었습니다.

앞으로도 델파이는 컴포넌트 또는 컨트롤이라는 개념으로 인하여 계속하여 명맥을 유지 할 것으로 보입니다.

다만! 아쉰운 점이 시스템이 해를 갈 수록 커지고, 다양한 사용자를 수용하게 하다보니 일부 사용자에게는 진짜로 불필요 할 것 같은 것들이 점점 늘어냐 이젠 비만증상이 오고
프로그램 가격인 너무 높게 책정(특히 국내)되다 보니 오히려 델파이를 접하려는 사람을 차단하기에 이르렀습니다.

비주얼 C#은 무료로 접근 할 수 가 있는 것에 비하면, 델파이는 너무 신규자, 기본 사용자에 차단벽이 너무 높다는 생각입니다.

비즈니스영역에서는 높은 사용료를 받드라도, 그렇지 않은 분야에서는 지금보다 1/10 정도의 비용으로 접근하게하는 것이 오히려 전체적인 eBusiness 면에서는 올바른 정책이 아닌가하는 생각이 들기도 합니다.

델파이 12.1이냐 11.3이냐?

 델파이가 12.1이 나왔습니다. 혹시 11.3버전의 커뮤니티버전이 필요하시는분이 있을 수 있을 것 같아 https://altd.embarcadero.com/.../RADStudio_11_3_esd_28... 와 이것 찾느랴 엄청고생함.