Search code examples
c#wpfmvvmpostsharpcode-injection

Injecting properties into .NET classes post-compile


I'd like to implement the ViewModel part of WPF's MVVM pattern without referencing WPF assemblies. The problematic part is command routing, which requires that ViewModels implement properties of type ICommand so that command bindings can work.

Now, I can avoid the ICommand and simply declare the properties as object. Everything still works, so that's that. But what bothers me is, I still have to declare them, and I really don't want to, because they feel like boiler plate code.

My ViewModels currently look like this:

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; }
}

The CommandHandlerAttribute enables runtime discovery of command handlers (an Action and an optional Func<bool>), while the BoundPropertyAttribute is really an aspect that injects itself into the property setter and calls INotifyPropertyChanged. I accompish this by using a compile time IL weaver.

Ideally, I'd like to make the last line (the SayHello property) implicit, too. There would be no point in having it there in the source if it wasn't for WPF's requirement.

So, naturally, I'm thinking of using the CommandHandlerAttribute aspect to inject the necessary IL into class and essentially creating the property post-compile. This is quite hard, although a good IL weaver (such as PostSharp) can go a long way to make it easier.

Before I embark on this journey, I'd like to hear what you all think of my approach. Is it sound? Is there a better way? How would/do you do it?


Solution

  • To me this sounds too clever by far. There's too much "magic" happening. In particular, I dislike the magic strings and other aspects of your CommandHandlerAttribute. That said, if I were to go down this route, I'd use something akin to the EventAggregator but for commands. IOW, SayHello wouldn't exist on your ViewModel at all. What ever magic creates the command bindings to SayHell() and CanSayHello() would instead locate the command in the global CommandAggregator. As long as we're using magic strings for this, the commands in the CommandAggregator could be lazily created, thus requiring no "boiler plate" coding on your part. All that's left is to create some XAML magic (markup extension) to specify the command on the ICommandSource.

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