我在表格上有一个MasterQry和一个SlaveQry。 MasterQry是这样的:
select * from Header where Active = 1
。
在AfterScroll事件中,我有以下内容:
select * from Slave where HeadID=:Header.ID
现在,如果我愿意:
if MasterQry.Active then MasterQry.Close;
MasterQry.Open;
如果我有一个以上的记录,这将完美地工作,但是如果我只有一个记录,则将无法工作。
即使我MasterQry.First;
也没事。
如果我尝试MasterQry.AfterScroll(MasterQry)
,则会遇到访问冲突。
我正在重构代码,并试图使其更紧凑,因为我做了很多“打开关闭定位ID”(需要刷新数据以获取实际状态,是否被锁定等),并且这样做: / p>
function RefreshQuery(AQuery : TADOQuery; ID : integer) : boolean ; overload;
var AfterOpen,AfterScroll : TDataSetNotifyEvent;
begin
result:=false;
AfterOpen := AQuery.AfterOpen;
AfterScroll := AQuery.AfterScroll;
AQuery.AfterOpen:=nil;
AQuery.AfterScroll:=nil;
if AQuery.Active then AQuery.Close;
AQuery.Open;
if not AQuery.Locate('id', ID, []) then
result:=false
else
result:=true;
AQuery.AfterOpen:=AfterOpen;
AQuery.AfterScroll:=AfterScroll;
if Assigned(AQuery.AfterScroll) then
AQuery.AfterScroll(AQuery);
end;
请注意,此代码不是通用代码,但非常适合我的需求。我注意到的是,即使MasterQry中只有一个记录,或者根本没有,也将触发AfterScroll事件。我真的很高兴,我对其进行了多次测试,并给出了正确的结果。
我检查了TADOQuery.First和TADOQuery.Locate过程,它们都具有DoAfterScroll,但是没有通过“一个记录”或“没有记录”来触发。 (SlaveQry处于打开状态,处于不希望的状态)
为此我做了很多搜索,但找不到原因。
我的问题是:为什么这样做有效?为什么AfterScroll触发一条或一条记录。
谢谢。
更新
我只能使用Microsoft SQL复制它。因此,为了测试这一点,您需要。两张桌子。
在 MasterTable 中添加两条记录(ID,Text,Active)
1首1
2秒1
在从表中添加两个或多个记录(ID,HeadID,文本)
1,1,First-1
2,1,First-2
3,2,Second-1
4,2,Second-2
现在将两个 ADOQueries 放在窗体 ADOConnection 上。
在 MainQuery 中,您有以下文本
Select * from MasterTable where Active=1
在 SlaveQuery 中,您有以下文字
select * from SlaveTable where HeadID=:HeadID
在 MainQuery.BeforeOpen 上,您具有以下功能:
MainQuery.AfterScroll:=nil;
在 MainQuery.AfterScroll 上,您具有以下功能:
if SlaveQuery.Active then SlaveQuery.Close;
SlaveQuery.Parameters.ParamByName('HeadID').Value:=MainQueryID.Value;
SlaveQuery.Open;
在 MainQuery.AfterOpen 上,您具有以下功能:
MainQuery.AfterScroll:=MainQueryAfterScroll;
在此表单中添加按钮:
Button1Click事件包含以下内容:
if MasterQuery.Active then MasterQuery.Close;
MasterQuery.Open;
因此,如果您现在将网格附加到两个查询,则可以看到它完美地遵循了。
在不关闭程序的情况下,进入SQL Server管理器并运行以下更新语句:
update MasterTable set Active=0
再次在表单上按Button1:
MasterQuery是Emtpy,SlaveQuery处于上次打开状态。
要解决此问题,您需要按以下方式更改Button1Click:
var AfterOpen,AfterScroll : TDataSetNotifyEvent;
begin
AfterOpen := AQuery.AfterOpen;
AfterScroll := AQuery.AfterScroll;
AQuery.AfterOpen:=nil;
AQuery.AfterScroll:=nil;
if AQuery.Active then AQuery.Close;
AQuery.Open;
AQuery.AfterOpen:=AfterOpen;
AQuery.AfterScroll:=AfterScroll;
if Assigned(AQuery.AfterScroll) then
AQuery.AfterScroll(AQuery);
end;
现在它可以工作了。我不知道为什么,因为MasterQuery.First应该触发DoAfterScroll但什么都不会发生。 似乎将AfterScroll设置为nil,然后再次返回就可以触发AfterScroll,即使它具有1条Record或为空。
答案 0 :(得分:4)
正如我在评论中所说,RefreshQuery
中的大多数代码都不是必需的,
因为链接Master-> Detail数据集应该“正常”。实际上,您的RefreshQuery
完全没有必要。
我仅通过删除组件就根据您的主表和从表创建了一个最小的项目
从面板上进行布线,然后仅在下面的Form1.FormCreate中添加代码。的
从属网格的内容正确跟踪主网格,包括以下情况:
没有匹配的从站记录,即从站网格显示为空。请注意,没有任何需要的数据事件,即没有AfterScroll
也没有Locate
等呼叫。
type
TForm1 = class(TForm)
dsMaster: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
DBGrid2: TDBGrid;
DataSource2: TDataSource;
DBNavigator2: TDBNavigator;
ADOConnection1: TADOConnection;
qMaster: TADOQuery;
qSlave: TADOQuery;
qSlaveID: TIntegerField;
qSlaveHeaderID: TIntegerField;
qSlaveAText: TWideStringField;
procedure FormCreate(Sender: TObject);
public
end;
[...]
procedure TForm1.FormCreate(Sender: TObject);
begin
qMaster.SQL.Text := 'select * from mastertable';
qSlave.DataSource := dsMaster;
qSlave.SQL.Text := 'select * from slavetable where headerid = :id';
// NOTE: because the DataSource property of qSlave is set to dsMaster,
// the ` = :id` tells the Ado run-time code to get the value of the
// ID field in the qMaster table.
qMaster.Open;
qSlave.Open;
end;
如果要在其他用户更改记录的情况下刷新主表或从表,则可以执行以下操作:
procedure TForm1.Button1Click(Sender: TObject);
begin
qMaster.Refresh;
end;
,但是请注意,该表需要在Sql Server上正确设置。只要将ID字段设置为主键,并且/或者在服务器上设置了唯一索引,对Refresh
的调用应该可以正常工作,但是如果没有,则会出现错误并显示一条消息,提示“密钥信息不足以进行更新或刷新”。您当然可以在计时器上进行刷新(但不要过于频繁地调用它,即每隔几秒钟不止一次)。