Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
234 changes: 124 additions & 110 deletions src/ReactiveUI.Tests/DependencyResolverTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,167 +5,181 @@
using ReactiveUI;
using ReactiveUI.Tests;
using Splat;

using Xunit;

namespace ReactiveUI.Tests
{
public class ExampleViewModel : ReactiveObject
{
}
public class ExampleViewModel : ReactiveObject
{
}

public class AnotherViewModel : ReactiveObject
{
}
public class AnotherViewModel : ReactiveObject
{
}

public class NeverUsedViewModel : ReactiveObject
{
}
public class NeverUsedViewModel : ReactiveObject
{
}

public class SingleInstanceExampleViewModel : ReactiveObject
{
}
public class SingleInstanceExampleViewModel : ReactiveObject
{
}

public class ViewModelWithWeirdName : ReactiveObject
{
}
public class ViewModelWithWeirdName : ReactiveObject
{
}

public class ExampleView : ReactiveUserControl<ExampleViewModel>
{
}
public class ExampleWindowViewModel : ReactiveObject
{
}

public class AnotherView : ReactiveUserControl<AnotherViewModel>
{
}
public class ExampleWindowView : ReactiveWindow<ExampleWindowViewModel>
{
}

public class ExampleView : ReactiveUserControl<ExampleViewModel>
{
}

public class AnotherView : ReactiveUserControl<AnotherViewModel>
{
}

[ViewContract("contract")]
public class ContractExampleView : ReactiveUserControl<ExampleViewModel>
{
}

[SingleInstanceView]
public class NeverUsedView : ReactiveUserControl<NeverUsedViewModel>
{
public static int Instances;

[ViewContract("contract")]
public class ContractExampleView : ReactiveUserControl<ExampleViewModel>
public NeverUsedView()
{
Instances++;
}
}

[SingleInstanceView]
public class NeverUsedView : ReactiveUserControl<NeverUsedViewModel>
{
public static int Instances;
[SingleInstanceView]
public class SingleInstanceExampleView : ReactiveUserControl<SingleInstanceExampleViewModel>
{
public static int Instances;

public NeverUsedView()
{
Instances++;
}
public SingleInstanceExampleView()
{
Instances++;
}
}

[SingleInstanceView]
public class SingleInstanceExampleView : ReactiveUserControl<SingleInstanceExampleViewModel>
{
public static int Instances;
[ViewContract("contract")]
[SingleInstanceView]
public class SingleInstanceWithContractExampleView : ReactiveUserControl<SingleInstanceExampleViewModel>
{
public static int Instances;

public SingleInstanceExampleView() { Instances++; }
public SingleInstanceWithContractExampleView()
{
Instances++;
}
}

[ViewContract("contract")]
[SingleInstanceView]
public class SingleInstanceWithContractExampleView : ReactiveUserControl<SingleInstanceExampleViewModel>
{
public static int Instances;
public class ViewWithoutMatchingName : ReactiveUserControl<ViewModelWithWeirdName>
{
}

public SingleInstanceWithContractExampleView() { Instances++; }
}
public class DependencyResolverTests
{
private readonly IMutableDependencyResolver _resolver;

public class ViewWithoutMatchingName : ReactiveUserControl<ViewModelWithWeirdName>
public DependencyResolverTests()
{
_resolver = new ModernDependencyResolver();
_resolver.InitializeSplat();
_resolver.InitializeReactiveUI();
_resolver.RegisterViewsForViewModels(GetType().Assembly);
}

public class DependencyResolverTests
[WpfFact]
public void RegisterViewsForViewModelShouldRegisterAllViews()
{
private readonly IMutableDependencyResolver _resolver;

public DependencyResolverTests()
using (_resolver.WithResolver())
{
_resolver = new ModernDependencyResolver();
_resolver.InitializeSplat();
_resolver.InitializeReactiveUI();
_resolver.RegisterViewsForViewModels(GetType().Assembly);
}

[WpfFact]
public void RegisterViewsForViewModelShouldRegisterAllViews()
{
using (_resolver.WithResolver())
{
Assert.Single(_resolver.GetServices<IViewFor<ExampleViewModel>>());
Assert.Single(_resolver.GetServices<IViewFor<AnotherViewModel>>());
Assert.Single(_resolver.GetServices<IViewFor<ViewModelWithWeirdName>>());
}
Assert.Single(_resolver.GetServices<IViewFor<ExampleViewModel>>());
Assert.Single(_resolver.GetServices<IViewFor<AnotherViewModel>>());
Assert.Single(_resolver.GetServices<IViewFor<ExampleWindowViewModel>>());
Assert.Single(_resolver.GetServices<IViewFor<ViewModelWithWeirdName>>());
}
}

[WpfFact]
public void RegisterViewsForViewModelShouldIncludeContracts()
[WpfFact]
public void RegisterViewsForViewModelShouldIncludeContracts()
{
using (_resolver.WithResolver())
{
using (_resolver.WithResolver())
{
Assert.Single(_resolver.GetServices(typeof(IViewFor<ExampleViewModel>), "contract"));
}
Assert.Single(_resolver.GetServices(typeof(IViewFor<ExampleViewModel>), "contract"));
}
}

[WpfFact]
public void NonContractRegistrationsShouldResolveCorrectly()
[WpfFact]
public void NonContractRegistrationsShouldResolveCorrectly()
{
using (_resolver.WithResolver())
{
using (_resolver.WithResolver())
{
Assert.IsType<AnotherView>(_resolver.GetService<IViewFor<AnotherViewModel>>());
}
Assert.IsType<AnotherView>(_resolver.GetService<IViewFor<AnotherViewModel>>());
}
}

[WpfFact]
public void ContractRegistrationsShouldResolveCorrectly()
[WpfFact]
public void ContractRegistrationsShouldResolveCorrectly()
{
using (_resolver.WithResolver())
{
using (_resolver.WithResolver())
{
Assert.IsType<ContractExampleView>(_resolver.GetService(typeof(IViewFor<ExampleViewModel>), "contract"));
}
Assert.IsType<ContractExampleView>(_resolver.GetService(typeof(IViewFor<ExampleViewModel>), "contract"));
}
}

[Fact]
public void SingleInstanceViewsShouldOnlyBeInstantiatedWhenRequested()
[Fact]
public void SingleInstanceViewsShouldOnlyBeInstantiatedWhenRequested()
{
using (_resolver.WithResolver())
{
using (_resolver.WithResolver())
{
Assert.Equal(0, NeverUsedView.Instances);
}
Assert.Equal(0, NeverUsedView.Instances);
}
}

[WpfFact]
public void SingleInstanceViewsShouldOnlyBeInstantiatedOnce()
[WpfFact]
public void SingleInstanceViewsShouldOnlyBeInstantiatedOnce()
{
using (_resolver.WithResolver())
{
using (_resolver.WithResolver())
{
Assert.Equal(0, SingleInstanceExampleView.Instances);
Assert.Equal(0, SingleInstanceExampleView.Instances);

var instance = _resolver.GetService(typeof(IViewFor<SingleInstanceExampleViewModel>));
Assert.Equal(1, SingleInstanceExampleView.Instances);
var instance = _resolver.GetService(typeof(IViewFor<SingleInstanceExampleViewModel>));
Assert.Equal(1, SingleInstanceExampleView.Instances);

var instance2 = _resolver.GetService(typeof(IViewFor<SingleInstanceExampleViewModel>));
Assert.Equal(1, SingleInstanceExampleView.Instances);
var instance2 = _resolver.GetService(typeof(IViewFor<SingleInstanceExampleViewModel>));
Assert.Equal(1, SingleInstanceExampleView.Instances);

Assert.Same(instance, instance2);
}
Assert.Same(instance, instance2);
}
}

[WpfFact]
public void SingleInstanceViewsWithContractShouldResolveCorrectly()
[WpfFact]
public void SingleInstanceViewsWithContractShouldResolveCorrectly()
{
using (_resolver.WithResolver())
{
using (_resolver.WithResolver())
{
Assert.Equal(0, SingleInstanceWithContractExampleView.Instances);
Assert.Equal(0, SingleInstanceWithContractExampleView.Instances);

var instance = _resolver.GetService(typeof(IViewFor<SingleInstanceExampleViewModel>), "contract");
Assert.Equal(1, SingleInstanceWithContractExampleView.Instances);
var instance = _resolver.GetService(typeof(IViewFor<SingleInstanceExampleViewModel>), "contract");
Assert.Equal(1, SingleInstanceWithContractExampleView.Instances);

var instance2 = _resolver.GetService(typeof(IViewFor<SingleInstanceExampleViewModel>), "contract");
Assert.Equal(1, SingleInstanceWithContractExampleView.Instances);
var instance2 = _resolver.GetService(typeof(IViewFor<SingleInstanceExampleViewModel>), "contract");
Assert.Equal(1, SingleInstanceWithContractExampleView.Instances);

Assert.Same(instance, instance2);
}
Assert.Same(instance, instance2);
}
}
}
}
70 changes: 70 additions & 0 deletions src/ReactiveUI.Wpf/ReactiveWindow.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Windows;

namespace ReactiveUI
{
/// <summary>
/// A <see cref="Window"/> that is reactive.
/// </summary>
/// <remarks>
/// <para>
/// This class is a <see cref="Window"/> that is also reactive. That is, it implements <see cref="IViewFor{TViewModel}"/>.
/// You can extend this class to get an implementation of <see cref="IViewFor{TViewModel}"/> rather than writing one yourself.
/// </para>
/// <para>
/// Note that the XAML for your control must specify the same base class, including the generic argument you provide for your view
/// model. To do this, use the <c>TypeArguments</c> attribute as follows:
/// <code>
/// <![CDATA[
/// <rxui:ReactiveWindow
/// x:Class="views:YourView"
/// x:TypeArguments="vms:YourViewModel"
/// xmlns:rxui="http://reactiveui.net"
/// xmlns:views="clr-namespace:Foo.Bar.Views"
/// xmlns:vms="clr-namespace:Foo.Bar.ViewModels">
/// <!-- view XAML here -->
/// </rxui:ReactiveWindow>
/// ]]>
/// </code>
/// </para>
/// </remarks>
/// <typeparam name="TViewModel">
/// The type of the view model backing the view.
/// </typeparam>
public abstract class ReactiveWindow<TViewModel> :
Window, IViewFor<TViewModel>
where TViewModel : class
{
/// <summary>
/// Gets the binding root view model.
/// </summary>
public TViewModel BindingRoot => ViewModel;

/// <summary>
/// The view model dependency property.
/// </summary>
public static readonly DependencyProperty ViewModelProperty =
DependencyProperty.Register(
"ViewModel",
typeof(TViewModel),
typeof(ReactiveWindow<TViewModel>),
new PropertyMetadata(null));

/// <inheritdoc/>
public TViewModel ViewModel
{
get => (TViewModel)GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}

/// <inheritdoc/>
object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (TViewModel)value;
}
}
}