2008年1月17日 星期四

Append a string to CEdit

最近為了某些事,自己動手做了簡單的 GUI, 我以前從來沒有碰過 MFC 的,學生時候也只用 BCB 拉過簡單的 GUI。由於以前沒碰過,所以連些最簡單的小東西我也可能不知道藏在哪邊,自己找不到時就要問 GUI team 的同事。
不過現在碰到一個小問題,很基本的小問題,竟然問了半天都沒有好的答案。我只是要在 multi line 的 CEdit 後面加一行字串,不論是 google 還是問人,都只能得到爛爛的方法,沒辦法像 CListBox 或 BCB 的 TMemo 一樣,用一個 AddString( ) 就解決,所幸最後還是找到了堪用的解法(不想看過程的人請直接跳到最後面)。
那為何不用 CListBox? 因為 CListBox 不能選取/copy/paste。

第一版:

就是先 GetWindowText( ) 到 buffer中,把字串字自往 buffer 後面加上去,再 SetWindowText(buffer) 回去。真是爛到不行的點子,也是最多人提出的解決方案。我明明只是要加字串到尾端,竟然還要先讀出全部的舊字串再寫回,又麻煩又影響效率,還得考慮配置 buffer 的相關問題。

第二版:預先配置一個 buffer, 把要 append 的字串先加入 buffer, 再把整個 buffer 透過 SetWindowText( ) 寫入 CEdit。這個方法其實跟第一個方法一樣,換湯不換藥,只是把工作換個地方做而已,而且從軟體工程的角度來看,其實更糟,因為這個 buffer 必需配置在 function 以外的地方,可能是某個 global 變數或是某個 class member, 若要放在這個 Append function 中,就得做成 static,那在 multi thread 時又會出問題!

第三版:網路上找到的方法,我把它包成 function:
inline void AppendStringToEdit(const string &str, CEdit &edit)
{
int length = edit.GetWindowTextLength();
edit.SetSel(length, length);
edit.ReplaceSel(str.c_str());
}
道理很簡單,就是先選取該 edit 中最後的部分,再將它取代,而這個被選取的部分其實是空的,因此造成 append 的效果。這雖然不太直接,但看來應該可行,可惜事與願違,它會把我要加的字串加在第一行的尾端,而非最後一行。

第四版:自己亂試試出來的
inline void AppendStringToEdit(const string &str, CEdit &edit)
{
edit.SetSel(-1, -1);
edit.ReplaceSel(str.c_str());
}

這個是第三版的改良版,會選取游標所在之處,而預設情況下,游標會在文字的最尾端,所以可以達到我想要的效果,這版終於做到我要的效果了!! 多數人應該到此就會打住了吧但我還是不滿意,因為只要使用者有動到游標(例如他想選取一部分 copy 起來),插入的位置就不在尾端,於是文字就亂掉了,因此有下一版的產生。

第五版:不會被游標干擾的版本
inline void AppendStringToEdit(const string &str, CEdit &edit)
{
int nBegin = edit.LineIndex(edit.GetLineCount() - 1);
int nEnd = nBegin + edit.LineLength(nBegin);
edit.SetSel(nEnd, nEnd);
edit.ReplaceSel(str.c_str());
}

這版會先取出最後一行( GetLineCount( ) -1 )所在的位置,然後選取該行的行末,再做取代工作,這樣就不會被使用者的游標給干擾了,太好了 :)
不過當使用者選取了某個範圍後,只要我一加新字串進去,他的選取範圍就會不見,這實在有點惱人,所以就有下一版的出現,這個改進還蠻理所當然的。

第六版:不會干擾游標的版本
inline void AppendStringToEdit(const string &str, CEdit &edit)
{
int nOrigBegin, nOrigEnd;
edit.GetSel(nOrigBegin, nOrigEnd);
int nBegin = edit.LineIndex(edit.GetLineCount() - 1);
int nEnd = nBegin + edit.LineLength(nBegin);
edit.SetSel(nEnd, nEnd);
edit.ReplaceSel(str.c_str());
edit.SetSel(nOrigBegin, nOrigEnd);
}

這版只多做一件事,就是把原先的選取範圍存下來,做完上一版事情後,再把原先的選取範圍給選回去. 就是先 GetSel( ),做完事後再 SetSel( )。這樣看起來舒服、自然多了。


CEdit 的 Append 改版到此暫時告一段落,不過若我發現還有改善空間還是會再改進的。

心得:MFC 果然是爛東西。

2 則留言:

Selina 提到...

其實這樣子的動作是很吃資源的, 我最近在寫個視窗軟體, 用這樣的方式去更新文字內容, 並且限制顯示的字數(顯示最新的4096個字元)會大量耗掉系統資源, 反而用最原始的方式將字串移轉後再setwindowtext, 可得到較佳的表現..
(以上的狀況是: 不停地將512個字元更新到視窗裡的字串)

匿名 提到...

大大您好
小弟想學c++ .net 或JAVA,剛剛逛了一下104發現,大部份公司要的都是C++的人材。

朋友都說我邏輯觀念蠻強的,一般Office系列軟體我都很強,朋友也常常拜託我修電腦,解毒等等,最近還有用會聲會影幫朋友剪接,這些東西我都是自學的,在一般朋友眼中都說我電腦很強,但就只缺一點不會寫程式,今年29歲了,不想再做業務的工做,想往程式設計師走,薪水比較穩定,以我這樣的條件..

我本身數學.英文程度很差可以嗎?

是要從Turbo C學起嗎?還是?

Turbo C 、C++ 、.net 到底差在哪裡?我要從哪個地方開始?

最後小弟我只能在家上網自學,沒錢去補習班,能不買書,錢就可以省下,能省就省

抱歉問了很多,麻煩你了大大,給我盞明燈吧