I have a window that uses a ReactiveUI Interaction to open a second window as a modal dialog, then return data from a ListBox in the second window.
The problem is that when .ShowDialog() completes, the ViewModel's SelectedItem always evaluates to null. I've confirmed that the binding is working correctly, and the selected item is getting updated in the dialog window's ViewModel properly. It's only when I return to the Interaction that the property resets to its default value (null).
I've put together a minimal example of the issue here:
https://github.com/replicaJunction/ReactiveDialogTest
The main logic under test is in MainWindowViewModel.cs.
Edit: Here's a snippet from the code with the basic idea:
GetNumberFromDialog = new Interaction<Unit, int>();
GetNumberFromDialog.RegisterHandler(interaction =>
{
var vm = new DialogWindowViewModel();
// Get a reference to the view for this VM
var view = Locator.Current.GetService<IViewFor<DialogWindowViewModel>>();
// Set its VM to our current reference
view.ViewModel = vm;
var window = view as Window;
var dialogResult = window.ShowDialog();
// At this point, vm.SelectedNumber is expected be the number the user selected -
// but instead, it always evaluates to 0.
if (true == dialogResult)
interaction.SetOutput(vm.SelectedNumber);
else
interaction.SetOutput(-1);
});
OpenDialog = ReactiveCommand.Create(() =>
{
GetNumberFromDialog.Handle(Unit.Default)
.Where(retVal => -1 != retVal) // If the dialog did not return true, don't update
.Subscribe(retVal =>
{
this.MyNumber = retVal;
});
});
Steps to reproduce the issue:
Expected behavior: the label in the main window beneath "my number is" should update to the value you selected in the ListBox.
Actual behavior: the label updates to 0 (default value for int).
Why is my ViewModel resetting itself when the dialog closes?
Looking at your sample on GitHub helps to reveal the issue. Your DialogWindow
looks like this:
public partial class DialogWindow : Window, IViewFor<DialogWindowViewModel>
{
public DialogWindow()
{
InitializeComponent();
this.ViewModel = new DialogWindowViewModel();
this.DataContext = this.ViewModel;
this.ViewModel
.WhenAnyValue(x => x.DialogResult)
.Where(x => null != x)
.Subscribe(val =>
{
this.DialogResult = val;
this.Close();
});
}
public DialogWindowViewModel ViewModel { get; set; }
object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (DialogWindowViewModel)value;
}
}
In your MainWindowViewModel
, you set the DialogWindow.ViewModel
property to a new instance of your DialogWindowViewModel
. The issue ocurrs at this point. Your problem is that setting the DialogWindow.ViewModel
property does not set the view's DataContext
or re-create the WhenAnyValue
observable. This means the view is still binding to the SelectedNumber
property on the old instance of the DialogWindowViewModel
(the one created in the DialogWindow
constructor). To fix your sample code above, you can simply avoid setting the ViewModel property, and use the ViewModel that's already set on the dialog:
GetNumberFromDialog.RegisterHandler(interaction =>
{
// Get a reference to the view for this VM
var view = Locator.Current.GetService<IViewFor<DialogWindowViewModel>>();
var window = view as Window;
var dialogResult = window.ShowDialog();
// use the ViewModel here that's already set on the DialogWindow
if (true == dialogResult)
interaction.SetOutput(view.ViewModel.SelectedNumber);
else
interaction.SetOutput(-1);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With