5

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#?

Kaveh Shahbazian
  • 13,088
  • 13
  • 80
  • 139
  • Are yo looking at writing VMs in F# or everything? I have WPF app with backend in F#, for MVVM we use Caliburn.Micro. All my VM are in C#. I also wonder if it's possible to use WPF strait from F#. I will try to rewrite one of the VM to F# and will see what happen. I let you know. – Sebastian.Belczyk May 12 '14 at 01:00
  • @Sebastian.Belczyk I would like to have all my stuff in F#. I do not like hybrid-lingual projects; they waste a lot of mental energy (probably other than markup languages & jQuery - It's not a language, I know). – Kaveh Shahbazian May 12 '14 at 16:09
  • I managed to have F# VM. I straggled a bit with circular dependencies, but once sorted it was fine. NotifyPropertyChanged implementation is a bit awkward but you can hide it in a base class. Next step would be to try solve problems mentioned by @PatrykĆwiek. – Sebastian.Belczyk May 12 '14 at 21:26

2 Answers2

5

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.

Patryk Ćwiek
  • 14,078
  • 3
  • 55
  • 76
  • It all looks like the friction occurs at the beginning. Once solved it may bring more value (like avoiding hybrid solution thus mental burden of switching technology @KavehShahbazian mentioned). It would be really nice to be able to use WPF and Caliburn.Micro in data intensive apps where F# is way better choice. – Sebastian.Belczyk May 12 '14 at 21:21
  • @Sebastian.Belczyk True, although it may seem that way because I didn't even manage to get past the very beginning. I can't say if there are any other dark corners which might or might not bite you at some point. I admit though, it would be nice if F# and Caliburn.Micro worked together... – Patryk Ćwiek May 13 '14 at 06:52
3

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.

TeaDrivenDev
  • 6,591
  • 33
  • 50
scrwtp
  • 13,437
  • 2
  • 26
  • 30
  • It took me forever to google the mystery out of this answer. It works, but you need to open Microsoft.FSharp.Quotations, and then Expr and Patterns.PropertyGet will be available. Thanks. – Bent Tranberg Aug 24 '16 at 06:38