Browse Source

Merge pull request #4963 from worldbeater/rx-user-controls

fix: DataContext Inheritance in ReactiveUserControl<TViewModel>
pull/4985/head
Dariusz Komosiński 5 years ago
committed by GitHub
parent
commit
b9b7147ab6
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 23
      src/Avalonia.ReactiveUI/ReactiveUserControl.cs
  2. 31
      src/Avalonia.ReactiveUI/ReactiveWindow.cs
  3. 71
      tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs

23
src/Avalonia.ReactiveUI/ReactiveUserControl.cs

@ -28,11 +28,7 @@ namespace Avalonia.ReactiveUI
// This WhenActivated block calls ViewModel's WhenActivated
// block if the ViewModel implements IActivatableViewModel.
this.WhenActivated(disposables => { });
this.ObservableForProperty(x => x.ViewModel, false, true)
.Subscribe(args => DataContext = args.Value);
this.ObservableForProperty(x => x.DataContext, false, true)
.Subscribe(args => ViewModel = args.Value as TViewModel);
this.GetObservable(ViewModelProperty).Subscribe(OnViewModelChanged);
}
/// <summary>
@ -49,5 +45,22 @@ namespace Avalonia.ReactiveUI
get => ViewModel;
set => ViewModel = (TViewModel)value;
}
protected override void OnDataContextChanged(EventArgs e)
{
ViewModel = DataContext as TViewModel;
}
private void OnViewModelChanged(object value)
{
if (value == null)
{
ClearValue(DataContextProperty);
}
else if (DataContext != value)
{
DataContext = value;
}
}
}
}

31
src/Avalonia.ReactiveUI/ReactiveWindow.cs

@ -28,11 +28,8 @@ namespace Avalonia.ReactiveUI
// This WhenActivated block calls ViewModel's WhenActivated
// block if the ViewModel implements IActivatableViewModel.
this.WhenActivated(disposables => { });
this.ObservableForProperty(x => x.ViewModel, false, true)
.Subscribe(args => DataContext = args.Value);
this.ObservableForProperty(x => x.DataContext, false, true)
.Subscribe(args => ViewModel = args.Value as TViewModel);
this.GetObservable(DataContextProperty).Subscribe(OnDataContextChanged);
this.GetObservable(ViewModelProperty).Subscribe(OnViewModelChanged);
}
/// <summary>
@ -49,5 +46,29 @@ namespace Avalonia.ReactiveUI
get => ViewModel;
set => ViewModel = (TViewModel)value;
}
private void OnDataContextChanged(object value)
{
if (value is TViewModel viewModel)
{
ViewModel = viewModel;
}
else
{
ViewModel = null;
}
}
private void OnViewModelChanged(object value)
{
if (value == null)
{
ClearValue(DataContextProperty);
}
else if (DataContext != value)
{
DataContext = value;
}
}
}
}

71
tests/Avalonia.ReactiveUI.UnitTests/ReactiveUserControlTest.cs

@ -1,4 +1,5 @@
using System.Reactive.Disposables;
using Avalonia.Controls;
using Avalonia.UnitTests;
using ReactiveUI;
using Splat;
@ -100,5 +101,75 @@ namespace Avalonia.ReactiveUI.UnitTests
Assert.NotNull(view.DataContext);
Assert.False(view.ViewModel.IsActive);
}
[Fact]
public void Should_Inherit_DataContext()
{
var vm1 = new ExampleViewModel();
var vm2 = new ExampleViewModel();
var view = new ExampleView();
var root = new TestRoot(view);
Assert.Null(view.DataContext);
Assert.Null(view.ViewModel);
root.DataContext = vm1;
Assert.Same(vm1, view.DataContext);
Assert.Same(vm1, view.ViewModel);
root.DataContext = null;
Assert.Null(view.DataContext);
Assert.Null(view.ViewModel);
root.DataContext = vm2;
Assert.Same(vm2, view.DataContext);
Assert.Same(vm2, view.ViewModel);
}
[Fact]
public void Should_Not_Overlap_Change_Notifications()
{
var vm1 = new ExampleViewModel();
var vm2 = new ExampleViewModel();
var view1 = new ExampleView();
var view2 = new ExampleView();
Assert.Null(view1.DataContext);
Assert.Null(view2.DataContext);
Assert.Null(view1.ViewModel);
Assert.Null(view2.ViewModel);
view1.DataContext = vm1;
Assert.Same(vm1, view1.DataContext);
Assert.Same(vm1, view1.ViewModel);
Assert.Null(view2.DataContext);
Assert.Null(view2.ViewModel);
view2.DataContext = vm2;
Assert.Same(vm1, view1.DataContext);
Assert.Same(vm1, view1.ViewModel);
Assert.Same(vm2, view2.DataContext);
Assert.Same(vm2, view2.ViewModel);
view1.ViewModel = null;
Assert.Null(view1.DataContext);
Assert.Null(view1.ViewModel);
Assert.Same(vm2, view2.DataContext);
Assert.Same(vm2, view2.ViewModel);
view2.ViewModel = null;
Assert.Null(view1.DataContext);
Assert.Null(view2.DataContext);
Assert.Null(view1.ViewModel);
Assert.Null(view2.ViewModel);
}
}
}

Loading…
Cancel
Save