1

I am trying to use compiled bindings on a project realised with the Catel MVVM Framework (should not matter). In the generated code however I get compiler errors because my ViewModel does not implement certain functions normally exposed by DependencyObjects:

CS1061 'MainViewModel' does not contain a definition for 'RegisterPropertyChangedCallback' and no extension method 'RegisterPropertyChangedCallback' accepting a first argument of type 'MainViewModel' could be found (are you missing a using directive or an assembly reference?)

Also:

CS0039 Cannot convert type 'Windows.UI.Xaml.DependencyObject' to 'CatelCompiledBindingTest.ViewModels.MainViewModel' via a reference conversion, boxing conversion, unboxing conversion, wrapping conversion, or null type conversion

The x:Bind compiler-generated code throwing the errors is the following:

public void DependencyPropertyChanged_VM_Visible(global::Windows.UI.Xaml.DependencyObject sender, global::Windows.UI.Xaml.DependencyProperty prop)
{
    MainView_obj1_Bindings bindings = TryGetBindingObject();
    if (bindings != null)
    {
        global::CatelCompiledBindingTest.ViewModels.MainViewModel obj = sender as global::CatelCompiledBindingTest.ViewModels.MainViewModel;
        if (obj != null)
        {
            bindings.Update_VM_Visible(obj.Visible, DATA_CHANGED);
        }
    }
}
private global::CatelCompiledBindingTest.ViewModels.MainViewModel cache_VM = null;
private long tokenDPC_VM_Visible = 0;
public void UpdateChildListeners_VM(global::CatelCompiledBindingTest.ViewModels.MainViewModel obj)
{
    if (obj != cache_VM)
    {
        if (cache_VM != null)
        {
            ((global::System.ComponentModel.INotifyPropertyChanged)cache_VM).PropertyChanged -= PropertyChanged_VM;
            cache_VM.UnregisterPropertyChangedCallback(global::CatelCompiledBindingTest.ViewModels.MainViewModel.VisibleProperty, tokenDPC_VM_Visible);
            cache_VM = null;
        }
        if (obj != null)
        {
            cache_VM = obj;
            ((global::System.ComponentModel.INotifyPropertyChanged)obj).PropertyChanged += PropertyChanged_VM;
            tokenDPC_VM_Visible = obj.RegisterPropertyChangedCallback(global::CatelCompiledBindingTest.ViewModels.MainViewModel.VisibleProperty, DependencyPropertyChanged_VM_Visible);
        }
    }
}

The MainViewModel is fairly simple (with Catel boilerplate code excluded):

public class MainViewModel : ViewModelBase
{
    public MainViewModel()
    {
        ToggleVisibility = new Command(OnToggleVisibilityExecute);
        AddText = new Command(OnAddTextExecute);
        SomeText = "";
    }

    //ViewModel properties

    public string SomeText
    {
        get { return GetValue<string>(SometextProperty); }
        set { SetValue(SometextProperty, value); }
    }

    public bool Visible
    {
        get { return GetValue<bool>(VisibleProperty); }
        set { SetValue(VisibleProperty, value); }
    }

    //ViewModel Commands

    public Command ToggleVisibility { get; private set; }

    public void OnToggleVisibilityExecute()
    {
        Visible = !Visible;
    }

    public Command AddText { get; private set; }

    public void OnAddTextExecute()
    {
        SomeText += "Random text! ";
    }
}

The View is the following:

<controls:Page
    x:Class="CatelCompiledBindingTest.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:CatelCompiledBindingTest.Views"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:controls="using:Catel.Windows.Controls"
    xmlns:converters="using:Catel.MVVM.Converters"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Page.Resources>
        <converters:BooleanToCollapsingVisibilityConverter x:Key="BooleanToVisibilityConverter"/>
    </Page.Resources>

    <Grid>
        <Grid.KeyboardAccelerators>
            <KeyboardAccelerator Invoked="{x:Bind VM.OnToggleVisibilityExecute}" Key="T" Modifiers="Control"/>
            <KeyboardAccelerator Invoked="{x:Bind VM.OnAddTextExecute}" Key="Add" Modifiers="Control"/>
        </Grid.KeyboardAccelerators>

        <Border Width="500" Height="500" Visibility="{x:Bind VM.Visible, Mode=OneWay}" Background="SlateGray">
            <TextBlock Style="{ThemeResource HeaderTextBlockStyle}" Text="{x:Bind VM.SomeText, Mode=OneWay}"/>
        </Border>

    </Grid>
</controls:Page>

With the following code-behind to be able to use compiled bindings:

public sealed partial class MainView : Page
{
    public MainView()
    {
        this.InitializeComponent();
        VM = DataContext as MainViewModel;
    }

    public MainViewModel VM { get; set; }
}

The question now is: Why does the compiler assume MainViewModel to be of Type DependencyObjectin the compiler-generated DependencyPropertyChanged_VM_Visible? According to Data Binding in Depth the ViewModel simply neeeds to implement IPropertyChangedwhich it does.

How do I resolve this issue (apart from resorting to {Binding}again)? Which namespaces do I need to include in the ViewModel for compiled Bindings to work?

TradeItEasy
  • 129
  • 8
  • if your project is in initial stages, may I suggest "Windows Template Studio" for generating your uwp project, it literally deals all kinds of biolerplate for you including many popular MVVM frameworks, and you can focus on your business logic, its viewmodel classes also come with build in observable stuff for the two way binding, have a look : https://github.com/Microsoft/WindowsTemplateStudio – Muhammad Touseef Aug 16 '18 at 16:01
  • I already tried that, but have changed from TemplateStudio/MVVMLight to Catel because Catel is much more rich in features - especially regarding nested user Controls. – TradeItEasy Aug 16 '18 at 16:03
  • 1
    then hopefully a catel expert can help you.seems like catel uses a different way of doing bindings – Muhammad Touseef Aug 16 '18 at 16:05

1 Answers1

0

It assumes it's a dependency property because the VM property is declared on the view MainView. You should define the VM property as a dependency property so it can be bound to (so not the value / type of the property, but the property itself).

Geert van Horrik
  • 5,689
  • 1
  • 18
  • 32
  • Hey, Geert, thanks for your reply. That was one of the first things I assumed as well but declaring `VM` as a dependency property does not help. In a reference project using MVVMLight with almost exactly the same code the approach worked fine even with `VM` just declared as a regular property. Comparing the MainView.g.cs between the projects reveals that the erroneous lines are not created by the compiler in the reference project. Does Catel handle Bindings differently? Which tool generates the g.cs? MSBuild? I didn't find anything open source yet to see what it's looking for... – TradeItEasy Aug 18 '18 at 00:07
  • Can you provide a repro on GitHub so we can look into it? – Geert van Horrik Aug 20 '18 at 07:32
  • I have created a repository here: https://github.com/TradeItEasy/Compiled-Bindings I really appreciate you looking into it! – TradeItEasy Aug 20 '18 at 15:58
  • Hey, Geert, did you have time to look into the issue? Could you reproduce the issue or is it dumb me? – TradeItEasy Aug 28 '18 at 12:00