DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
lazarus/fpc/Free Pascal
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: jsuguo
今日帖子: 31
在线用户: 17
导航: 论坛 -> DELPHI技术 斑竹:liumazi,sephil  
作者:
男 sxqwhxq (步惊云) ★☆☆☆☆ -
普通会员
2020/12/16 12:09:04
标题:
在Panel中内嵌窗口实现模块化编程 浏览:1987
加入我的收藏
楼主: 管理型程序往往有很多子模块,如果都写在主程序是非常难于维护的。因此把业务写在子窗口中,然后在主控窗口中创建并调用子窗口,实现模块化编程。以前用delphi xe,只要记住一点,用全局变量引用创建的Form,用后和创建前,一律FreeAndNil,就不会有内存访问错误和重复创建问题,但10.3,这样操作会频繁出现不可解决的内存管理问题,可能是内存管理方式与xe不同导致的,必须另谋出路。

一、用Form还是Frame

必须用Frame。Frame是纯vcl控件,里面的消息管理机制比较简单。Form则是一个复杂的vcl/win32窗口控件,delphi对它进行了复杂处理,比如消除处理机制,同时还有些处理是隐藏的。当然一次性创建相同窗口,一般不会出现问题。但重复创建不同的子窗口,则肯定会频繁出现invalid pointer opertor错误,有时正常有时不正常,这个问题具有一定隐匿性,要反复测试才会重现问题。

二、怎样创建

procedure TYzdjMainFM.RzGroup1Items4Click(Sender: TObject);
var
  FrArchives:TFrArchives;
  OldFr:TComponent;
begin
   FindOldFr(OldFr);
    if Assigned(OldFr) then FreeAndNil(OldFr);
   FrArchives:=TFrArchives.Create(self);
   FrArchives.Name:='FrArchives';
   FrArchives.Parent:=RzPanel2;
   FrArchives.Align:=alclient;
   FrArchives.SetFocus;
   FrArchives.Show;
end;

procedure TYzdjMainFM.RzGroup1Items5Click(Sender: TObject);
  var
   FrFinanReg:TFrFinanReg;
   OldFr:TComponent;
begin
  FindOldFr(OldFr);
  if Assigned(OldFr) then FreeAndNil(OldFr);
  FrFinanReg:=TFrFinanReg.Create(self);
  FrFinanReg.Name:='FrFinanReg';
  FrFinanReg.Parent:=RzPanel2;
  FrFinanReg.Align:=alClient;
  FrFinanReg.SetFocus;
  FrFinanReg.Show;
end;

三、怎样避免内存访问错误

一是必须  FrFinanReg.SetFocus;防止子窗口焦点乱跑,这个是内嵌窗口的通病,

二是创建前必须先在内存里寻找是否有该Frame的实例,如果没有才创建。

这里必须对前面已经创建的各类Frame对象进行销毁。

Procedure TYzdjMainFM.FindOldFr(var OldFr:TComponent);
begin
   if FindComponent('FrArchives')<>nil then OldFr:=FindComponent('FrArchives');
   if FindComponent('FrFinanReg')<>nil then OldFr:=FindComponent('FrFinanReg');

  ..........//所有要创建的窗口对象找到后都必须销毁
end;

三是用局部变量接纳窗口变量,实践表明,如果用全局变量引用不同的窗口变量,又会导致新的内存管理错误。

这样写后,不管子窗口里有任何复杂的内存操作,例如动态创建窗口、流、数据集等,都不会再出现内存访问错误。

四、怎样初始化Frame

 覆盖这两个事件方法,可分别在这两个事件中初始化和事后处理。

 private
    { Private declarations }
  public
    { Public declarations }
    procedure AfterConstruction; override;   //类似OnCreate事件
    procedure BeforeDestruction; override; //类型OnDestroy事件
  end;

但是没有onclose。

//初始化,给DBComboBoxEh1添加下拉选项

procedure TFrArchives.AfterConstruction;
var
  TempFdq:TFdquery;
begin
  inherited;//这个必须写在第一句
  TempFdq:=TFdquery.Create(self);
 TempFdq.Connection:=MainDataMd.FDConnection1;
  TempFdq.Open('select * from dmArchtype order by xh');
  while not TempFdq.Eof do
  begin
   DBComboBoxEh1.Items.Add(TempFdq.Fields[1].asstring);
   TempFdq.Next;
  end;
end;
----------------------------------------------
-
作者:
男 looper (keyo) ★☆☆☆☆ -
盒子活跃会员
2020/12/16 15:18:59
1楼: 感谢分享!
----------------------------------------------
虽千万人吾往矣!
作者:
男 dalas (dalas) ★☆☆☆☆ -
普通会员
2020/12/16 16:10:09
2楼: 模块化编程。。。。

原来如此,我用Form,偶尔出现你所说的 invalid pointer opertor错误,找不到原因。
----------------------------------------------
-
作者:
男 dalas (dalas) ★☆☆☆☆ -
普通会员
2020/12/16 19:56:40
3楼: 换一个思路,用于保存动态创建子窗口的数组,不要用TForm,用Pointer。
我反复测试了近1小时,没有出现invalid pointer opertor错误。楼主试试看
----------------------------------------------
-
作者:
男 bluestorm8 (bluestorm) ▲▲△△△ -
普通会员
2020/12/17 11:39:50
4楼: 应该用TFrame而不是用TForm来实现模块化编程
----------------------------------------------
-
作者:
男 sxqwhxq (步惊云) ★☆☆☆☆ -
普通会员
2020/12/17 12:01:33
5楼: @dalas (dalas)
换一个思路,用于保存动态创建子窗口的数组,不要用TForm,用Pointer。
我反复测试了近1小时,没有出现invalid pointer opertor错误。楼主试试看
----------
您说是设定一个Pointer指针数组来保存动态创建的窗口吗?估计还是会出错的。因为机制仍然是一样的。
没有出错可能是没有遇到这些操作:
您在动态创建的子窗口的代码模块中:
1、是否在子窗口中再次创建窗口并销毁吗?
2、有在子窗口中创建流并销毁吗?这个处理图片经常要用;
3、有在子窗口中再次创建对话框窗口,并在再次弹出的窗口中访问数据库吗?
以上这些操作都是一个复杂模块化项目中,经常要用到的。
----------------------------------------------
-
作者:
男 lxh524 (lxh524) ★☆☆☆☆ -
普通会员
2020/12/17 14:02:05
6楼: 模块化是DLL模式。跟你采用TFrame,TForm 没关系。这玩意要先要透彻理解释放动作。对程序员的要求有点高,不然动不动报地址错误。
----------------------------------------------
my computer
作者:
男 dalas (dalas) ★☆☆☆☆ -
普通会员
2020/12/17 17:38:09
7楼: @sxqwhxq (步惊云)

1、是否在子窗口中再次创建窗口并销毁吗?
3、有在子窗口中再次创建对话框窗口,并在再次弹出的窗口中访问数据库吗?

这两点有,第2点,虽然有处理图片,但我是直接用TBitmap,没有用流:
1、是加载数据时,显示个 “请稍候。。” 提示窗口,加载完销毁。
3、是加载Excel文件里的数据,并预览,用ADO存到数据库。
此帖子包含附件:
PNG 图像
大小:24.9K
----------------------------------------------
-
作者:
男 dalas (dalas) ★☆☆☆☆ -
普通会员
2020/12/17 17:40:47
8楼: 这是动态创建“座席分布视图”的代码,今天也一直在反复测试,没有报错
此帖子包含附件:
PNG 图像
大小:17.5K
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2020/12/17 17:52:02
9楼: 楼上你这样写代码,有点多此一举的感觉。
----------------------------------------------
-
作者:
男 dalas (dalas) ★☆☆☆☆ -
普通会员
2020/12/17 21:15:06
10楼: @ pcplayer (pcplayer)

现在用你的办法,改成这样了,不需要保存动态创建的窗口的指针,很方便,全局数组也不需要了
此帖子包含附件:
PNG 图像
大小:63.4K
----------------------------------------------
-
作者:
男 dalas (dalas) ★☆☆☆☆ -
普通会员
2020/12/17 21:18:54
11楼: 主窗口的Close或CloseQuery事件调用这个函数就可以

CloseChildForm(Self.Handle);  //排除主窗口自己


procedure CloseChildForm(Hand:THandle);
var
  iIndex:Integer;
begin
  for iIndex := 0 to Application.ComponentCount-1 do
  begin
    if Application.Components[iIndex] is TForm then
    if (Application.Components[iIndex] as TForm).Handle<>Hand then
      (Application.Components[iIndex] as TForm).Close;
  end;
end;
----------------------------------------------
-
作者:
男 thinknet (thinknet) ★☆☆☆☆ -
盒子活跃会员
2020/12/17 23:00:48
12楼: 动态创建窗体,不到万不得以别给实例名,不可能存在楼主遇到的问题。
我做的桌面项目几乎都是动态创建窗体,从没有过“invalid pointer ...."问题
如果一定要给动态创建的窗体实例名,维护好实例名在窗体Destory时赋值Nil就行了。
这些都是Delphi的基础操作,不应该有什么问题。
----------------------------------------------
-
作者:
男 wk_knife (wk_knife) ★☆☆☆☆ -
盒子活跃会员
2020/12/18 13:05:50
13楼: 用接口不香么?通过接口把form(各类view)的接口暴露出来。

一是可以使用接口的对象释放机制,二是可以把程序改成MVC、MVP、MVVM模式,视图里可以做到基本没有业务逻辑,界面变动基本不用动代码。
----------------------------------------------
-
作者:
男 sxqwhxq (步惊云) ★☆☆☆☆ -
普通会员
2020/12/18 15:40:01
14楼: 楼上的大神给点代码行不?
我以前用xe时,Tform确实没问题,用10.3就这样了。
可是混用接口和类,有时也麻烦。
反正,我是用Tframe就没问题了。
----------------------------------------------
-
作者:
男 dalas (dalas) ★☆☆☆☆ -
普通会员
2020/12/18 15:53:00
15楼: 我摸索着,感觉是一个数组,不能放不同类的实例。
虽然  TFrom1、TForm2 都是从 TForm 继承下来,但放了其他控件后,就已经成为一个新的类了,所以放到同一个数组就出问题。
我测试的 Tarray<TForm> 就会有问题,但换成 trray<Pointer> 就不会。
Pointer 只是一个纯粹的指针,所以相当于一个数组放是同一个类型。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2020/12/18 15:59:59
16楼: 楼上你的理解是错误的。即便出问题也不是因为这个原因。

就好比出了小偷在厕所偷东西的问题,原因并不是你把男人女人放进一个厕所导致。
----------------------------------------------
-
作者:
男 ysai (ysai) ★☆☆☆☆ -
盒子活跃会员
2020/12/23 9:51:55
17楼: 模块化?看看这个吧
https://bitbucket.org/sglienke/spring4d/
----------------------------------------------
-
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v3.0.1 版权所有 页面执行109.375毫秒 RSS