高手进,动态控件的销毁问题

sxjswift 2008-09-04 05:29:13
用FlowLayouPanel实现了一个WinForm动态界面。

添加控件时使用FlowLayouPanel.Controls.Add()

更新时,由于每次子Control都不一样,不能只更新子Control的属性,
所以先删除所有子控件,再添加新的子控件,
先FlowLayouPanel.Controls.Clear()清除Panel上的控件。
然后FlowLayouPanel.Controls.Add()添加新的控件。

每次更新这个FlowLayouPanel,
使用内存都增加,
多次,FlowLayouPanel.Controls.Add()处出现错误:创建窗口句柄时出错。

应该是
FlowLayouPanel.Controls.Clear()
时并没有销毁子控件的窗口句柄。

请问高手门有什么解决方案?
...全文
366 15 打赏 收藏 转发到动态 举报
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
bf234 2011-12-15
  • 打赏
  • 举报
回复
这帖子不错,学习了
helloDongXiu 2011-05-06
  • 打赏
  • 举报
回复
每天回帖即可获得10分可用分!
xue_smile 2009-07-02
  • 打赏
  • 举报
回复
关注中...
fengyecsdn 2008-09-09
  • 打赏
  • 举报
回复
我以前也作过全动态的东西。 确实存在资源浪费的问题。但是只要有办法实现管理机制,多困难也要实现。
因为只要你失去控制,必然会出现资源枯竭。 有句话,只要有可能出现的,就一定会出现。

其实全自定义的东西绝大多数的工作是在管理上。
sxjswift 2008-09-04
  • 打赏
  • 举报
回复
Relector反编译发现,实际上Controls.Clear()调用的Controls.RemoveAt(),所以实际是一样的

public virtual void Clear()
{
this.owner.SuspendLayout();
CommonProperties.xClearAllPreferredSizeCaches(this.owner);
try
{
while (this.Count != 0)
{
this.RemoveAt(this.Count - 1);
}
}
finally
{
this.owner.ResumeLayout();
}
}
public void RemoveAt(int index)
{
this.Remove(this[index]);
}


我实现的实际是xml定义的界面,用于设置属性,
而这个xml比较随机,所以控件只能新创建。

看反编译代码,时候Dispos的时候已经销毁窗口句柄了,但是仍然会出现“创建窗口句柄时出错”错误。


protected override void Dispose(bool disposing)
{
if (this.GetState(0x200000))
{
object obj2 = this.Properties.GetObject(PropBackBrush);
if (obj2 != null)
{
IntPtr handle = (IntPtr) obj2;
if (handle != IntPtr.Zero)
{
SafeNativeMethods.DeleteObject(new HandleRef(this, handle));
}
this.Properties.SetObject(PropBackBrush, null);
}
}
this.UpdateReflectParent(false);
if (disposing)
{
if (!this.GetState(0x1000))
{
if (this.GetState(0x40000))
{
throw new InvalidOperationException(SR.GetString("ClosingWhileCreatingHandle", new object[] { "Dispose" }));
}
this.SetState(0x1000, true);
this.SuspendLayout();
try
{
this.DisposeAxControls();
ContextMenu menu = (ContextMenu) this.Properties.GetObject(PropContextMenu);
if (menu != null)
{
menu.Disposed -= new EventHandler(this.DetachContextMenu);
}
this.ResetBindings();
if (this.IsHandleCreated)
{
this.DestroyHandle();
}
if (this.parent != null)
{
this.parent.Controls.Remove(this);
}
ControlCollection controls = (ControlCollection) this.Properties.GetObject(PropControlsCollection);
if (controls != null)
{
for (int i = 0; i < controls.Count; i++)
{
Control control = controls[i];
control.parent = null;
control.Dispose();
}
this.Properties.SetObject(PropControlsCollection, null);
}
base.Dispose(disposing);
}
finally
{
this.ResumeLayout(false);
this.SetState(0x1000, false);
this.SetState(0x800, true);
}
}
}
else
{
if (this.window != null)
{
this.window.ForceExitMessageLoop();
}
base.Dispose(disposing);
}
}

[EditorBrowsable(EditorBrowsableState.Advanced), SecurityPermission(SecurityAction.InheritanceDemand, Flags=SecurityPermissionFlag.UnmanagedCode), UIPermission(SecurityAction.LinkDemand, Window=UIPermissionWindow.AllWindows)]
protected virtual void DestroyHandle()
{
if (this.RecreatingHandle && (this.threadCallbackList != null))
{
lock (this.threadCallbackList)
{
if (threadCallbackMessage != 0)
{
NativeMethods.MSG msg = new NativeMethods.MSG();
if (UnsafeNativeMethods.PeekMessage(ref msg, new HandleRef(this, this.Handle), threadCallbackMessage, threadCallbackMessage, 0))
{
this.SetState(0x8000, true);
}
}
}
}
if (!this.RecreatingHandle && (this.threadCallbackList != null))
{
lock (this.threadCallbackList)
{
Exception exception = new ObjectDisposedException(base.GetType().Name);
while (this.threadCallbackList.Count > 0)
{
ThreadMethodEntry entry = (ThreadMethodEntry) this.threadCallbackList.Dequeue();
entry.exception = exception;
entry.Complete();
}
}
}
if ((0x40 & ((int) ((long) UnsafeNativeMethods.GetWindowLong(new HandleRef(this.window, this.InternalHandle), -20)))) != 0)
{
UnsafeNativeMethods.DefMDIChildProc(this.InternalHandle, 0x10, IntPtr.Zero, IntPtr.Zero);
}
else
{
this.window.DestroyHandle();
}
this.trackMouseEvent = null;
}

fengyecsdn 2008-09-04
  • 打赏
  • 举报
回复
控件本身其实不用必须DISPOSE,(有特殊系统资源请求的除外,比如数据访问,或者及其大的控件像WEB浏览器,或者是COM组件等等)一般的 标准控件都会被系统回收处理,但是时间上就没准了。

其实控件多不怕,适时的REMOVE走不用的控件,也还是会被系统释放的。但是要考虑频率和你是否真的释放了,比如你虽然REMOVE了,但是还有其他变量或者集合在引用着控件,那就释放不了啦。其实要注意少用不必要的变量,REMOVE之后,把引用变量=NULL也好。当然了 你主动的DISPOSE还是好习惯的。

我看问题主要是出在楼主总是ADD的问题上。
对于以后还要用到的控件,为什么要删了新来一个呢? 为什么不隐藏起来以后再用呢?
更好的办法是 你吧你的流程做成几个帧,一组一组的包装成一个大控件(可以是USERCONTROL)
然后一页一页的显示这些大控件。用过也不要扔掉,藏起来就好,需要回退的时候就显示出来。
luluyy 2008-09-04
  • 打赏
  • 举报
回复
[Quote=引用 6 楼 sxjswift 的回复:]
按我的理解Controls.Remove ()应该和Controls.Clear() 一样作用吧
[/Quote]
不一样的remove是指定移除其中的一个 而clear会把所有的控件全部清空


比如Button btn=new Button();
btn.name="1";
btn.text="2";
btn.location=new point();
btn.........
FlowLayouPanel.Controls.Add(btn);
如果是多个Button 你可以写一个循环
for(int i=1;i<10;i++)
{
btn.name="1";
btn.text="2";
btn.location=new point();
btn.........
FlowLayouPanel.Controls.Add(btn);
}
当然生成的个数不是就这样写死` 你可以从数据库啊xml文件什么地方读要生成的个数
这样每个BUTTON都是有有名字的`就不会出现那样的错了`~~ Button的属性在btn.后面都可以找到
你可以把它的位置大小显示的字体什么的都可以放到配置文件或数据库中,这样就可以变动了




xiaoyue520 2008-09-04
  • 打赏
  • 举报
回复
接分中.........
sunshine_anycall 2008-09-04
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 KermitYue 的回复:]
dispose释放资源
[/Quote]
但是释放资源没有那么块,及时是调了这个函数也只是告诉了内存回收器说这个可以回收了,但是不会立即回收,C#不是C++
sxjswift 2008-09-04
  • 打赏
  • 举报
回复
按我的理解Controls.Remove ()应该和Controls.Clear() 一样作用吧
KermitYue 2008-09-04
  • 打赏
  • 举报
回复
dispose释放资源
ZengHD 2008-09-04
  • 打赏
  • 举报
回复
试试 Controls.Remove ()
sxjswift 2008-09-04
  • 打赏
  • 举报
回复

private void DisposeControls(Control cParent)
{
foreach (Control c in cParent.Controls)
{
DisposeControls(c);
c.Dispose();
}
}


试过了,还是这样的
行者无疆-Kevin 2008-09-04
  • 打赏
  • 举报
回复
遍历父控件中的所有子控件
然后 Dispose
LQknife 2008-09-04
  • 打赏
  • 举报
回复
Dispose才释放资源吧

111,120

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧