Browse Source

Merge branch 'master' into add-content-presenters-fluent-textbox

pull/4422/head
danwalmsley 6 years ago
committed by GitHub
parent
commit
3c1e18fa76
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      build/ReactiveUI.props
  2. 2
      src/Avalonia.Controls/Window.cs
  3. 11
      src/Avalonia.Controls/WindowBase.cs
  4. 4
      src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs
  5. 22
      src/Avalonia.ReactiveUI/RoutedViewHost.cs
  6. 81
      tests/Avalonia.ReactiveUI.UnitTests/AutoDataTemplateBindingHookTest.cs
  7. 76
      tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs

2
build/ReactiveUI.props

@ -1,5 +1,5 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<PackageReference Include="ReactiveUI" Version="10.3.6" />
<PackageReference Include="ReactiveUI" Version="11.5.17" />
</ItemGroup>
</Project>

2
src/Avalonia.Controls/Window.cs

@ -652,8 +652,8 @@ namespace Avalonia.Controls
PlatformImpl?.Show();
Renderer?.Start();
SetWindowStartupLocation(Owner?.PlatformImpl);
}
SetWindowStartupLocation(Owner?.PlatformImpl);
OnOpened(EventArgs.Empty);
}

11
src/Avalonia.Controls/WindowBase.cs

@ -39,6 +39,7 @@ namespace Avalonia.Controls
public static readonly StyledProperty<bool> TopmostProperty =
AvaloniaProperty.Register<WindowBase, bool>(nameof(Topmost));
private int _autoSizing;
private bool _hasExecutedInitialLayoutPass;
private bool _isActive;
private bool _ignoreVisibilityChange;
@ -97,11 +98,7 @@ namespace Avalonia.Controls
/// <summary>
/// Whether an auto-size operation is in progress.
/// </summary>
protected bool AutoSizing
{
get;
private set;
}
protected bool AutoSizing => _autoSizing > 0;
/// <summary>
/// Gets or sets the owner of the window.
@ -186,8 +183,8 @@ namespace Avalonia.Controls
/// </remarks>
protected IDisposable BeginAutoSizing()
{
AutoSizing = true;
return Disposable.Create(() => AutoSizing = false);
++_autoSizing;
return Disposable.Create(() => --_autoSizing);
}
/// <summary>

4
src/Avalonia.ReactiveUI/AutoDataTemplateBindingHook.cs

@ -48,6 +48,10 @@ namespace Avalonia.ReactiveUI
if (itemsControl.ItemTemplate != null)
return true;
if (itemsControl.DataTemplates != null &&
itemsControl.DataTemplates.Count > 0)
return true;
itemsControl.ItemTemplate = DefaultItemTemplate;
return true;
}

22
src/Avalonia.ReactiveUI/RoutedViewHost.cs

@ -65,8 +65,15 @@ namespace Avalonia.ReactiveUI
{
this.WhenActivated(disposables =>
{
this.WhenAnyObservable(x => x.Router.CurrentViewModel)
.DistinctUntilChanged()
var routerRemoved = this
.WhenAnyValue(x => x.Router)
.Where(router => router == null)
.Cast<object>();
this.WhenAnyValue(x => x.Router)
.Where(router => router != null)
.SelectMany(router => router.CurrentViewModel)
.Merge(routerRemoved)
.Subscribe(NavigateToViewModel)
.DisposeWith(disposables);
});
@ -92,6 +99,13 @@ namespace Avalonia.ReactiveUI
/// <param name="viewModel">ViewModel to which the user navigates.</param>
private void NavigateToViewModel(object viewModel)
{
if (Router == null)
{
this.Log().Warn("Router property is null. Falling back to default content.");
Content = DefaultContent;
return;
}
if (viewModel == null)
{
this.Log().Info("ViewModel is null. Falling back to default content.");
@ -110,8 +124,8 @@ namespace Avalonia.ReactiveUI
this.Log().Info($"Ready to show {viewInstance} with autowired {viewModel}.");
viewInstance.ViewModel = viewModel;
if (viewInstance is IStyledElement styled)
styled.DataContext = viewModel;
if (viewInstance is IDataContextProvider provider)
provider.DataContext = viewModel;
Content = viewInstance;
}
}

81
tests/Avalonia.ReactiveUI.UnitTests/AutoDataTemplateBindingHookTest.cs

@ -28,10 +28,17 @@ namespace Avalonia.ReactiveUI.UnitTests
public class ExampleView : ReactiveUserControl<ExampleViewModel>
{
public ItemsControl List { get; } = new ItemsControl();
public ItemsControl List { get; } = new ItemsControl
{
Template = GetTemplate()
};
public ExampleView()
public ExampleView(Action<ItemsControl> adjustItemsControl = null)
{
adjustItemsControl?.Invoke(List);
List.ApplyTemplate();
List.Presenter.ApplyTemplate();
Content = List;
ViewModel = new ExampleViewModel();
this.OneWayBind(ViewModel, x => x.Items, x => x.List.Items);
@ -50,18 +57,15 @@ namespace Avalonia.ReactiveUI.UnitTests
{
var view = new ExampleView();
Assert.NotNull(view.List.ItemTemplate);
Assert.IsType<FuncDataTemplate<object>>(view.List.ItemTemplate);
}
[Fact]
public void Should_Use_View_Model_View_Host_As_Data_Template()
public void Should_Use_ViewModelViewHost_As_Data_Template_By_Default()
{
var view = new ExampleView();
view.ViewModel.Items.Add(new NestedViewModel());
view.List.Template = GetTemplate();
view.List.ApplyTemplate();
view.List.Presenter.ApplyTemplate();
var child = view.List.Presenter.Panel.Children[0];
var container = (ContentPresenter) child;
container.UpdateChild();
@ -70,16 +74,11 @@ namespace Avalonia.ReactiveUI.UnitTests
}
[Fact]
public void Should_Resolve_And_Embedd_Appropriate_View_Model()
public void ViewModelViewHost_Should_Resolve_And_Embedd_Appropriate_View_Model()
{
var view = new ExampleView();
var root = new TestRoot { Child = view };
view.ViewModel.Items.Add(new NestedViewModel());
view.List.Template = GetTemplate();
view.List.ApplyTemplate();
view.List.Presenter.ApplyTemplate();
var child = view.List.Presenter.Panel.Children[0];
var container = (ContentPresenter) child;
container.UpdateChild();
@ -93,19 +92,55 @@ namespace Avalonia.ReactiveUI.UnitTests
Assert.IsType<string>(host.DataContext);
}
private FuncControlTemplate GetTemplate()
[Fact]
public void Should_Not_Override_Data_Template_Binding_When_Item_Template_Is_Set()
{
var view = new ExampleView(control => control.ItemTemplate = GetItemTemplate());
Assert.NotNull(view.List.ItemTemplate);
Assert.IsType<FuncDataTemplate<TextBlock>>(view.List.ItemTemplate);
}
[Fact]
public void Should_Not_Use_View_Model_View_Host_When_Item_Template_Is_Set()
{
var view = new ExampleView(control => control.ItemTemplate = GetItemTemplate());
view.ViewModel.Items.Add(new NestedViewModel());
var child = view.List.Presenter.Panel.Children[0];
var container = (ContentPresenter) child;
container.UpdateChild();
Assert.IsType<TextBlock>(container.Child);
}
[Fact]
public void Should_Not_Use_View_Model_View_Host_When_Data_Templates_Are_Not_Empty()
{
var view = new ExampleView(control => control.DataTemplates.Add(GetItemTemplate()));
view.ViewModel.Items.Add(new NestedViewModel());
var child = view.List.Presenter.Panel.Children[0];
var container = (ContentPresenter) child;
container.UpdateChild();
Assert.IsType<TextBlock>(container.Child);
}
private static FuncDataTemplate GetItemTemplate()
{
return new FuncDataTemplate<TextBlock>((parent, scope) => new TextBlock());
}
private static FuncControlTemplate GetTemplate()
{
return new FuncControlTemplate<ItemsControl>((parent, scope) =>
return new FuncControlTemplate<ItemsControl>((parent, scope) => new Border
{
return new Border
Background = new Media.SolidColorBrush(0xffffffff),
Child = new ItemsPresenter
{
Background = new Media.SolidColorBrush(0xffffffff),
Child = new ItemsPresenter
{
Name = "PART_ItemsPresenter",
[~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
}.RegisterInNameScope(scope)
};
Name = "PART_ItemsPresenter",
[~ItemsPresenter.ItemsProperty] = parent[~ItemsControl.ItemsProperty],
}.RegisterInNameScope(scope)
});
}
}

76
tests/Avalonia.ReactiveUI.UnitTests/RoutedViewHostTest.cs

@ -62,51 +62,85 @@ namespace Avalonia.ReactiveUI.UnitTests
PageTransition = null
};
var root = new TestRoot
{
Child = host
var root = new TestRoot
{
Child = host
};
Assert.NotNull(host.Content);
Assert.Equal(typeof(TextBlock), host.Content.GetType());
Assert.IsType<TextBlock>(host.Content);
Assert.Equal(defaultContent, host.Content);
var first = new FirstRoutableViewModel();
screen.Router.Navigate
.Execute(first)
.Subscribe();
screen.Router.Navigate.Execute(first).Subscribe();
Assert.NotNull(host.Content);
Assert.Equal(typeof(FirstRoutableView), host.Content.GetType());
Assert.IsType<FirstRoutableView>(host.Content);
Assert.Equal(first, ((FirstRoutableView)host.Content).DataContext);
Assert.Equal(first, ((FirstRoutableView)host.Content).ViewModel);
var second = new SecondRoutableViewModel();
screen.Router.Navigate
.Execute(second)
.Subscribe();
screen.Router.Navigate.Execute(second).Subscribe();
Assert.NotNull(host.Content);
Assert.Equal(typeof(SecondRoutableView), host.Content.GetType());
Assert.IsType<SecondRoutableView>(host.Content);
Assert.Equal(second, ((SecondRoutableView)host.Content).DataContext);
Assert.Equal(second, ((SecondRoutableView)host.Content).ViewModel);
screen.Router.NavigateBack
.Execute(Unit.Default)
.Subscribe();
screen.Router.NavigateBack.Execute(Unit.Default).Subscribe();
Assert.NotNull(host.Content);
Assert.Equal(typeof(FirstRoutableView), host.Content.GetType());
Assert.IsType<FirstRoutableView>(host.Content);
Assert.Equal(first, ((FirstRoutableView)host.Content).DataContext);
Assert.Equal(first, ((FirstRoutableView)host.Content).ViewModel);
screen.Router.NavigateBack
.Execute(Unit.Default)
.Subscribe();
screen.Router.NavigateBack.Execute(Unit.Default).Subscribe();
Assert.NotNull(host.Content);
Assert.IsType<TextBlock>(host.Content);
Assert.Equal(defaultContent, host.Content);
}
[Fact]
public void RoutedViewHost_Should_Show_Default_Content_When_Router_Is_Null()
{
var screen = new ScreenViewModel();
var defaultContent = new TextBlock();
var host = new RoutedViewHost
{
DefaultContent = defaultContent,
PageTransition = null,
Router = null
};
var root = new TestRoot
{
Child = host
};
Assert.NotNull(host.Content);
Assert.Equal(typeof(TextBlock), host.Content.GetType());
Assert.Equal(defaultContent, host.Content);
host.Router = screen.Router;
Assert.NotNull(host.Content);
Assert.Equal(defaultContent, host.Content);
var first = new FirstRoutableViewModel();
screen.Router.Navigate.Execute(first).Subscribe();
Assert.NotNull(host.Content);
Assert.IsType<FirstRoutableView>(host.Content);
host.Router = null;
Assert.NotNull(host.Content);
Assert.Equal(defaultContent, host.Content);
host.Router = screen.Router;
Assert.NotNull(host.Content);
Assert.IsType<FirstRoutableView>(host.Content);
}
}
}
}

Loading…
Cancel
Save