0

We have been trying to get ToProperty to work correctly but are experiencing exceptions which aren't being caught by ThrownExceptions.

Our test view model looks like:

    public class ViewModel : ReactiveObject, ISupportsActivation
{
    private readonly ViewModelActivator _activator = new ViewModelActivator();

    public ViewModelActivator Activator
    {
        get { return _activator; }
    }

    private ObservableAsPropertyHelper<String> _output;

    public string Output
    {
        get { return _output.Value; }
    }

    public ViewModel()
    {
        this.WhenActivated(delegate(Action<IDisposable> action)
        {
            action(
                Run()
                    .ToObservable()
                    .ToProperty(this, vm => vm.Output, out _output, "INIT", RxApp.MainThreadScheduler)
                    .ThrownExceptions.Subscribe(ex => Debug.WriteLine("Exception {0}", ex.Message)));
        });
    }

    public async Task<string> Run()
    {
        Debug.WriteLine("Running : Begin");

        await Task.Delay(5000);
        throw new InvalidOperationException("no way hosay");

        Debug.WriteLine("Running : End");
        return "Assigned";
    }
}

and our somewhat simple view looks like

public sealed partial class MainPage : Page,IViewFor<ViewModel>
{
    public MainPage()
    {
        this.InitializeComponent();

        this.NavigationCacheMode = NavigationCacheMode.Required;

        this.WhenActivated(d => 
        {
           d(this.OneWayBind(ViewModel, vm => vm.Output, v => v.Text.Text));
        });
    }

    object IViewFor.ViewModel
    {
        get { return ViewModel; }
        set { ViewModel = (ViewModel)value; }
    }

    public ViewModel ViewModel
    {
        get { return (ViewModel)GetValue(ViewModelProperty); }
        set { SetValue(ViewModelProperty, value); }
    }

    public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register("ViewModel", typeof(ViewModel), typeof(MainPage), new PropertyMetadata(null));

    private void BindButton_OnClick(object sender, RoutedEventArgs e)
    {
        this.ViewModel = new ViewModel();
    }

}

Now, if he hit the 'BindButton' quickly a few times in quick succession we get an exception which isn't caught by the ThrownExceptions.

Why is this?

Additionally, what is the guidance around the use of ToProperty?

For example not having the bind in the WhenActivated results in an exception in the get property for the Output property since _output has a null value. Given the requirement for potentially long running tasks to populate ToProperty values which is the typical structure that should be followed?

Edit:

By making this change to the view model, we now have a situation whereby we always seem to trap the exception, Why is this?

var exec = Run().ToObservable();
_output = exec.ToProperty(this, x => x.Output, "INIT", RxApp.MainThreadScheduler);
_output.ThrownExceptions.Subscribe(ex => Debug.WriteLine("Exception {0}", ex.Message));
jamie
  • 1,224
  • 1
  • 11
  • 19

1 Answers1

1

The reason you're seeing these exceptions is because you're using WhenActivated in the ViewModel, and those VMs are being removed before the Task completes. You don't need to use WhenActivated in the ViewModel unless:

  1. You need to know when the View associated with this ViewModel actually becomes visible or is removed from the Visual Tree
  2. You are Subscribing to an object that outlives your own (i.e. a ListBoxItemViewModel that subscribes to MessageBus)

But to be honest, instead of creating a new ViewModel every time you hit the button, it is better to create a single ViewModel in the View's constructor, then bind the button to a ReactiveCommand.

Ana Betts
  • 73,868
  • 16
  • 141
  • 209
  • Paul, thanks for the reply On the "creating a view model on the hit the button" this was done just to perform a test-what i was looking at was more the behaviour for activation of view models for 'kicking' of async activities which are used to property the view but the view model being effectively disposed before the async tasks complete. – jamie Mar 16 '15 at 09:07