Browse Source

Merge branch 'master' into azure-pipelines

pull/1992/head
danwalmsley 8 years ago
committed by GitHub
parent
commit
34af11af4d
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .travis.yml
  2. 7
      build.cake
  3. 44
      src/Avalonia.ReactiveUI/ReactiveUserControl.cs
  4. 44
      src/Avalonia.ReactiveUI/ReactiveWindow.cs
  5. 2
      src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs
  6. 2
      src/Markup/Avalonia.Markup/Data/MultiBinding.cs
  7. 31
      tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests.cs
  8. 82
      tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs
  9. 2
      tools/packages.config

2
.travis.yml

@ -11,6 +11,8 @@ mono:
- 5.2.0
dotnet: 2.1.200
script:
- sudo apt-get update
- sudo apt-get install castxml
- ./build.sh --target "Travis" --configuration "Release"
notifications:
email: false

7
build.cake

@ -2,9 +2,10 @@
// ADDINS
///////////////////////////////////////////////////////////////////////////////
#tool "nuget:?package=NuGet.CommandLine&version=4.3.0"
#tool "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2017.1.20170613.162720"
#addin "nuget:?package=NuGet.Core&version=2.14.0"
#tool "nuget:?package=NuGet.CommandLine&version=4.7.1"
#tool "nuget:?package=JetBrains.ReSharper.CommandLineTools&version=2018.2.3"
///////////////////////////////////////////////////////////////////////////////
// TOOLS
///////////////////////////////////////////////////////////////////////////////

44
src/Avalonia.ReactiveUI/ReactiveUserControl.cs

@ -0,0 +1,44 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia;
using Avalonia.VisualTree;
using Avalonia.Controls;
using ReactiveUI;
namespace Avalonia
{
/// <summary>
/// A ReactiveUI UserControl that implements <see cref="IViewFor{TViewModel}"/>
/// and will activate your ViewModel automatically if it supports activation.
/// </summary>
/// <typeparam name="TViewModel">ViewModel type.</typeparam>
public class ReactiveUserControl<TViewModel> : UserControl, IViewFor<TViewModel> where TViewModel : class
{
public static readonly AvaloniaProperty<TViewModel> ViewModelProperty = AvaloniaProperty
.Register<ReactiveWindow<TViewModel>, TViewModel>(nameof(ViewModel));
/// <summary>
/// Initializes a new instance of the <see cref="ReactiveUserControl{TViewModel}"/> class.
/// </summary>
public ReactiveUserControl()
{
DataContextChanged += (sender, args) => ViewModel = DataContext as TViewModel;
}
/// <summary>
/// The ViewModel.
/// </summary>
public TViewModel ViewModel
{
get => GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (TViewModel)value;
}
}
}

44
src/Avalonia.ReactiveUI/ReactiveWindow.cs

@ -0,0 +1,44 @@
// Copyright (c) The Avalonia Project. All rights reserved.
// Licensed under the MIT license. See licence.md file in the project root for full license information.
using Avalonia;
using Avalonia.VisualTree;
using Avalonia.Controls;
using ReactiveUI;
namespace Avalonia
{
/// <summary>
/// A ReactiveUI Window that implements <see cref="IViewFor{TViewModel}"/>
/// and will activate your ViewModel automatically if it supports activation.
/// </summary>
/// <typeparam name="TViewModel">ViewModel type.</typeparam>
public class ReactiveWindow<TViewModel> : Window, IViewFor<TViewModel> where TViewModel : class
{
public static readonly AvaloniaProperty<TViewModel> ViewModelProperty = AvaloniaProperty
.Register<ReactiveWindow<TViewModel>, TViewModel>(nameof(ViewModel));
/// <summary>
/// Initializes a new instance of the <see cref="ReactiveWindow{TViewModel}"/> class.
/// </summary>
public ReactiveWindow()
{
DataContextChanged += (sender, args) => ViewModel = DataContext as TViewModel;
}
/// <summary>
/// The ViewModel.
/// </summary>
public TViewModel ViewModel
{
get => GetValue(ViewModelProperty);
set => SetValue(ViewModelProperty, value);
}
object IViewFor.ViewModel
{
get => ViewModel;
set => ViewModel = (TViewModel)value;
}
}
}

2
src/Gtk/Avalonia.Gtk3/Gtk3Platform.cs

@ -66,7 +66,7 @@ namespace Avalonia.Gtk3
DisplayClassName =
Utf8Buffer.StringFromPtr(Native.GTypeName(Marshal.ReadIntPtr(Marshal.ReadIntPtr(disp))));
using (var utf = new Utf8Buffer("avalonia.app." + Guid.NewGuid()))
using (var utf = new Utf8Buffer($"avalonia.app.a{Guid.NewGuid().ToString("N")}"))
App = Native.GtkApplicationNew(utf, 0);
//Mark current thread as UI thread
s_tlsMarker = true;

2
src/Markup/Avalonia.Markup/Data/MultiBinding.cs

@ -61,7 +61,7 @@ namespace Avalonia.Data
var targetType = targetProperty?.PropertyType ?? typeof(object);
var children = Bindings.Select(x => x.Initiate(target, null));
var input = children.Select(x => x.Subject).CombineLatest().Select(x => ConvertValue(x, targetType));
var input = children.Select(x => x.Observable).CombineLatest().Select(x => ConvertValue(x, targetType));
var mode = Mode == BindingMode.Default ?
targetProperty?.GetMetadata(target.GetType()).DefaultBindingMode : Mode;

31
tests/Avalonia.Markup.UnitTests/Data/MultiBindingTests.cs

@ -41,6 +41,37 @@ namespace Avalonia.Markup.UnitTests.Data
Assert.Equal("1,2,3", result);
}
[Fact]
public async Task Nested_MultiBinding_Should_Be_Set_Up()
{
var source = new { A = 1, B = 2, C = 3 };
var binding = new MultiBinding
{
Converter = new ConcatConverter(),
Bindings =
{
new Binding { Path = "A" },
new MultiBinding
{
Converter = new ConcatConverter(),
Bindings =
{
new Binding { Path = "B"},
new Binding { Path = "C"}
}
}
}
};
var target = new Mock<IAvaloniaObject>().As<IControl>();
target.Setup(x => x.GetValue(Control.DataContextProperty)).Returns(source);
var observable = binding.Initiate(target.Object, null).Observable;
var result = await observable.Take(1);
Assert.Equal("1,2,3", result);
}
[Fact]
public void Should_Return_FallbackValue_When_Converter_Returns_UnsetValue()
{

82
tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs

@ -23,7 +23,8 @@ namespace Avalonia
public TestUserControlWithWhenActivated()
{
this.WhenActivated(disposables => {
this.WhenActivated(disposables =>
{
Active = true;
Disposable
.Create(() => Active = false)
@ -38,7 +39,8 @@ namespace Avalonia
public TestWindowWithWhenActivated()
{
this.WhenActivated(disposables => {
this.WhenActivated(disposables =>
{
Active = true;
Disposable
.Create(() => Active = false)
@ -47,6 +49,42 @@ namespace Avalonia
}
}
public class ActivatableViewModel : ISupportsActivation
{
public ViewModelActivator Activator { get; }
public bool IsActivated { get; private set; }
public ActivatableViewModel()
{
Activator = new ViewModelActivator();
this.WhenActivated(disposables =>
{
IsActivated = true;
Disposable
.Create(() => IsActivated = false)
.DisposeWith(disposables);
});
}
}
public class ActivatableWindow : ReactiveWindow<ActivatableViewModel>
{
public ActivatableWindow() => this.WhenActivated(disposables => { });
}
public class ActivatableUserControl : ReactiveUserControl<ActivatableViewModel>
{
public ActivatableUserControl() => this.WhenActivated(disposables => { });
}
public AvaloniaActivationForViewFetcherTest()
{
Locator.CurrentMutable.RegisterConstant(
new AvaloniaActivationForViewFetcher(),
typeof(IActivationForViewFetcher));
}
[Fact]
public void Visual_Element_Is_Activated_And_Deactivated()
{
@ -86,10 +124,6 @@ namespace Avalonia
[Fact]
public void Activation_For_View_Fetcher_Should_Support_When_Activated()
{
Locator.CurrentMutable.RegisterConstant(
new AvaloniaActivationForViewFetcher(),
typeof(IActivationForViewFetcher));
var userControl = new TestUserControlWithWhenActivated();
Assert.False(userControl.Active);
@ -104,10 +138,6 @@ namespace Avalonia
[Fact]
public void Activation_For_View_Fetcher_Should_Support_Windows()
{
Locator.CurrentMutable.RegisterConstant(
new AvaloniaActivationForViewFetcher(),
typeof(IActivationForViewFetcher));
using (var application = UnitTestApplication.Start(TestServices.MockWindowingPlatform))
{
var window = new TestWindowWithWhenActivated();
@ -120,5 +150,37 @@ namespace Avalonia
Assert.False(window.Active);
}
}
[Fact]
public void Activatable_Window_View_Model_Is_Activated_And_Deactivated()
{
using (var application = UnitTestApplication.Start(TestServices.MockWindowingPlatform))
{
var viewModel = new ActivatableViewModel();
var window = new ActivatableWindow { ViewModel = viewModel };
Assert.False(viewModel.IsActivated);
window.Show();
Assert.True(viewModel.IsActivated);
window.Close();
Assert.False(viewModel.IsActivated);
}
}
[Fact]
public void Activatable_User_Control_View_Model_Is_Activated_And_Deactivated()
{
var root = new TestRoot();
var viewModel = new ActivatableViewModel();
var control = new ActivatableUserControl { ViewModel = viewModel };
Assert.False(viewModel.IsActivated);
root.Child = control;
Assert.True(viewModel.IsActivated);
root.Child = null;
Assert.False(viewModel.IsActivated);
}
}
}

2
tools/packages.config

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Cake" version="0.28.0" />
<package id="Cake" version="0.30.0" />
</packages>

Loading…
Cancel
Save