在编译后将属性注入.NET类

时间:2009-03-24 15:47:41

标签: c# wpf mvvm postsharp code-injection

我想在不引用WPF程序集的情况下实现WPF MVVM模式的ViewModel部分。有问题的部分是命令路由,它要求ViewModel实现类型ICommand的属性,以便命令绑定可以工作。

现在,我可以避开ICommand,只需将属性声明为object即可。一切仍然有效,所以就是这样。但困扰我的是,我仍然需要声明它们,我真的不想,因为他们感觉像锅炉板代码

我的ViewModel目前看起来像这样:

public class HelloWorldViewModel : ViewModel
{
    [BoundProperty]
    public string Name { get; set; }

    [CommandHandler("SayHello")]
    public bool CanSayHello()
    {
        return Name != "" && Name != null;
    }

    [CommandHandler("SayHello")]
    public void SayHello()
    {
        View.ShowMessage("Hello, {0}!", Name);
    }

    public object SayHello { get; private set; }
}

CommandHandlerAttribute启用命令处理程序的运行时发现(Action和可选的Func<bool>),而BoundPropertyAttribute实际上是方面将自身注入属性设置器并调用INotifyPropertyChanged。我通过使用编译时间IL weaver 来实现这一点。

理想情况下,我也想隐含最后一行(SayHello属性)。如果没有WPF的要求,那么在源代码中没有任何意义。

所以,当然,我正在考虑使用CommandHandlerAttribute方面将必要的IL注入到类中,并且基本上创建编译后的属性。这很难,虽然一个好的IL编织器(例如 PostSharp )可以大大简化它。

在我开始这段旅程之前,我想听听你们对我的方法的看法。听起来不错吗?有没有更好的办法?你会怎么做?

5 个答案:

答案 0 :(得分:3)

到目前为止,这听起来太聪明了。发生了太多的“魔法”。特别是,我不喜欢你的CommandHandlerAttribute的魔术字符串和其他方面。也就是说,如果我沿着这条路走下去,我会使用类似于EventAggregator的东西但是用于命令。 IOW,SayHello根本不存在于你的ViewModel上。什么魔术创建命令绑定到SayHell()和CanSayHello()将改为在全局CommandAggregator中找到命令。只要我们使用魔术字符串,CommandAggregator中的命令就可以延迟创建,因此您不需要“锅炉板”编码。剩下的就是创建一些XAML魔术(标记扩展)来指定ICommandSource上的命令。

<Button Command="{my:AggregateCommand SayHello}"/>

答案 1 :(得分:2)

我建议你看看这是如何实现的,它会有所帮助:

“有点魔力” 轻松的INotifyPropertyChanged

[http://visualstudiogallery.msdn.microsoft.com/d5cd6aa1-57a5-4aaa-a2be-969c6db7f88a] [1]

作为将其添加到一个属性的示例:

[Magic] 
public string Name { get { return _name; } set { _name = value; } } 
string _name;

将其添加到所有类属性的另一个示例:

[Magic] 
public class MyViewModel: INotifyPropertyChanged 
{ 
  public string Name { get; set; } 
  public string LastName { get; set; } 
  ..... 
}

答案 2 :(得分:1)

在玩Prism之后的一段时间,但在我看起来像MVVM的东西之前,我想出了一个我认为有一定效力的策略:

我基于反射创建了ICommand接口的实现。构造函数接受目标对象和操作名称。使用反射,代码查找名为“[operation]”的方法,名称为“Can [operation]”或“[operation] Enabled”的属性或方法以及名称为“Can [operation] Changed”或“[操作]启用已更改“。只需要第一个,但反射的方法/属性/事件连接到ICommand接口的非常基本的实现。

然后我创建了一个IValueConverter实现,它将创建前一个类的实例,传递要转换为目标对象的值,并将转换器的参数作为操作名称。

鉴于上述组件,我可以,例如,将按钮的Command属性直接绑定到操作源(以及指定转换器),并将Button的CommandParameter属性设置为操作的名称。通过这种方式,我获得了声明性命令绑定,而没有命令源具有任何WPF的肉体知识。

答案 3 :(得分:0)

我个人认为这很有意思,但我会避免这种情况。

避免样板代码(或感觉像锅炉板代码的代码)会产生影响。这似乎是一个好主意,因为你不是经常重新安排事情,但从长远来看,你会让它变得不那么容易理解。

就个人而言,我尝试设置好的代码模板来为我插入样板代码,并将其包装在区域中,以便我可以在源代码中隐藏它。在这种情况下用锅炉板填充文件所需的30秒比我花费的2小时更少痛苦,两年后当我试图理解代码时,或者更糟糕的是,这两周有人两年后,当他们试图理解我的代码时花了两年......

答案 4 :(得分:0)

您认为最好的方法是代理或装饰模式。您可以在运行时期间使用UI / WPF内容成员包装/修饰的低级实体。这是节省时间的最简单但有效的方法,不需要考虑框架,注射等。

唯一的事情是你必须设计一些小型基础设施来用适当的装饰器包装你的实体。

相关问题