确定组件的所有者何时加载

Posted

技术标签:

【中文标题】确定组件的所有者何时加载【英文标题】:Determining when the Owner of a Component has Loaded 【发布时间】:2013-06-24 08:01:27 【问题描述】:

我创建了一个包含自定义组件的 WinForms 应用程序。

组件需要在启动时触发其中一个事件,但在调用组件的构造函数时,所有事件处理程序仍为空。

我需要的是一个事件,它告诉我拥有该组件的窗口已加载并且所有事件处理程序都已设置。

但是,组件似乎没有Load 事件。事实上,除了Disposed 事件之外,它们似乎根本不附带任何事件。

我的组件如何知道何时可以安全地在启动时触发事件?

【问题讨论】:

目前还不清楚 any 代码是如何在您的组件中运行可能触发事件的。这只有在表单类中的代码明确要求它做某事时才有效。或者如果你启动一个线程,在这种情况下你会遇到更大的问题。您应该关注“当表单类显式询问时”的角度,这就是所有组件的工作方式。您可以使用 this trick 订阅表单的 Load 事件。 这个组件管理所有的文件命令。启动应用程序时,会隐式执行 New 命令。如果组件可以触发指示文件已更改的事件,则表单可以有一个处理程序来处理文件已更改的每种情况(包括应用程序刚启动时)。此外,如果我在构造函数中初始化组件的公共属性,它们会在表单加载时为空。构造函数执行此初始化还为时过早。这对我来说似乎是一个相当大的疏忽。 您还没有真正解决更大的问题,您的组件如何知道文件已更改?该代码如何运行?如果您使用 FileSystemWatcher,那么您会在工作线程上收到一个事件,这是更大的问题。您需要发布一个 sn-p,这太模糊了。 我的组件将处理程序添加到表单的文件命令中。例如,如果当前文件被修改,open 命令会保存当前文件,然后显示一个打开文件对话框,用户可以在其中选择要打开的文件。如果选择了一个文件,它会触发它的 FileOpen 事件(表单可以在其中执行文件的实际读取)。如果没有发出错误信号,则组件知道文件已更改并触发它的 FileChanged 事件。组件管理所有这些,跟踪当前文件是否被修改等。因此它知道文件何时发生变化。 这一切对我来说毫无意义。 Anyhoo,我给了你一个链接到一个链接表单加载事件的方法。实现 ISupportInitialize 接口是另一种方法,在订阅事件处理程序后调用 EndInit() 方法。 【参考方案1】:

一种可能的解决方案是在组件连接到侦听器时触发事件。您需要创建自己的事件属性。

class MyClass

    private static readonly _myEvent = new object();

    private EventHandlerList _handlers = new EventHandlerList();

    public event EventHandler MyEvent
    
        add 
         
            _handlers.AddHandler(_myEvent, value); 
           OnMyEvent(); // fire the startup event
        
        remove  _handlers.RemoveHandler(_myEvent, value); 
    

    private void OnMyEvent()
    
        EventHandler myEvent = _handlers[_myEvent] as EventHandler;
        if (myEvent != null) myEvent(this, EventArgs.Empty);
    

    ...


【讨论】:

组件如何知道何时发生这种情况? 查看事件属性的添加部分。 谢谢。不过,这似乎真的很不幸。我有许多需要执行的事件和初始化,用户可能永远不会添加处理程序。我可以解决它,但它会变得相当复杂。对我来说,这似乎是组件设计中缺少的部分。 @JonathanWood loaded component 的定义是什么?组件的声明何时完成?何时完成组件的一些初始化?还是别的什么? @KingKing:嗯,我的简短回答是拥有该组件的窗口已被加载。我更长的答案是,该组件已经完全初始化,与拥有它的窗口有关。例如,如果您在表单设计器中声明组件事件的处理程序,那么直到实际设置这些处理程序之后,事情才会完全初始化。【参考方案2】:

至少有两种不同的方式。 第一种方法是在设计时使用站点跟踪容器(运行时不调用站点)。它只是在设计时保存 ContainerControl 属性,因此在运行时可以使用它。 您可以在框架的某些组件的属性浏览器中看到它。

    private ContainerControl _containerControl;

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
    public ContainerControl ContainerControl
    
        get  return _containerControl; 
        set
        
            _containerControl = value;
            if (DesignMode || _containerControl == null)
                return;

            if (_containerControl is Form)
                ((Form) _containerControl).Load += (sender, args) =>  Load(); ;
            else if (_containerControl is UserControl)
                ((UserControl)_containerControl).Load += (sender, args) =>  Load(); ;
            else
                System.Diagnostics.Debug.WriteLine("Unknown container type. Cannot setup initialization.");
        
    

    [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
    [Browsable(false)]
    public override ISite Site
    
        get  return base.Site; 
        set
        
            base.Site = value;
            if (value == null)
                return;

            IDesignerHost host = value.GetService(typeof(IDesignerHost)) as IDesignerHost;
            if (host == null)
                return;

            IComponent componentHost = host.RootComponent;

            if (componentHost is ContainerControl)
                ContainerControl = componentHost as ContainerControl;
        
    

    private void Load()
    

    

第二种方法是在Component中实现ISupportInitialize。 在这种情况下,Visual Studio (2013) 在设计时生成调用组件上的 ISupportInitialize 方法(BeginInit 和 EndInit)的代码。

【讨论】:

以上是关于确定组件的所有者何时加载的主要内容,如果未能解决你的问题,请参考以下文章

如何知道 vue.js 中何时加载了所有子组件

在反应中,只有在所有数据可用后才显示组件?

Vue 检查异步组件何时加载

渲染页面/加载所有组件时的事件

何时为动态加载的角度组件触发 OnInit 事件?

角度-知道模板渲染何时完成-使用异步管道