diff --git a/build.cake b/build.cake
index 24f529ee4a..56653109ae 100644
--- a/build.cake
+++ b/build.cake
@@ -170,6 +170,7 @@ Task("Run-Unit-Tests-Impl")
RunCoreTest("./tests/Avalonia.Styling.UnitTests", data.Parameters, false);
RunCoreTest("./tests/Avalonia.Visuals.UnitTests", data.Parameters, false);
RunCoreTest("./tests/Avalonia.Skia.UnitTests", data.Parameters, false);
+ RunCoreTest("./tests/Avalonia.ReactiveUI.UnitTests", data.Parameters, false);
if (data.Parameters.IsRunningOnWindows)
{
RunCoreTest("./tests/Avalonia.Direct2D1.UnitTests", data.Parameters, false);
diff --git a/build/ReactiveUI.props b/build/ReactiveUI.props
index acdfdd215a..1208be34b8 100644
--- a/build/ReactiveUI.props
+++ b/build/ReactiveUI.props
@@ -1,5 +1,5 @@
-
+
diff --git a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
index eb08ef9656..eba17f92e4 100644
--- a/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
+++ b/samples/VirtualizationDemo/ViewModels/MainWindowViewModel.cs
@@ -7,6 +7,7 @@ using System.Linq;
using Avalonia.Collections;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
+using ReactiveUI.Legacy;
using ReactiveUI;
namespace VirtualizationDemo.ViewModels
diff --git a/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs
index 3eab54115a..d763febdf3 100644
--- a/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs
+++ b/src/Avalonia.ReactiveUI/AppBuilderExtensions.cs
@@ -4,6 +4,7 @@
using Avalonia.Controls;
using Avalonia.Threading;
using ReactiveUI;
+using Splat;
namespace Avalonia
{
@@ -15,6 +16,9 @@ namespace Avalonia
return builder.AfterSetup(_ =>
{
RxApp.MainThreadScheduler = AvaloniaScheduler.Instance;
+ Locator.CurrentMutable.Register(
+ () => new AvaloniaActivationForViewFetcher(),
+ typeof(IActivationForViewFetcher));
});
}
}
diff --git a/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs
new file mode 100644
index 0000000000..828d8024e6
--- /dev/null
+++ b/src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs
@@ -0,0 +1,38 @@
+// 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 System;
+using System.Reflection;
+using System.Reactive.Linq;
+using Avalonia;
+using Avalonia.VisualTree;
+using ReactiveUI;
+
+namespace Avalonia
+{
+ public class AvaloniaActivationForViewFetcher : IActivationForViewFetcher
+ {
+ public int GetAffinityForView(Type view)
+ {
+ return typeof(IVisual).GetTypeInfo().IsAssignableFrom(view.GetTypeInfo()) ? 10 : 0;
+ }
+
+ public IObservable GetActivationForView(IActivatable view)
+ {
+ if (!(view is IVisual visual)) return Observable.Return(false);
+ var viewLoaded = Observable
+ .FromEventPattern(
+ x => visual.AttachedToVisualTree += x,
+ x => visual.DetachedFromVisualTree -= x)
+ .Select(args => true);
+ var viewUnloaded = Observable
+ .FromEventPattern(
+ x => visual.DetachedFromVisualTree += x,
+ x => visual.DetachedFromVisualTree -= x)
+ .Select(args => false);
+ return viewLoaded
+ .Merge(viewUnloaded)
+ .DistinctUntilChanged();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
index d6b95bc7b0..c3c867d3fb 100644
--- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
+++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
@@ -976,6 +976,9 @@ namespace Avalonia.Win32.Interop
[DllImport("shcore.dll")]
public static extern void GetScaleFactorForMonitor(IntPtr hMon, out uint pScale);
+ [DllImport("user32.dll", SetLastError = true)]
+ public static extern bool SetProcessDPIAware();
+
[DllImport("user32.dll")]
public static extern IntPtr MonitorFromPoint(POINT pt, MONITOR dwFlags);
@@ -987,7 +990,7 @@ namespace Avalonia.Win32.Interop
[DllImport("user32", EntryPoint = "GetMonitorInfoW", ExactSpelling = true, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.Bool)]
- public static extern bool GetMonitorInfo([In] IntPtr hMonitor, [Out] MONITORINFO lpmi);
+ public static extern bool GetMonitorInfo([In] IntPtr hMonitor, ref MONITORINFO lpmi);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "PostMessageW")]
@@ -1055,12 +1058,17 @@ namespace Avalonia.Win32.Interop
}
[StructLayout(LayoutKind.Sequential)]
- internal class MONITORINFO
+ internal struct MONITORINFO
{
- public int cbSize = Marshal.SizeOf();
- public RECT rcMonitor = new RECT();
- public RECT rcWork = new RECT();
- public int dwFlags = 0;
+ public int cbSize;
+ public RECT rcMonitor;
+ public RECT rcWork;
+ public int dwFlags;
+
+ public static MONITORINFO Create()
+ {
+ return new MONITORINFO() { cbSize = Marshal.SizeOf() };
+ }
public enum MonitorOptions : uint
{
diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs
index e1df24151d..5cfceb68b7 100644
--- a/src/Windows/Avalonia.Win32/ScreenImpl.cs
+++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs
@@ -26,8 +26,8 @@ namespace Avalonia.Win32
EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero,
(IntPtr monitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr data) =>
{
- MONITORINFO monitorInfo = new MONITORINFO();
- if (GetMonitorInfo(monitor, monitorInfo))
+ MONITORINFO monitorInfo = MONITORINFO.Create();
+ if (GetMonitorInfo(monitor,ref monitorInfo))
{
RECT bounds = monitorInfo.rcMonitor;
RECT workingArea = monitorInfo.rcWork;
diff --git a/src/Windows/Avalonia.Win32/Win32Platform.cs b/src/Windows/Avalonia.Win32/Win32Platform.cs
index 89943b5b0a..5041942c63 100644
--- a/src/Windows/Avalonia.Win32/Win32Platform.cs
+++ b/src/Windows/Avalonia.Win32/Win32Platform.cs
@@ -77,6 +77,8 @@ namespace Avalonia.Win32
public static void Initialize(bool deferredRendering = true)
{
+ UnmanagedMethods.SetProcessDPIAware();
+
AvaloniaLocator.CurrentMutable
.Bind().ToSingleton()
.Bind().ToConstant(CursorFactory.Instance)
diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs
index 65580ff9c8..45120fa21b 100644
--- a/src/Windows/Avalonia.Win32/WindowImpl.cs
+++ b/src/Windows/Avalonia.Win32/WindowImpl.cs
@@ -837,9 +837,9 @@ namespace Avalonia.Win32
if (monitor != IntPtr.Zero)
{
- MONITORINFO monitorInfo = new MONITORINFO();
+ MONITORINFO monitorInfo = MONITORINFO.Create();
- if (GetMonitorInfo(monitor, monitorInfo))
+ if (GetMonitorInfo(monitor,ref monitorInfo))
{
RECT rcMonitorArea = monitorInfo.rcMonitor;
diff --git a/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj
new file mode 100644
index 0000000000..7c0ff79183
--- /dev/null
+++ b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj
@@ -0,0 +1,14 @@
+
+
+ netcoreapp2.0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs
new file mode 100644
index 0000000000..97701f8437
--- /dev/null
+++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs
@@ -0,0 +1,89 @@
+using System;
+using System.Reactive.Concurrency;
+using System.Reactive.Disposables;
+using Avalonia.Controls;
+using Avalonia.Rendering;
+using Avalonia.Platform;
+using Avalonia.UnitTests;
+using Avalonia;
+using ReactiveUI;
+using DynamicData;
+using Xunit;
+using Splat;
+
+namespace Avalonia
+{
+ public class AvaloniaActivationForViewFetcherTest
+ {
+ public class TestUserControl : UserControl, IActivatable { }
+
+ public class TestUserControlWithWhenActivated : UserControl, IActivatable
+ {
+ public bool Active { get; private set; }
+
+ public TestUserControlWithWhenActivated()
+ {
+ this.WhenActivated(disposables => {
+ Active = true;
+ Disposable
+ .Create(() => Active = false)
+ .DisposeWith(disposables);
+ });
+ }
+ }
+
+ [Fact]
+ public void Visual_Element_Is_Activated_And_Deactivated()
+ {
+ var userControl = new TestUserControl();
+ var activationForViewFetcher = new AvaloniaActivationForViewFetcher();
+
+ activationForViewFetcher
+ .GetActivationForView(userControl)
+ .ToObservableChangeSet(scheduler: ImmediateScheduler.Instance)
+ .Bind(out var activated)
+ .Subscribe();
+
+ var fakeRenderedDecorator = new TestRoot();
+ fakeRenderedDecorator.Child = userControl;
+ Assert.True(activated[0]);
+ Assert.Equal(1, activated.Count);
+
+ fakeRenderedDecorator.Child = null;
+ Assert.True(activated[0]);
+ Assert.False(activated[1]);
+ Assert.Equal(2, activated.Count);
+ }
+
+ [Fact]
+ public void Get_Affinity_For_View_Should_Return_Non_Zero_For_Visual_Elements()
+ {
+ var userControl = new TestUserControl();
+ var activationForViewFetcher = new AvaloniaActivationForViewFetcher();
+
+ var forUserControl = activationForViewFetcher.GetAffinityForView(userControl.GetType());
+ var forNonUserControl = activationForViewFetcher.GetAffinityForView(typeof(object));
+
+ Assert.NotEqual(0, forUserControl);
+ Assert.Equal(0, forNonUserControl);
+ }
+
+ [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);
+
+ var fakeRenderedDecorator = new TestRoot();
+ fakeRenderedDecorator.Child = userControl;
+ Assert.True(userControl.Active);
+
+ fakeRenderedDecorator.Child = null;
+ Assert.False(userControl.Active);
+ }
+ }
+}
\ No newline at end of file