创建可设计的.NET组件时,需要提供默认构造函数。来自 IComponent 文档:
要成为组件,类必须 实现 IComponent 接口并 提供一个基本的构造函数 不需要参数或单个 IContainer 类型的参数。
这使得无法通过构造函数参数进行依赖注入。 (可以提供额外的构造函数,但设计者会忽略它们。)我们正在考虑的一些替代方案:
服务定位器
不要使用依赖注入,而是使用服务定位器模式来获取依赖项。这似乎就是 IComponent.Site.GetService 的用途。我想我们可以创建一个可重用的 ISite 实现(ConfigurableServiceLocator?),它可以配置必要的依赖项。但这在设计师环境中如何运作?
通过属性进行依赖注入
通过属性注入依赖项。 提供默认实例(如果有) 需要在a中显示该组件 设计师。记录哪些属性 需要注射。
使用 Initialize 方法注入依赖项
这很像通过属性注入,但它将需要注入的依赖项列表保留在一处。这样,所需依赖项的列表就会被隐式记录,当列表更改时,编译器将帮助您解决错误。
知道这里的最佳实践是什么吗?你是怎么做到的?
同样的问题困扰了我很长时间,直到我意识到我的思考方式是错误的。 AFAIR,创建
IComponent
实现的唯一原因是提供设计时功能 - IComponent
实现没有运行时效果。
由此推论,这意味着您应该主要创建组件来实现设计时功能。特别是对于控件来说,这意味着您可以配置组件以某种方式运行。认识到这与组件的实际行为方式或它显示的数据完全不同,这一点非常重要。它不应该在设计时有行为,也不应该包含数据。
因此,对构造函数的约束实际上是一件好事,因为它会指导您重新思考您的设计。控件是一种与数据源无关的软件,以某种方式显示数据并与数据交互。只要该数据符合某些接口等,控件就会满意。控制端不关心数据如何到达,也不应该关心。让 Control 控制数据的加载和修改方式将是错误的。
在 WPF 中,这比在 Windows 窗体中明确得多:您为特定的 Control 提供一个 DataContext 并将该 Control 的属性绑定到该 DataContext 的成员。 DataContext(可以是任何对象)源自控件外部;这就是你的表示层的责任。
在 Windows 窗体中,您仍然可以通过将数据上下文分配给控件来执行相同的操作。本质上,这是属性注入——只是要注意你不应该注入服务;你应该注入数据。
总而言之,我不会接受你的任何建议。相反,让控件具有一个或多个属性,允许您将数据分配给控件,并使用数据绑定来绑定此数据。在控件的实现内部,准备好处理没有数据的情况:每次设计时控件由 VS 托管时都会发生这种情况。空对象模式对于实现这种弹性非常有用。
然后,从控制器设置数据上下文。这就是 MVC 的实现方式:控件是视图,但您应该有一个单独的控制器,可以实例化模型并将其分配给视图。