最近微软团队碰到一个不大不小的难题,搞了1个月也没解决掉,最后甚至打算提交给上海微软开Case来解决。
问题症结是:微软团队为我们开发一个StandardControl控件,组合了多个控件和组件,包括第三方的DevExpress,然而,当在工程的窗体中拖进StandardControl、编辑窗体控件属性、重新打开窗体,然后在调试(按F5)状态下,程序运行的好好,但IDE中这个窗体却不见了,而当退回到设计状态时,IDE甚至报错:“设计器加载程序未提供根组件,但没有指出原因。”由于IDE没有提供更多的提示,再加上有时报错是DevExpress控件报出的,所以怀疑是第三方控件造成的问题,还得我们帮忙来解决。
其实魔术拆穿了,很简单。
遮挡微软团队视角的有这几个方面:
1,认为.NET、IDE会自动控制资源的释放:这是一入道就玩.NET的人常犯的错误,如果是从C、Pascal等语言训练过来的人,对资源的加载和释放都是非常敏感的,特别是释放资源的事情。
2,不清楚特殊条件下如何调试代码:IDE不是万能的,一旦不允许对代码进行单步调试的时候怎么办,其实很简单,在需要设断点而无法调试到的代码前后,可以加上MessageBox()、或者写日志,总之,只要是能执行的代码,都是可以跟踪到的。
那么,只要具备应付这两问题的编程素质,所谓的难题自然不会困扰他很久。
我们通过在代码中设置MessageBox(),搞清楚IDE在构建、释放user control的时候都做了些什么动作,得出一个结论:IDE在释放资源的时候,并未按照设计者的思路行事。原因是user control中加入的组件BarManager与StandardControl有着指针的关联,一定的IDE环境下,IDE的资源释放顺序发生错乱,而最终导致IDE都无法分辨的莫名其妙故障。
那么到此,解决方法就很简单了,干预IDE的资源释放顺序就可以了。
以下是摘录的代码段,供设计.NET的组合控件时参考。
- public class StandardControlDesigner : ControlDesigner
- {
- public StandardControlDesigner() { }
- private StandardControl ContextControl
- {
- get { return this.Control as StandardControl; }
- }
- private IComponentChangeService _changeService;
- private BarManager _barManager;
- public override void Initialize(IComponent component)
- {
- base.Initialize(component);
- if (_changeService == null)
- {
- _changeService = GetService(typeof(IComponentChangeService)) as IComponentChangeService;
- if (_changeService != null)
- _changeService.ComponentRemoving += new ComponentEventHandler(OnComponentRemoving);
- }
- }
- public override void InitializeNewComponent(IDictionary defaultValues)
- {
- base.InitializeNewComponent(defaultValues);
- CreateMenu();
- }
- protected override void Dispose(bool disposing)
- {
- if (disposing)
- {
- ClearContainer();
- if (_barManager != null)
- _barManager.Dispose();
- }
- base.Dispose(disposing);
- }
- private void OnComponentRemoving(object sender, ComponentEventArgs e)
- {
- if (ContextControl == e.Component)
- ClearContainer();
- else if (_barManager == e.Component)
- _barManager = null;
- }
- private void ClearContainer()
- {
- if (ContextControl.Container != null)
- foreach (Component item in ContextControl.Container.Components)
- {
- BarManager temp = item as BarManager;
- if (temp != null)
- if (temp.Form == ContextControl)
- {
- if (_barManager == temp)
- _barManager = null;
- _changeService.ComponentRemoving -= new ComponentEventHandler(OnComponentRemoving);
- try
- {
- temp.Dispose();
- }
- finally
- {
- _changeService.ComponentRemoving += new ComponentEventHandler(OnComponentRemoving);
- }
- break;
- }
- }
- }
- private void CreateMenu()
- {
- IDesignerHost host = GetService(typeof(IDesignerHost)) as IDesignerHost;
- if (host == null)
- return;
- if (_barManager != null)
- _barManager.Dispose();
- _barManager = new BarManager(ContextControl.Container);
- _barManager.Form = ContextControl;
- ...
- }
- }
- }