Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Databinding a F# viewmodel

Tags:

wpf

xaml

f#

From the XAML editor, I can set the namespace to the Viewmodel contained in a C# project

namespace ViewModelDB
{
    public class DependencyViewModel : IViewModelDB
    {
        public string Message { get; set; }
    }
}

And in my xaml

<UserControl
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:ViewModelDB="clr-namespace:ViewModelDB;assembly=ViewModelDB"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
        >
    <UserControl.DataContext>
        <ViewModelDB:DependencyViewModel/>
    </UserControl.DataContext>
    <Grid>
        <TextBlock Text="{Binding Message}"/>
    </Grid>
</UserControl>

The binding "Message" is then recognized.

When I point at a F# namespace of similar constituency

namespace ModuleDBGraph

open Infrastructure
open Microsoft.Practices.Prism.Regions;
open Microsoft.Practices.Unity;

type IDependencyViewModel =
    inherit IViewModel
    abstract Message : string with get, set

type DependencyViewModel () = 
    interface IDependencyViewModel with 
        member val Message = "" with get, set

I then loose the recognition of the binding Message

<UserControl
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:ViewModelDB="clr-namespace:ViewModelDB;assembly=ViewModelDB"
             xmlns:ViewModelDBFS="clr-namespace:ModuleDBGraph;assembly=ViewModelDBGraphFS"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
        >
    <UserControl.DataContext>
        <ViewModelDBFS:DependencyViewModel/>
    </UserControl.DataContext>
    <Grid>
        <TextBlock Text="{Binding Message}"/>
    </Grid>
</UserControl>

Am I doing something wrong ? This is due to Message being an implementation of the interface IDependencyViewModel and explicit implementation of interfaces in F#, which is a good thing, but is there away to work around this here ?

like image 988
nicolas Avatar asked Mar 18 '13 11:03

nicolas


1 Answers

I don't think there is a better solution than the one we discussed in comments, so I'm turning that into a longer answer.

The reason why it does not work is - as you already suggested - that F# implements interfaces explicitly and so WPF does not see the Message property when it is an interface member. The most direct workaround is to define it as an explicit property (and the interface implementation can just refer to the main property):

type DependencyViewModel () = 
    member val Message = "" with get, set
    interface IDependencyViewModel with 
        member x.Message with get() = x.Message and set(v) = x.Message <- v

In general, I think the patterns recommended for C# do not always work nicely in F#. For example, because F# is more succinct (making things easier to rewrite) and less error-prone (catching more bugs statically), I think you might not actually need an interface in this case at all.

A more complicated workaround would be to generate an implicit implementation of the interface using reflection at runtime (from the explicit implementation) and then set it as DataContext but that would not work nicely with editors, so maybe that's not a good direction.

like image 114
Tomas Petricek Avatar answered Sep 24 '22 18:09

Tomas Petricek