Question

As I understand Caliburn.Micro does a considerable amount of auto-wiring and plumbing for a WPF project in a convention-based manner; employing MVVM.

Question: Are there any C# specific parts in Caliburn.Micro; which used some C#-only features? What parts of Caliburn.Micro can not be used with F# code? What do I lose by using F#?

Was it helpful?

Solution

Unfortunately, Caliburn.Micro and F# don't work that well together.

For example, Caliburn.Micro treats views as regular classes during convention matching (via reflection), which is true for C# - views are partial classes.

F# has no support for partial classes and views are XAML files only. That means they can't be resolved by Caliburn.Micro. It also means you will have problem wiring up IoC container in the bootstrapper, because it won't be able to create views - at least not without manual registration with Application.LoadComponent and such.

Setting up the boostrapper was also a pain, because you have to specify the key in App.xaml, but for whatever reason that key was not matched with the class. Bootstrapper<T> also passes bool useApplication = true to BootstrapperBase as a default - and F# had different problems depending whether that flag was set or not, unfortunately I don't remember the details. That's because in F# you wire up the entry point to the application yourself, while Caliburn.Micro is built to intercept that step automatically. That causes conflicts...

If you get it to work by overriding convention resolution mechanism completely and by bending any IoC container to your will, more power to you. In my opinion, currently there's just too much friction.

Personally, I'd set up the core foundation for the application in C# using Caliburn.Micro and do the 'real work' in F#. Alternatively, I think you can even have view models written in F# in separate project, views in C# project, then you'd just have to adjust how the convention lookup works in the bootstrapper. I'm not exactly sure about that approach though so your mileage may vary and proceed with caution.

OTHER TIPS

That's a nice question. If you go through with it, be sure to let us know how it goes. I was looking at Caliburn for a side project, but I didn't make much progress on it.

So to kick off, something you might have already stumbled upon:

Lambda expression overload of NotifyOfPropertyChange

The basic method for raising PropertyChanged events has two overrides. One that takes property name as a string, and another that takes a lambda expression. The latter uses a clever trick/ugly hack for extracting a property name from the expression's body. This gives you a nice, succinct syntax in C# that's to some extent checked by the compiler:

public string SomeProp
{
    get { return someField; }
    set
    {
        someField = value;
        NotifyOfPropertyChange(() => SomeProp);
    }
}

But this doesn't translate well to F#. Your choices are either to fallback to the string one, or sugar it by using quotations. I've used something like this:

let notify<'a> (notifier: PropertyChangedBase) (expr: Expr<'a>) =
    let name =
        match expr with
        | PropertyGet (_, pi, _) -> pi.Name
        | _ -> failwith "Can't get property name to notify"
    notifier.NotifyOfPropertyChange(name)

Which was called like this:

member this.SomeProp 
    with get () = 
        someField
    and set(value) = 
        someField <- value
        notify this <@ this.SomeProp @>

I believe you could take it a step further and roll it into your own PropertyChanged class built on top of PropertyChangedBase.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top