目前分類:BCB 程式開發筆記 (26)

瀏覽方式: 標題列表 簡短摘要
假設你有一個 TreeView 叫做 tvArticle,有個 PopupMenu 叫做 PopupMenu1,你希望用右鍵可以點選 tvArticle 的節點,然後再顯示右鍵選單。
首先你要把 PopupMenu1 的 AutoPupop 屬性設為 false,然後在 tvArticle 的 OnMouseDown 事件加入以下程式碼。

void __fastcall TForm1::tvArticleMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
TPoint p;
GetCursorPos(&p); // 取得游標在螢幕的真實座標
if (Button == mbRight && tvArticle->GetNodeAt(X, Y) != NULL)
{
tvArticle->GetNodeAt(X, Y)->Selected = true;
PopupMenu1->Popup(p.x, p.y); // 顯示右鍵選單
}
}

以上的程式碼會在你選中 TreeNode 時才會出現右鍵選單,若你希望不選中  TreeNode 也能出現選單的話,就改成以下這樣:

void __fastcall TForm1::tvArticleMouseDown(TObject *Sender,
TMouseButton Button, TShiftState Shift, int X, int Y)
{
TPoint p;
GetCursorPos(&p); // 取得游標在螢幕的真實座標
if (Button == mbRight)
{
if (
tvArticle->GetNodeAt(X, Y) != NULL)
tvArticle->GetNodeAt(X, Y)->Selected = true;
PopupMenu1->Popup(p.x, p.y); // 顯示右鍵選單
}
}

Nelson 發表在 痞客邦 留言(0) 人氣()

這一段程式碼是可以取得指定目錄及其子目錄下,所有副檔名為 .url 的檔案 (沒錯,這就是拿來掃瞄 IE 我的最愛),取得的所有檔名會存放在一個名叫 sList 的 StringList 裡頭。



void __fastcall TForm1::LoadFavorite(AnsiString Dir, TStringList *sList) // Dir 就是你要掃瞄的目錄, sList 拿來存放檔名

{

��� WIN32_FIND_DATA filedata;� // Structure for file data

��� HANDLE filehandle;�������� // Handle for searching

��� AnsiString szFileName, szDir;

��� szDir = IncludeTrailingPathDelimiter(Dir);� // 確保最後有反斜線����



��� filehandle = FindFirstFile((szDir + "*.*").c_str(), &filedata);� // 因為我們要包含子目錄,所以要用 *.*,不然直接用 *.url 就行了

��� if (filehandle != INVALID_HANDLE_VALUE)

��� {

������� do

������� {

����������� /* 不處理隱藏檔及 . 跟 .. */

����������� if ((filedata.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) != 0 ||

��������������� strcmp(filedata.cFileName, ".") == 0 ||

��������������� strcmp(filedata.cFileName, "..") == 0)

��������������� continue;

����������� /* 若是資料夾 */

����������� if ((filedata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)

����������� {

��������������� szFileName = szDir + AnsiString(filedata.cFileName);�// 資料夾完整路徑

��������������� Application->ProcessMessages();

��������������� LoadFavorite(szFileName); // 遞迴找下一層目錄

����������� }

����������� else if (ExtractFileExt(filedata.cFileName).LowerCase() == ".url")�// 若找到的檔案的副檔名是 .url

����������� {

��������������� szFileName = szDir + AnsiString(filedata.cFileName);

��������������� sList->Add(szFileName);� // 將完整路徑加到 sList 裡頭

��������������� Application->ProcessMessages();

����������� }

������� } while (FindNextFile(filehandle, &filedata));

������� FindClose(filehandle);

��� }

}


Nelson 發表在 痞客邦 留言(0) 人氣()

我們可以透過 ComponentCount 來掃瞄 Form 底下的所有元件,進而控制這些元件,例如底下這個 sample code 可以把 Form 底下的 Edit 元件一口氣改掉。

for (int i = 0; i < ComponentCount; ++i)

{

� � if (Components[i]->ClassNameIs("TEdit"))

� � � � ((TEdit *)Components[i])->Text = "我被改了";

}



那若是要改某個元件底下的子元件呢? 例如要改 GroupBox 裡頭的所有 Label 要怎辦呢? 這時就要用到 ControlCount 了。

for (int i = 0; i < GroupBox1->ControlCount; ++i)

{

� � if (GroupBox1->Controls[i]->ClassNameIs("TLabel"))

� � � � ((TLabel *)GroupBox1->Controls[i])->Caption = "我被改了";

}


這樣就可以了~


再補上一招,若是要修改名稱有固定規則的元件又要怎麼做呢? 例如要修改 Edit1~Edit10 的文字內容。這時我們可以叫出 FindComponent() 來幫忙

for (int i = 1; i <= 10; ++i)
{
� � ((TEdit *)FindComponent("Edit" + IntToStr(i)))->Text = "我被改了";
}

類似地,也可以用 FindChildControl() 找到子元件

for (int i = 0; i < 10; ++i)
{
��� ((TEdit *)GroupBox1->FindChildControl("Edit" + IntToStr(i)))->Text = "我被改了";
}


戲法人人會變,巧妙各有不同。透過以上三個例子,應該可以滿足大部分需要大量修改元件屬性的需求了。至於怎麼應用,就看個人的心思啦 :)






Nelson 發表在 痞客邦 留言(3) 人氣()

void __fastcall TForm1::ListBox1MouseUp(TObject *Sender,
����� TMouseButton Button, TShiftState Shift, int X, int Y)
{
��� if (Button == mbRight)
��� {
������� ListBox1->ItemIndex = ListBox1->ItemAtPos(Point(X,Y), true);
��� }
}


Nelson 發表在 痞客邦 留言(0) 人氣()

void __fastcall TformMain::CheckInstance(void)
{
��� Application->Title = Caption;
��� HANDLE PrevInstHandle;
��� Mutex = OpenMutex(SYNCHRONIZE, false, Application->Title.c_str());

�� �/* 檢查是否有重複執行 */
��� if (Mutex != NULL)
��� {
�� ��� �/*
�� ��� � *�� �若有重複,把之前的程式帶到前景,
�� ��� � *�� �再關閉本身
�� ��� � */
������� String AppTitle = Application->Title;
������� Application->ShowMainForm = false;
������� SetWindowText(Application->Handle, NULL);
������� PrevInstHandle = FindWindow("TApplication", AppTitle.c_str());
������� if (IsIconic(PrevInstHandle))
����������� ShowWindow(PrevInstHandle, SW_RESTORE);
������� else
������� {
����������� BringWindowToTop(PrevInstHandle);
����������� SetForegroundWindow(PrevInstHandle);
������� }
������� Application->Terminate();
��� }
��� else
������� Mutex = CreateMutex(NULL, false, Application->Title.c_str());
}
//---------------------------------------------------------------------------
void __fastcall TformMain::FormClose(TObject *Sender, TCloseAction &Action)
{
��� ReleaseMutex(Mutex);��� // 記得在結束程式前,要釋放為了避免重複執行而產生的 Mutex
}


Nelson 發表在 痞客邦 留言(1) 人氣()

有時候你可能會需要讓使用者在 Memo 貼上文字之後自動換行。要達到這樣的效果有幾種方法辦得到。第一種是在 OnMouseUp event 裡頭偵測使用者是否按下了 Ctrl+V。不過這方法並不是很好,所以在這裡我要介紹第二種方法。第二種方法是直接偵測送到 MemoWM_PASTE 訊息。



// 在 Unit1.h

private:��� // User declarations

��� TWndMethod OldMemoProc;

��� void __fastcall NewMemoProc(TMessage &Message);





// 在 Unit1.cpp

void __fastcall TForm1::NewMemoProc(TMessage &Message)

{

��� OldMemoProc(Message);

��� if (Message.Msg == WM_PASTE)��� // 若是貼上

������� Memo1->Lines->Add(""); � � �// 就自動換行

}

//---------------------------------------------------------------------------

void __fastcall TForm1::FormCreate(TObject *Sender)

{

��� OldMemoProc = Memo1->WindowProc;

��� Memo1->WindowProc = NewMemoProc;

}




Nelson 發表在 痞客邦 留言(0) 人氣()

【作者】geniustom

【內文】http://delphi.ktop.com.tw/topic.asp?topic_id=59601



一般人看到【取亂數不重複】..很直接的想法就是一直取,每次取完之後再跟之前的比較..

其實這樣的效率極差!取N個不重複的亂數需要做N階乘次比對..

有學過資料結構的應該都知道.在複雜度裡面..N階乘算是【極差】

取亂數可以使用洗牌法..

意思就是說..

1..假設剛買來的牌..有52張..分別照順序排好...

2..你只需要把52張牌全部攤開..隨便選2張交換...

3..交換52次之後..可以做出很平均的亂數處理..

所以..如果要產生A~B不重複的亂數.演算法如下..

var

Nums: array of integer;

i,j,k,temp: integer;

begin

randomize; 灑下亂數種子

setlength(Nums,B-A) //產生B-A張牌

for i := 0 to (B-A)-1 do

Nums[i] := i; //產生一副新牌..都是照順序排好的

for i := 0 to (B-A)-1 do

begin

j := random(B-A+1); //隨便選兩張牌(索引) 取出0~(B-A)的亂數

k := random(B-A+1);

temp := Nums[j]; //交換兩張隨便取的牌

Nums[j] := Nums[k];

Nums[k] := temp;

end;

for i := 0 to (B-A)-1 do

Nums[j]:=Nums[j]+A; //最後..把這邊的排全部變成A~B的值

end;



假如您要取5個..

那只要選Nums[0]~Nums[4]..就是一組很漂亮的亂數..

效率極佳..只需要O(n)次...

而一般人想到那種差勁的演算法..差不多到100..程式就要死當了


Nelson 發表在 痞客邦 留言(2) 人氣()

int MessageBox(const char* Text, const char* Caption, int Flags);

Text : 您要顯示的訊息

Caption : MessageBox 的標題

Flags : 設定要顯示哪些東西



以下是 Flags 的設定值

0 (MB_OK) : OK按鈕

1 (MB_OKCANCEL) : OK, Cancel按鈕

2 (MB_ABORTRETRYIGNORE) : Abort, Retry, Ignore按鈕

3 (MB_YESNOCANCEL) : Yes, No, Cancel按鈕

4 (MB_YESNO) : Yes, No按鈕

5 (MB_RETRYCANCEL) : Retry, Cancel按鈕

16 : 圖示

32 : 圖示

48 : 圖示

64 : 圖示

0 :�� 將第一按鈕設為預設

256 : 將第二按鈕設為預設

512 : 將第三按鈕設為預設


將數字加起來就可以得到你要的樣式了。





以下是它的回傳值,代表按了什麼按鈕,您可根據回傳值做些相應的動作。

1 (IDOK)

2 (IDCANCEL)

3 (IDABORT)

4 (IDRETRY)

5 (IDIGNORE)

6 (IDYES)

7 (IDNO)



//這是多行訊息。


String s = "這是測試1\n";

s += "這是測試2";

Application->MessageBox(s.c_str(), "Test", 32+3);� //最後的Flag可用數字組合.


Nelson 發表在 痞客邦 留言(1) 人氣()

#define NO_WIN32_LEAN_AND_MEAN� // BCB6 一定要有這句, 不然會錯
#include

AnsiString szPreDir = "";
int __stdcall BrowseProc(HWND hwnd,UINT uMsg, LPARAM lParam, LPARAM lpData )
{
��� char szDir[MAX_PATH];

Nelson 發表在 痞客邦 留言(0) 人氣()



1 void __fastcall TForm1::FormCreate(TObject *Sender)
2 {
3 DWORD dwVerInfoSize = 0;
4 AnsiString szFile = Application->ExeName;
5 dwVerInfoSize = GetFileVersionInfoSize(szFile.c_str(), &dwVerInfoSize);
6 BYTE *bVerInfoBuf = new BYTE[dwVerInfoSize];
7 if (GetFileVersionInfo(szFile.c_str(), 0, dwVerInfoSize, bVerInfoBuf))
8 {
9 VS_FIXEDFILEINFO *vsInfo;
10 UINT vsInfoSize;
11 if (VerQueryValue(bVerInfoBuf, "\\", (void**)&vsInfo, &vsInfoSize))
12 {
13 int iFileVerMajor = HIWORD(vsInfo->dwFileVersionMS);
14 int iFileVerMinor = LOWORD(vsInfo->dwFileVersionMS);
15 int iFileVerRelease = HIWORD(vsInfo->dwFileVersionLS);
16 int iFileVerBuild = LOWORD(vsInfo->dwFileVersionLS);
17 Caption = IntToStr(iFileVerMajor) + "." + IntToStr(iFileVerMinor) +
18 "." + IntToStr(iFileVerRelease) + "." + IntToStr(iFileVerBuild);
19 }
20 }
21 delete bVerInfoBuf;
22 }



更多詳情請看 http://delphi.ktop.com.tw/topic.asp?TOPIC_ID=34575

Nelson 發表在 痞客邦 留言(0) 人氣()


1 void "#0000FF">__fastcall TForm1::ListBox1DrawItem(TWinControl *Control, int Index,
2 TRect &Rect, TOwnerDrawState State)
3 {
4 if(State.Contains(odSelected))
5 {
6 ListBox1->Canvas->Brush->Color = clBlue;
7 ListBox1->Canvas->Font->Color = clYellow;
8 }
9 else
10 {
11 ListBox1->Canvas->Brush->Color = clWhite;
12 ListBox1->Canvas->Font->Color = clBlack;
13 }
14 ListBox1->Canvas->FillRect(Rect);
15 ListBox1->Canvas->TextOut(Rect.Left, Rect.Top, ListBox1->Items->Strings[Index]);
16 }





記得要先把 ListBox1 的 Style 屬性改成 lbOwnerDrawVariable
或是 lbOwnerDrawFixed

Nelson 發表在 痞客邦 留言(0) 人氣()

在 BCB 裡頭可以很輕易的取得某個檔案的名稱,可是它會包含其副檔名,
有很多時候,我們並不需要副檔名,以下介紹三個方法可以簡單去掉它。

第一個方法是用 fnsplit 函式來達成,它最後會把檔名放在 szFileName 裡頭,更多說明就請自行看 help 啦~

1 #include
2 char szFileName[MAXFILE];
3 fnsplit(OpenDialog1->FileName.c_str(), 0, 0, szFileName, 0);


第二個方法


1 void __fastcall TForm1::BitBtn1Click(TObject *Sender)
2 {
3 AnsiString Name;
4 Name = "1234.bmp";
5 Name = Name.Delete(Name.Pos(ExtractFileExt(Name) ),ExtractFileExt(Name).Length() ) ;
6
7 }


第三個方法


1 if (OpenDialog1->Execute())
2 {
3 AnsiString FileName = ChangeFileExt(ExtractFileName(OpenDialog1->FileName), "");
4 ShowMessage(FileName);
5 }


三個方法都很好玩,要怎麼用就看自己的需求囉 :)

Nelson 發表在 痞客邦 留言(1) 人氣()

我寫了一個字串輸入工具,可以把常用的字串送到大多數的視窗,有不少人好奇這支程式是怎麼寫的。

其實我也是在論壇上面找了很久的資料,也得到不少人的幫助。



抱著回饋的心情,我把我的程式碼貼在論壇,既可做為他人參考資料,也可大家一同討論。詳情請看 :

http://delphi.ktop.com.tw/TOPIC.ASP?TOPIC_ID=64701

裡頭不只講了如何送字串,也說明如何建立自己的 System Tray Icon,以及如何防止程式重複執行。

希望對各位有幫助,也歡迎大家一同研究程式 :)



BTW,Delphi K.Top 是我很常逛的一個論壇,它可說是全台最大,資源最豐富的 Delphi & BCB 論壇, 裡頭臥虎藏龍,高手如雲,幾乎我遇過的問題,在那裡都有解答。

Nelson 發表在 痞客邦 留言(3) 人氣()

http://delphi.ktop.com.tw/topic.asp?topic_id=18760

http://delphi.ktop.com.tw/topic.asp?topic_id=35223




做法有很多,這裡提出兩種都很實用。


Nelson 發表在 痞客邦 留言(1) 人氣()

詳情請看  http://delphi.ktop.com.tw/topic.asp?method=AnswerOK&topic_id=75071


// 這是產生 Form 的 code

TfmMsgEditor *fmMsgEditor = new TfmMsgEditor(Application);

fmMsgEditor->Caption = IntToStr(MDIChildCount);

fmMsgEditor->OnClose = this->ChildFrmClose;  // 為新視窗指定關閉函式

TabControl1->Tabs->AddObject(IntToStr(MDIChildCount), fmMsgEditor);

TabControl1->TabIndex = TabControl1->Tabs->Count-1;





// 關閉視窗的函式
void __fastcall TfmMain::ChildFrmClose(TObject *Sender, TCloseAction &Action)
{
for(int i = 0; i < TabControl1->Tabs->Count; ++i)
{
if (TabControl1->Tabs->Objects[i] == Sender)
{
TabControl1->Tabs->Delete(i);
TabControl1->Refresh();
}
}
Action = caFree;
}





// Tab 切換的函式
void __fastcall TfmMain::TabControl1Change(TObject *Sender)
{
AnsiString s = TabControl1->Tabs->Objects[TabControl1->TabIndex]->ClassName();
// 若有多種不同的 MDI Child Form, 就可以這樣判斷
if (s == "TfmMsgEditor")
{
TfmMsgEditor *Child = dynamic_cast(TabControl1->Tabs->Objects[TabControl1->TabIndex]);
Child->BringToFront();
}
else if (s == "TfmEnumEditor")
{
TfmEnumEditor *Child = dynamic_cast(TabControl1->Tabs->Objects[TabControl1->TabIndex]);
Child->BringToFront();
}
}





// 這是在 Child Form 的 OnActivate 事件裡的 code, 這樣點選視窗時,Tab 也能跟著切換了
for (int i = 0; i < fmMain->TabControl1->Tabs->Count; ++i)
{
if (fmMain->TabControl1->Tabs->Strings[i] == Caption)
{
fmMain->TabControl1->TabIndex = i;
}
}

 




目前還有一個小缺點,就是在切換 Tab 的時候,

會有閃一下的現象產生,不知是否有方法可以避免呢?


Nelson 發表在 痞客邦 留言(2) 人氣()

透過以下的程式碼,可以在程式一執行時就決定是否要出現在底下的工作列,

也可以在執行期間,透過一個 CheckBox 動態決定它是否要出現在工作列。




/*

* 以下是在啟動時決定是否要出現在下方的工作列:

*/

Nelson 發表在 痞客邦 留言(0) 人氣()

原文在 http://delphi.ktop.com.tw/topic.asp?method=AnswerOK&topic_id=72744



#include

#include // 為了使用 GlobalFreePtr()



// 如果 shlobj.h 內未定義 BIF_NEWDIALOGSTYLE,請自定如下:

#define BIF_NEWDIALOGSTYLE 0x0040



/*


SetDirectory 自定函式:







功能簡述:



1. 顯示選擇目錄對話框,並提供 "新資料夾" 的按鈕項目,可在選擇目錄對話框直接建立新目錄。



2. 可取得被選擇目錄的路徑字串,以及被選擇目錄的名稱字串 (如果有的話)







bool SetDirectory(AnsiString StartDir, AnsiString *selDir, AnsiString *dirName)







參數說明:



StartDir: 開啟對話框時的起始目錄。



selDir: 選擇目錄後存放目錄路徑的字串,例如:
C:\



dirName: 選擇目錄後存放目錄名稱的字串,例如:本機磁碟 (C:)







傳回值: 取消傳回 false,選擇目錄則傳回 true



*/



int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM wParam, LPARAM lpData)

{

if(uMsg == BFFM_INITIALIZED)

{

PCSTR prevDir = reinterpret_cast (lpData);

if(DirectoryExists(prevDir))

{

SendMessage(hwnd, BFFM_SETSELECTION, static_cast (1L), lpData);



HWND shellCtrl = GetDlgItem(hwnd, 0);

if(shellCtrl != NULL)

{

HWND treeCtrl = GetDlgItem(shellCtrl, 0x64);

TVITEMEX tvi;

if(tvi.hItem = TreeView_GetSelection(treeCtrl))

{

TreeView_GetItem(hwnd, &tvi);

TreeView_Expand(treeCtrl, tvi.hItem, TVE_EXPAND);

}

}


}

}

return 0;

}




bool __fastcall SetDirectory(AnsiString StartDir, AnsiString *selDir, AnsiString *dirName)

{

auto bool rc = true;

BROWSEINFO bi;

char PathStr[MAX_PATH];

char FolderName[MAX_PATH];

LPITEMIDLIST ItemID;

memset(&bi, 0, sizeof(BROWSEINFO));

memset(PathStr, 0, MAX_PATH);

bi.hwndOwner = Application->Handle;

bi.pszDisplayName = FolderName;

bi.lpszTitle = "選擇資料夾";

bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_DONTGOBELOWDOMAIN | BIF_NEWDIALOGSTYLE;

bi.lpfn = reinterpret_cast (BrowseCallbackProc);

bi.lParam = reinterpret_cast (StartDir.c_str());

CoInitialize(NULL);

ItemID = SHBrowseForFolder(&bi);

if(ItemID == NULL)

{

rc = false;

}

else

{

SHGetPathFromIDList(ItemID, PathStr);

}

GlobalFreePtr(ItemID);

CoUninitialize();

*selDir = PathStr;

*dirName = FolderName;

return rc;

}

// 使用範例

void __fastcall TForm1::Button1Click(TObject *Sender)

{

AnsiString StartDirectory = "C:\\";
// 設定對話框起始目錄

AnsiString SelectedDirectory; // 儲存被選擇目錄的路徑字串

AnsiString DirectoryName; // 儲存被選擇目錄的名稱字串

if(SetDirectory(StartDirectory, &SelectedDirectory, &DirectoryName))

{

ShowMessage("選擇目錄:"+SelectedDirectory+"\n目錄名稱:"+DirectoryName);

}

}



The End~


Nelson 發表在 痞客邦 留言(0) 人氣()

原始出處 :  http://www.ccrun.com/doc/go.asp?id=598 
通過修改VCL源碼實現自定義輸入對話框

在BCB中有兩個函數可以實現輸入對話框:InputBox和InputQuery,其實InputBox也是調用了InputQuery,這個函數有幾個缺點:
  (1).輸入對話框上的按鈕是英文的。
  (2).不能實現星號密碼的效果。
但在實際編程中經常會遇到這樣的問題。全是中文的界面和提示,可是在輸入對話框中卻是英文的按鈕,顯的有些不協調,而且有時在Win2k下做的程序,換在Win98下的時候,輸入對話框中的提示會顯示不完整。通常在Form中可以通過更改默認字體為細明體9號來解決,但是在輸入對話框中可不可以呢?答案是肯定的,方法就是:修改VCL源碼!

以下方法介紹了在BCB 6.0 企業版中通過修改VCL源碼實現自定義輸入對話框。

1. 在BCB6安裝目錄的SourceVcl目錄下找到dialogs.pas,並copy一份到你的工程目錄下.

2. 打開剛才複製過來的dialogs.pas,修改其中的InputQuery函數代碼(1857-1927行):
   修改後的代碼如下:

Nelson 發表在 痞客邦 留言(0) 人氣()

 出處 :  http://dev.csdn.net/article/54/54130.shtm

 如何減小應用程序(EXE)的大小?

一般來說,由Delphi生成的EXE文件,要比其由它編程語言生成的體積大一些。這主要是由於使用VCL的原因(當然,VCL是有許多優點的!)

以下是減小EXE文件大小的幾種途徑:

01) 使用加殼工具(如 UPX,ASPack 等)
02) 使用 KOL
03) 不使用VCL來編寫程序
04) 使用 ACL (API 控制庫)
05) 使用 StripReloc
06) 去除運程調試信息與 TD32

Nelson 發表在 痞客邦 留言(0) 人氣()

作者: Nelson (茫) 看板: P_Nelson

標題: [文件] Virtual Key Code

時間: Mon Aug 30 22:29:48 2004



虛擬鍵的編碼(Virtual-key Codes).....


Nelson 發表在 痞客邦 留言(2) 人氣()

1 2