全國最多中醫師線上諮詢網站-台灣中醫網
發文 回覆 瀏覽次數:7607
推到 Plurk!
推到 Facebook!

如何讓 DBGrid 內的欄位在新增模式時可修改在編輯模式時變唯讀

尚未結案
bestlong
站務副站長


發表:126
回覆:734
積分:512
註冊:2002-10-19

發送簡訊給我
#1 引用回覆 回覆 發表時間:2013-07-10 16:50:10 IP:210.242.xxx.xxx 訂閱
因為資料有連動關連性被其他功能參考,例如採購單會被採購驗收入庫單參考
原本的寫法就是在採購單明細用 BeforEdit 來檢查已有驗收入庫就禁止編輯

不過因為使用者需要編輯沒有關連性的欄位,例如備註資料
所以要改成可編輯但將關連性欄位設為唯讀狀態的操作模式

表頭資料因為都是用 TDBEdit 的元件,所以在 DataSet 狀態變化實直接設定元件為 Read Only 就可以,程式如下

[code delphi]
procedure TF6B040.dsMStateChange(Sender: TObject);
begin
//編輯模式料號不可異動
if dsM.DataSet.State = dsInsert then
begin
DBEditPart_num.Color := clWindow;
DBEditPart_num.ReadOnly := False;
end else begin
DBEditPart_num.Color := clAqua;
DBEditPart_num.ReadOnly := True;
end;
end;
[/code]

但是明細是用 DBGrid 的就不確定如何處理了
請教各位站友有什麼好方法可分享的嗎?
------
http://blog.bestlong.idv.tw/
http://www.bestlong.idv.tw/
http://delphi-ktop.bestlong.idv.tw/
老大仔
尊榮會員


發表:78
回覆:837
積分:1088
註冊:2006-07-06

發送簡訊給我
#2 引用回覆 回覆 發表時間:2013-07-11 07:29:52 IP:210.61.xxx.xxx 未訂閱
不知道是否有誤解大大的意思
我猜想~是不是照本宣科就可以了呢?
if dsDetail.State = 'dsInsert' then
begin
DBGrid.Columns[0].ReadOnly := True;
end
else if dsDetail.State = 'dsEdit' then
begin
DBGrid.Columns[0].ReadOnly := False;
end;


bestlong
站務副站長


發表:126
回覆:734
積分:512
註冊:2002-10-19

發送簡訊給我
#3 引用回覆 回覆 發表時間:2013-07-11 09:24:26 IP:210.242.xxx.xxx 訂閱
看到您提議使用 Columns[0] 來處理
但是我想大家使用 DBGrid 都不會刻意鎖定欄位不可調整順序
因為使用者會調整欄位順序
所以我先用設定 Columns[0].color 來測試
如果使用者將別的欄位搬到第一欄就破功了

===================引 用 老大仔 文 章===================
不知道是否有誤解大大的意思
我猜想~是不是照本宣科就可以了呢?

[code delphi]
if dsDetail.State = 'dsInsert' then
begin
DBGrid.Columns[0].ReadOnly := True;
end
else if dsDetail.State = 'dsEdit' then
begin
DBGrid.Columns[0].ReadOnly := False;
end;
[/code]
------
http://blog.bestlong.idv.tw/
http://www.bestlong.idv.tw/
http://delphi-ktop.bestlong.idv.tw/
老大仔
尊榮會員


發表:78
回覆:837
積分:1088
註冊:2006-07-06

發送簡訊給我
#4 引用回覆 回覆 發表時間:2013-07-11 10:48:41 IP:210.61.xxx.xxx 未訂閱
那~
方法一:
將管控Enable的Code寫成一個Procedure
然後在 DataSource的StateChange事件 和 DBGrid的ColumnMoved事件中去呼叫那段Procedure
該Procedure大概長這樣
for i := 0 to DBGrid.Columns.Count - 1 do
begin
if DBGrid.Columns[i].FieldName = 'xxx' then
DBGrid.Columns[i].ReadOnly := True
else
DBGrid.Columns[i].ReadOnly := False;
end;
當然~詳細控制內容大大您需自行調整~

方法二:
除了原先的管控外
直接在DBGrid的ColumnMoved事件中再增加如下:
if Column.FieldName = 'xxx' then
Column.ReadOnly := True
else
Column.ReadOnly := False;




不知以上方法是否能有效管控?


===================引 用 bestlong 文 章===================
看到您提議使用 Columns[0] 來處理
但是我想大家使用 DBGrid 都不會刻意鎖定欄位不可調整順序
因為使用者會調整欄位順序
所以我先用設定 Columns[0].color 來測試
如果使用者將別的欄位搬到第一欄就破功了

編輯記錄
老大仔 重新編輯於 2013-07-11 12:23:54, 註解 無‧
bestlong
站務副站長


發表:126
回覆:734
積分:512
註冊:2002-10-19

發送簡訊給我
#5 引用回覆 回覆 發表時間:2013-07-11 17:17:34 IP:210.242.xxx.xxx 訂閱
目前在 StateChange 處理,程式碼如下:

[code delphi]
procedure TF6B040.dsDStateChange(Sender: TObject);
var i: Integer;
begin
//dsD is TDataSource
if dsD.DataSet.State = dsInsert then
begin
for i := 0 to DBGrid.Columns.Count - 1 do
begin
if DBGrid.Columns[i].FieldName = 'Mkod_num' then DBGrid.Columns[i].ReadOnly := False;
if DBGrid.Columns[i].FieldName = 'Part_num' then DBGrid.Columns[i].ReadOnly := False;
end;
end;

if dsD.DataSet.State = dsEdit then
begin
for i := 0 to DBGrid.Columns.Count - 1 do
begin
if DBGrid.Columns[i].FieldName = 'Mkod_num' then DBGrid.Columns[i].ReadOnly := True;
if DBGrid.Columns[i].FieldName = 'Part_num' then DBGrid.Columns[i].ReadOnly := True;
end;
end;
end;
[/code]

不過還是碰到一些麻煩就是
使用者會直接在 DBGrid 的欄位輸入資料

在沒有管控時就是直接進入編輯模式

而在有控管時 DBGrid 還是會先出現編輯輸入框,但該欄位已經被切換唯讀
此時的輸入框也沒有原本欄位內的資料
然後不管是有輸入資料或直接跳下一欄時都是出現 Field '工令編號' cannot be modified 的錯誤訊息
工令編號對應的就是欄位 Mkod_num

如果是點選編輯按鈕再進入要編輯欄位整個操作流程就太麻煩了,因為每一筆都要按
一整個不順又麻煩,絕對被使用者罵翻

------
http://blog.bestlong.idv.tw/
http://www.bestlong.idv.tw/
http://delphi-ktop.bestlong.idv.tw/
編輯記錄
bestlong 重新編輯於 2013-07-12 15:51:45, 註解 將程式碼內 dsM 改正為 dsD (連接明細資料表)‧
herbert2
尊榮會員


發表:58
回覆:640
積分:894
註冊:2004-04-16

發送簡訊給我
#6 引用回覆 回覆 發表時間:2013-07-11 22:43:54 IP:202.39.xxx.xxx 訂閱
可否將 dsDetail 的 OnAfterInsert 與 OnAfterEdit 都指向 dsDetail.AfterEdit,
並於 dsDetail.AfterEdit 用迴圈檢視全部的 dsDetail.Fields,
當 dsDetail.Fields.Field[i].FieldName = 'Mkod_num' 時,
若 dsDetail.State = 'dsInsert' 則 dsDetail.Fields.Field[i].ReadOnly := False;
若 dsDetail.State = 'dsEdit' 則 dsDetail.Fields.Field[i].ReadOnly := True;
如此便不耽心 DBGrid.Coulmn[i] 被 User 移動的問題了.


[code delphi]
procedure TF6B040.dsMAfterEdit(Sender: TObject);
var i: Integer;
begin
for i := 0 to dsM.FieldCount - 1 do
begin
if dsM.Fields.Field[i].FieldName = 'Mkod_num' then
begin
if dsM.DataSet.State = dsInsert then
dsM.Fields.Field[i].ReadOnly := False;
if dsM.DataSet.State = dsEdit then
dsM.Fields.Field[i].ReadOnly := True;
end;
if dsM.Fields.Field[i].FieldName = 'Part_num' then
begin
if dsM.DataSet.State = dsInsert then
dsM.Fields.Field[i].ReadOnly := False;
if dsM.DataSet.State = dsEdit then
dsM.Fields.Field[i].ReadOnly := True;
end;
end;
end;
[/code]
編輯記錄
herbert2 重新編輯於 2013-07-11 22:46:24, 註解 無‧
herbert2 重新編輯於 2013-07-11 22:57:59, 註解 無‧
bestlong
站務副站長


發表:126
回覆:734
積分:512
註冊:2002-10-19

發送簡訊給我
#7 引用回覆 回覆 發表時間:2013-07-12 11:20:11 IP:210.242.xxx.xxx 訂閱
關於使用者移動欄位的問題
將 DBGrid.Columns[i]
若是直接對 DataSet.Field[0] 設定也是要改成掃描全部欄位用 DataSet.Field[i]
這裡的 DataSet 可以是 TTable 或 TQuery
這樣就可解決移動欄位的問題,程式碼變多而且要小心欄位名稱與大小寫是否有對應到

使用者在 DBGrid 上都是用鍵盤方向鍵來移動焦點
需要修改時就會直接輸入新值
這時就會碰上出現編輯框但該欄位已被程式設定唯讀
所以送出編輯就會有錯誤 Field 'XXXX' cannot be modified 產生

這個狀況我在 DataSource 的 StateChange 事件內處理管制切換
或者是 TQuery or TTAble 的 BeforeInsert and BeforeEdit 事件內處理管制切換
都是一樣
所以我還無法確認要用什麼順序與事件時機才能不改變使用者的操作習慣

目前思考或許應該還需要 DBGrid.OnEnter 或 DataSource.OnDataChange 或 DataSet.AfterScroll 這幾個事件來配合
這個配方真希望能快點探索出來
不然就只能改變架構直接鎖住資料,另寫異動功能來處理了


===================引 用 herbert2 文 章===================
可否將 dsDetail 的 OnAfterInsert 與 OnAfterEdit 都指向 dsDetail.AfterEdit,
並於 dsDetail.AfterEdit 用迴圈檢視全部的 dsDetail.Fields,
當 dsDetail.Fields.Field[i].FieldName = 'Mkod_num' 時,
若 dsDetail.State = 'dsInsert' 則 dsDetail.Fields.Field[i].ReadOnly := False;
若 dsDetail.State = 'dsEdit' 則 dsDetail.Fields.Field[i].ReadOnly := True;
如此便不耽心 DBGrid.Coulmn[i] 被 User 移動的問題了.
[code delphi]
procedure TF6B040.dsMAfterEdit(Sender: TObject);
var i: Integer;
begin
for i := 0 to dsM.FieldCount - 1 do
begin
if dsM.Fields.Field[i].FieldName = 'Mkod_num' then
begin
if dsM.DataSet.State = dsInsert then
dsM.Fields.Field[i].ReadOnly := False;
if dsM.DataSet.State = dsEdit then
dsM.Fields.Field[i].ReadOnly := True;
end;
if dsM.Fields.Field[i].FieldName = 'Part_num' then
begin
if dsM.DataSet.State = dsInsert then
dsM.Fields.Field[i].ReadOnly := False;
if dsM.DataSet.State = dsEdit then
dsM.Fields.Field[i].ReadOnly := True;
end;
end;
end;
[/code]
------
http://blog.bestlong.idv.tw/
http://www.bestlong.idv.tw/
http://delphi-ktop.bestlong.idv.tw/
老大仔
尊榮會員


發表:78
回覆:837
積分:1088
註冊:2006-07-06

發送簡訊給我
#8 引用回覆 回覆 發表時間:2013-07-12 12:41:20 IP:210.61.xxx.xxx 未訂閱
請問dsM是Dataset?還是DataSource?
是Master的?還是Detail的?


===================引 用 bestlong 文 章===================
關於使用者移動欄位的問題
將 DBGrid.Columns[i]
若是直接對 DataSet.Field[0] 設定也是要改成掃描全部欄位用 DataSet.Field[i]
這裡的 DataSet 可以是 TTable 或 TQuery
這樣就可解決移動欄位的問題,程式碼變多而且要小心欄位名稱與大小寫是否有對應到

使用者在 DBGrid 上都是用鍵盤方向鍵來移動焦點
需要修改時就會直接輸入新值
這時就會碰上出現編輯框但該欄位已被程式設定唯讀
所以送出編輯就會有錯誤 Field 'XXXX' cannot be modified 產生

這個狀況我在 DataSource 的 StateChange 事件內處理管制切換
或者是 TQuery or TTAble 的 BeforeInsert and BeforeEdit 事件內處理管制切換
都是一樣
所以我還無法確認要用什麼順序與事件時機才能不改變使用者的操作習慣

目前思考或許應該還需要 DBGrid.OnEnter 或 DataSource.OnDataChange 或 DataSet.AfterScroll 這幾個事件來配合
這個配方真希望能快點探索出來
不然就只能改變架構直接鎖住資料,另寫異動功能來處理了

編輯記錄
老大仔 重新編輯於 2013-07-12 12:41:37, 註解 無‧
bestlong
站務副站長


發表:126
回覆:734
積分:512
註冊:2002-10-19

發送簡訊給我
#9 引用回覆 回覆 發表時間:2013-07-12 16:06:23 IP:210.242.xxx.xxx 訂閱
我有將回覆修正並補充了
發問那一篇的程式碼內 dsM 是 DataSource 元件連接 tbM (主檔)
是控管表頭的欄位,都是用 TDBEdit 元件來顯示資料
所以可以在主檔的 DataSource 元件直接控管 TDBEdit 元件的唯讀屬性
因為要新增或編輯都是要先按下按鈕後才能異動

明細檔是用 DBGrid 來顯示,雖然也有專用的增、刪、改按鈕
不過可以直接輸入就進入新增或編輯模式
這已經是使用者長年的操作習慣
所以明細檔的 DataSource 名稱是 dsD

整個元件關係如下:
tbM > dsM > TDBEdit
tbD > dsD > DBGrid


===================引 用 老大仔 文 章===================
請問dsM是Dataset?還是DataSource?
是Master的?還是Detail的?

------
http://blog.bestlong.idv.tw/
http://www.bestlong.idv.tw/
http://delphi-ktop.bestlong.idv.tw/
老大仔
尊榮會員


發表:78
回覆:837
積分:1088
註冊:2006-07-06

發送簡訊給我
#10 引用回覆 回覆 發表時間:2013-07-17 07:20:10 IP:210.61.xxx.xxx 未訂閱
那假如照大大這麼說的話
是否能在進入新增/編輯模式時再加入一次的判斷呢?

===================引 用 bestlong 文 章===================
我有將回覆修正並補充了
發問那一篇的程式碼內 dsM 是 DataSource 元件連接 tbM (主檔)
是控管表頭的欄位,都是用 TDBEdit 元件來顯示資料
所以可以在主檔的 DataSource 元件直接控管 TDBEdit 元件的唯讀屬性
因為要新增或編輯都是要先按下按鈕後才能異動

明細檔是用 DBGrid 來顯示,雖然也有專用的增、刪、改按鈕
不過可以直接輸入就進入新增或編輯模式

這已經是使用者長年的操作習慣
所以明細檔的 DataSource 名稱是 dsD
整個元件關係如下:
tbM > dsM > TDBEdit
tbD > dsD > DBGrid


===================引 用 老大仔 文 章===================
請問dsM是Dataset?還是DataSource?
是Master的?還是Detail的?

bestlong
站務副站長


發表:126
回覆:734
積分:512
註冊:2002-10-19

發送簡訊給我
#11 引用回覆 回覆 發表時間:2013-07-19 13:34:24 IP:210.242.xxx.xxx 訂閱
我了解您的想法
正如我前面在 #7 有提到

整個元件關係如下:
tbM > dsM > TDBEdit
tbD > dsD > DBGrid


===================引 用 老大仔 文 章===================
請問dsM是Dataset?還是DataSource?
是Master的?還是Detail的?

------
http://blog.bestlong.idv.tw/
http://www.bestlong.idv.tw/
http://delphi-ktop.bestlong.idv.tw/
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#12 引用回覆 回覆 發表時間:2013-07-21 00:13:46 IP:118.169.xxx.xxx 未訂閱
因為站務大大問的是有沒有好方法, 而不是問如何在dbgrid 上解決輸人的問題, 所以就問的方式來說

這個問題, 其實我遇過太多了, 要在dbgrid 控制輸入, 的確不是一件容易的事, 而要如樓上各位樓主所言,
可能還在 after, before 之間打轉, 不免一個控制不好, 該 readonly 的, 結果可以modify, 或反過來,
這真的是吃力不討好, 所以我後來都不再用 dbgrid 來編輯, 雖然是很直覺化的操作, 但卻是最不人性的設計,
因此我寧可拿dbgrid 做read, 另外開 dbedit 來修正, 要不然用 dbctrl 元件加 dbedit 進行也是可以的,

如果是要問後者的方式, 我現在的做法, 都放棄dbgrid 的模式, 改用 stringgrid,
一樣可以達到 dbgrid 的效果, 而 stringgird 要好控制多了, 至於資料的回寫, 透過自己在底層寫控制回去資料庫就好了,
不用去考慮什麼 after, before 的問題

以上提供意見~~
===================引 用 bestlong 文 章===================
因為資料有連動關連性被其他功能參考,例如採購單會被採購驗收入庫單參考
原本的寫法就是在採購單明細用 BeforEdit 來檢查已有驗收入庫就禁止編輯

不過因為使用者需要編輯沒有關連性的欄位,例如備註資料
所以要改成可編輯但將關連性欄位設為唯讀狀態的操作模式

表頭資料因為都是用 TDBEdit 的元件,所以在 DataSet 狀態變化實直接設定元件為 Read Only 就可以,程式如下

[code delphi]
procedure TF6B040.dsMStateChange(Sender: TObject);
begin
//編輯模式料號不可異動
if dsM.DataSet.State = dsInsert then
begin
DBEditPart_num.Color := clWindow;
DBEditPart_num.ReadOnly := False;
end else begin
DBEditPart_num.Color := clAqua;
DBEditPart_num.ReadOnly := True;
end;
end;
[/code]

但是明細是用 DBGrid 的就不確定如何處理了
請教各位站友有什麼好方法可分享的嗎?
編輯記錄
P.D. 重新編輯於 2013-07-21 00:14:33, 註解 無‧
P.D. 重新編輯於 2013-07-21 00:15:45, 註解 無‧
aftcast
站務副站長


發表:81
回覆:1485
積分:1763
註冊:2002-11-21

發送簡訊給我
#13 引用回覆 回覆 發表時間:2013-07-21 01:31:54 IP:114.44.xxx.xxx 訂閱
我也推與pd一樣的做法!!  除非是研究,否則使用dbgrid要來複雜控制恐不是一件愉快且助益大的做法。  

喔,我送出內容後才發現… 標題「

如何讓 DBGrid 內的欄位在新增模式時可修改在編輯模式時變唯讀



sorry,maybe我還是離題了…


===================引 用 P.D. 文 章===================


如果是要問後者的方式, 我現在的做法, 都放棄dbgrid 的模式, 改用 stringgrid,
一樣可以達到 dbgrid 的效果, 而 stringgird 要好控制多了, 至於資料的回寫, 透過自己在底層寫控制回去資料庫就好了,
不用去考慮什麼 after, before 的問題

以上提供意見~~

------


蕭沖
--All ideas are worthless unless implemented--

C++ Builder Delphi Taiwan G+ 社群
http://bit.ly/cbtaiwan
編輯記錄
aftcast 重新編輯於 2013-07-21 01:33:24, 註解 sorry,maybe我還是離題了…‧
bestlong
站務副站長


發表:126
回覆:734
積分:512
註冊:2002-10-19

發送簡訊給我
#14 引用回覆 回覆 發表時間:2013-07-22 13:11:45 IP:210.242.xxx.xxx 訂閱
感謝大家參與討論

因為大環境的趨勢就是訂單少量多樣、製造分批
造成使用者新增與修改的操作量很大
為了持續增加防呆防錯的功能
以及試著尋找簡化使用者操作流程的方向

關於 DBGrid 與 DataSource 以及 DataSet 三個元件之間的事件糾葛,真的令人苦惱


而 P.D. 所言改用 StringGrid 來處理也給出一個不同的選擇方向
我試一下發現也是很考驗功力,樣樣要自己來喔
填滿資料
排序資料
增刪改查
欄位隱藏(外键與資料版本)
欄位搬移
顏色管理
匯入匯出
------
http://blog.bestlong.idv.tw/
http://www.bestlong.idv.tw/
http://delphi-ktop.bestlong.idv.tw/
P.D.
版主


發表:603
回覆:4038
積分:3874
註冊:2006-10-31

發送簡訊給我
#15 引用回覆 回覆 發表時間:2013-07-22 14:44:38 IP:59.120.xxx.xxx 未訂閱
如果要用delphi標準stringgrid 真的會需要一點功力, 因為提供的事件太少了, 
我現在都是用 tms , 所提到的功能幾乎都有開發出來了, 不妨考慮一下,
不過 tms 一旦掛進來, 程式byte會擴增一倍以上,
所以在意 k數大小, 不建議使用
===================引 用 bestlong 文 章===================
感謝大家參與討論

因為大環境的趨勢就是訂單少量多樣、製造分批
造成使用者新增與修改的操作量很大
為了持續增加防呆防錯的功能
以及試著尋找簡化使用者操作流程的方向

關於 DBGrid 與 DataSource 以及 DataSet 三個元件之間的事件糾葛,真的令人苦惱


而 P.D. 所言改用 StringGrid 來處理也給出一個不同的選擇方向
我試一下發現也是很考驗功力,樣樣要自己來喔
填滿資料
排序資料
增刪改查
欄位隱藏(外键與資料版本)
欄位搬移
顏色管理
匯入匯出
Mickey
版主


發表:77
回覆:1882
積分:1390
註冊:2002-12-11

發送簡訊給我
#16 引用回覆 回覆 發表時間:2013-10-21 09:34:04 IP:114.35.xxx.xxx 訂閱
大家好 :

若是我, 會考慮 override TDBGrid 的
function CanEditAcceptKey(Key: Char): Boolean; override;
function CanEditModify: Boolean; override;
但壞處是...必須改為 RunTime Create TxxDBGrid.

以下是屏蔽計算欄位的可編輯性, 參考:

TxxDBGrid=class(TDBGrid)
protected
function CanEditAcceptKey(Key: Char): Boolean; override;
function CanEditModify: Boolean; override;
.....
end;

implementation


{TxxDBGrid}
function TxxDBGrid.CanEditAcceptKey(Key: Char): Boolean;
begin
if Assigned(SelectedField) and (SelectedField.FieldKind in [fkCalculated, fkInternalCalc]) then
Result := False
else
Result := inherited CanEditAcceptKey(Key);
end;

function TxxDBGrid.CanEditModify: Boolean;
begin
if Assigned(SelectedField) and (SelectedField.FieldKind in [fkCalculated, fkInternalCalc]) then
Result := False
else Result := inherited CanEditModify;
end;

編輯記錄
Mickey 重新編輯於 2013-10-21 09:35:05, 註解 無‧
hagar
版主


發表:143
回覆:4056
積分:4445
註冊:2002-04-14

發送簡訊給我
#17 引用回覆 回覆 發表時間:2013-10-29 16:44:39 IP:61.218.xxx.xxx 未訂閱
試試直接設定 TField 的 ReadOnly 屬性:

[code delphi]

procedure TForm1.DataSource1.StateChange(Sender: TObject);
begin
if DataSource1.State = dsInsert then
begin
DataSource1.DataSet.FieldByName('field1').ReadOnly := False;
DataSource1.DataSet.FieldByName('field2').ReadOnly := False;
DataSource1.DataSet.FieldByName('field3').ReadOnly := False;
// ...
end
else
begin
DataSource1.DataSet.FieldByName('field1').ReadOnly := True;
DataSource1.DataSet.FieldByName('field2').ReadOnly := True;
DataSource1.DataSet.FieldByName('field3').ReadOnly := True;
// ...
end
end;
[/code]


系統時間:2024-11-21 18:59:20
聯絡我們 | Delphi K.Top討論版
本站聲明
1. 本論壇為無營利行為之開放平台,所有文章都是由網友自行張貼,如牽涉到法律糾紛一切與本站無關。
2. 假如網友發表之內容涉及侵權,而損及您的利益,請立即通知版主刪除。
3. 請勿批評中華民國元首及政府或批評各政黨,是藍是綠本站無權干涉,但這裡不是政治性論壇!