7

I know this is pretty esoteric but hoping someone has a solution for me.

I have an application that started life as WPF, moved to UWP and now lives primarily as XF/MAUI and WinUI.

I'm wanting to port this to macOS and essentially just replace the UI and retain the MVVM architecture underneath and write as little glue as possible. I'm using Xamarin Mac and C# to do this.

Cocoa supports bindings but the implementation is incompatible with the standard Microsoft MVVM pattern.

My work around at present is to do something like this:

public class LoginModel: NSObject
{
    private LoginViewModel _wrappedVm; // Implements both INotifyPropertyChanged and INotifyPropertyChanging

    /* Reimplement properties on LoginViewModel here and forward to the underlying view model */

    public LoginModel()
    {
        // snip: create the view model
        _wrappedVm.PropertyChanged += (s, e) =>
        {
           DidChangeValue(e.PropertyName);
        }

        _wrappedVm.PropertyChanging += (s, e) =>
        {
           WillChangeValue(e.PropertyName);
        }
    }
}

There is obviously a lot of boilerplate here but it works. I'd love for there to be a way to bind directly to the MVVM view model without an intermediate step, and as a compromise would use a proxy. I haven't investigated proxying it, but given my understanding that the model needs to be an NSObject there might be challenges in doing this.

Sam
  • 4,219
  • 7
  • 52
  • 80
  • 1
    Another idea could be to keep your current approach, but generate the `LoginModel` instead of coding it by hand. We've done something similar in the past (using T4, although I don't know the current state of T4 on VS for Mac). Your model definitely needs to be a subclass of `NSObject` to integrate with Cocoa bindings. – TheNextman May 11 '22 at 15:14
  • You can always template this wrapping with a class like: `public class ObservableObject : NSObject where T: INotifyPropertyChanging, INotifyPropertyChanged` and copy the exact same structure you present but with type of `T` instead of `LoginViewModel` – Andreas M. May 12 '22 at 03:33

1 Answers1

-1

Not a big change, but should reduce the boilerplate assumption here is that the DidChangeValue and the WillChangeValue have to be implemented in the ViewModel anyway

public interface INSObjectBindableView
{
   void DidChangeValue(string propertyName);
   void WillChangeValue(string propertyName);
}

public static class NSObjectBindableEx
{
   public static void Bind(this INSObjectBindableView view, NSObject model)
   {
        view.PropertyChanged += (s, e) =>
        {
           DidChangeValue(e.PropertyName);
        }

        view.PropertyChanging += (s, e) =>
        {
           WillChangeValue(e.PropertyName);
        }
   }
}


this allows you to keep the binding code to a single line inside the NSObject sub-class like


public class LoginModel: NSObject
{
    private LoginViewModel _wrappedVm; 

    public LoginModel()
    {
      _wrappedVm.Bind(this);
    }
}

Surya Pratap
  • 495
  • 8
  • 17