From c311474d5b5a507e73b96c8000d96480c5f8088f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 28 Sep 2018 17:28:13 +0200 Subject: [PATCH 01/21] Don't await updates in render loop. The render loop should not be waiting for an update to occur on the UI thread before rendering a frame. Instead of awaiting the call, simply call `IDispatcher.Post` to fire-and-forget. Placed a guard around the update to make sure multiple updates don't get queued if an update doesn't complete in a single frame. Also don't call `IRenderLoopTask.Update` unless `IRenderLoopTask.NeedsUpdate == true`. Fixes #1920 --- src/Avalonia.Visuals/Rendering/RenderLoop.cs | 31 +++++++++++++++----- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/RenderLoop.cs b/src/Avalonia.Visuals/Rendering/RenderLoop.cs index d0d5b2250d..427c91658b 100644 --- a/src/Avalonia.Visuals/Rendering/RenderLoop.cs +++ b/src/Avalonia.Visuals/Rendering/RenderLoop.cs @@ -19,7 +19,8 @@ namespace Avalonia.Rendering private readonly IDispatcher _dispatcher; private List _items = new List(); private IRenderTimer _timer; - private int inTick; + private int _inTick; + private int _inUpdate; /// /// Initializes a new instance of the class. @@ -84,21 +85,35 @@ namespace Avalonia.Rendering } } - private async void TimerTick(TimeSpan time) + private void TimerTick(TimeSpan time) { - if (Interlocked.CompareExchange(ref inTick, 1, 0) == 0) + if (Interlocked.CompareExchange(ref _inTick, 1, 0) == 0) { try { - if (_items.Any(item => item.NeedsUpdate)) + if (_items.Any(item => item.NeedsUpdate) && + Interlocked.CompareExchange(ref _inUpdate, 1, 0) == 0) { - await _dispatcher.InvokeAsync(() => + System.Diagnostics.Debug.WriteLine("Posted update"); + _dispatcher.Post(() => { foreach (var i in _items) { - i.Update(time); + if (i.NeedsUpdate) + { + try + { + i.Update(time); + } + catch (Exception ex) + { + Logger.Error(LogArea.Visual, this, "Exception in render update: {Error}", ex); + } + } } - }, DispatcherPriority.Render).ConfigureAwait(false); + + Interlocked.Exchange(ref _inUpdate, 0); + }, DispatcherPriority.Render); } foreach (var i in _items) @@ -112,7 +127,7 @@ namespace Avalonia.Rendering } finally { - Interlocked.Exchange(ref inTick, 0); + Interlocked.Exchange(ref _inTick, 0); } } } From 51f85cfac9a375912b8efd7bbf483b01d069d8d4 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 28 Sep 2018 20:10:44 +0200 Subject: [PATCH 02/21] Removed debug code. --- src/Avalonia.Visuals/Rendering/RenderLoop.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Visuals/Rendering/RenderLoop.cs b/src/Avalonia.Visuals/Rendering/RenderLoop.cs index 427c91658b..e10e1c4006 100644 --- a/src/Avalonia.Visuals/Rendering/RenderLoop.cs +++ b/src/Avalonia.Visuals/Rendering/RenderLoop.cs @@ -94,7 +94,6 @@ namespace Avalonia.Rendering if (_items.Any(item => item.NeedsUpdate) && Interlocked.CompareExchange(ref _inUpdate, 1, 0) == 0) { - System.Diagnostics.Debug.WriteLine("Posted update"); _dispatcher.Post(() => { foreach (var i in _items) From 62a5b66228fbf5e08f0b63e3cd888f9c7e971165 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 28 Sep 2018 20:11:35 +0200 Subject: [PATCH 03/21] Fix failing tests. --- .../Rendering/RenderLoopTests.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/tests/Avalonia.Visuals.UnitTests/Rendering/RenderLoopTests.cs b/tests/Avalonia.Visuals.UnitTests/Rendering/RenderLoopTests.cs index 16c2d3ee18..e9aefc46e6 100644 --- a/tests/Avalonia.Visuals.UnitTests/Rendering/RenderLoopTests.cs +++ b/tests/Avalonia.Visuals.UnitTests/Rendering/RenderLoopTests.cs @@ -19,14 +19,13 @@ namespace Avalonia.Visuals.UnitTests.Rendering bool inDispatcher = false; dispatcher.Setup( - d => d.InvokeAsync(It.IsAny(), DispatcherPriority.Render)) + d => d.Post(It.IsAny(), DispatcherPriority.Render)) .Callback((Action a, DispatcherPriority _) => { inDispatcher = true; a(); inDispatcher = false; - }) - .Returns(Task.CompletedTask); + }); var timer = new Mock(); @@ -71,14 +70,13 @@ namespace Avalonia.Visuals.UnitTests.Rendering var dispatcher = new Mock(); bool inDispatcher = false; dispatcher.Setup( - d => d.InvokeAsync(It.IsAny(), DispatcherPriority.Render)) + d => d.Post(It.IsAny(), DispatcherPriority.Render)) .Callback((Action a, DispatcherPriority _) => { inDispatcher = true; a(); inDispatcher = false; - }) - .Returns(Task.CompletedTask); + }); var timer = new Mock(); var loop = new RenderLoop(timer.Object, dispatcher.Object); @@ -100,9 +98,8 @@ namespace Avalonia.Visuals.UnitTests.Rendering { var dispatcher = new Mock(); dispatcher.Setup( - d => d.InvokeAsync(It.IsAny(), DispatcherPriority.Render)) - .Callback((Action a, DispatcherPriority _) => a()) - .Returns(Task.CompletedTask); + d => d.Post(It.IsAny(), DispatcherPriority.Render)) + .Callback((Action a, DispatcherPriority _) => a()); var timer = new Mock(); var loop = new RenderLoop(timer.Object, dispatcher.Object); From 4645ec1c4515f193237bbba0a0056c9d5c16ea75 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Fri, 28 Sep 2018 20:12:34 +0200 Subject: [PATCH 04/21] Use indexer for iterating items. --- src/Avalonia.Visuals/Rendering/RenderLoop.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Visuals/Rendering/RenderLoop.cs b/src/Avalonia.Visuals/Rendering/RenderLoop.cs index e10e1c4006..df32d9eec1 100644 --- a/src/Avalonia.Visuals/Rendering/RenderLoop.cs +++ b/src/Avalonia.Visuals/Rendering/RenderLoop.cs @@ -96,13 +96,15 @@ namespace Avalonia.Rendering { _dispatcher.Post(() => { - foreach (var i in _items) + for (var i = 0; i < _items.Count; ++i) { - if (i.NeedsUpdate) + var item = _items[i]; + + if (item.NeedsUpdate) { try { - i.Update(time); + item.Update(time); } catch (Exception ex) { From 1df826281bbd61df064bcb334e5f968c2fbedb71 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 29 Sep 2018 16:26:32 +0200 Subject: [PATCH 05/21] Added failing test for #1932. --- .../Primitives/SelectingItemsControlTests.cs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs index 14e1b15ebc..bbe1d85acb 100644 --- a/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs +++ b/tests/Avalonia.Controls.UnitTests/Primitives/SelectingItemsControlTests.cs @@ -707,6 +707,26 @@ namespace Avalonia.Controls.UnitTests.Primitives Assert.True(target.SelectedIndex == 1); } + [Fact] + public void Binding_With_DelayedBinding_And_Initialization_Where_DataContext_Is_Root_Works() + { + // Test for #1932. + var root = new RootWithItems(); + + root.BeginInit(); + root.DataContext = root; + + var target = new ListBox(); + target.BeginInit(); + root.Child = target; + + DelayedBinding.Add(target, ItemsControl.ItemsProperty, new Binding(nameof(RootWithItems.Items))); + DelayedBinding.Add(target, ListBox.SelectedItemProperty, new Binding(nameof(RootWithItems.Selected))); + target.EndInit(); + root.EndInit(); + + Assert.Equal("b", target.SelectedItem); + } private FuncControlTemplate Template() { @@ -745,5 +765,11 @@ namespace Avalonia.Controls.UnitTests.Primitives public IList Items { get; set; } public Item SelectedItem { get; set; } } + + private class RootWithItems : TestRoot + { + public List Items { get; set; } = new List() { "a", "b", "c", "d", "e" }; + public string Selected { get; set; } = "b"; + } } } From d0b77ecca191fe630f01e8af8eb2945f9f89ce55 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 29 Sep 2018 16:30:21 +0200 Subject: [PATCH 06/21] Update before calling base.EndInit. Fixes #1932. --- src/Avalonia.Controls/Primitives/SelectingItemsControl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index d7db04f369..f8440aac47 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -289,12 +289,12 @@ namespace Avalonia.Controls.Primitives /// public override void EndInit() { - base.EndInit(); - if (--_updateCount == 0) { UpdateFinished(); } + + base.EndInit(); } /// From b53b3d590f6db78df737c878b1b3391e0334cada Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Mon, 24 Sep 2018 13:08:48 +0300 Subject: [PATCH 07/21] add failing unit test for listbox issue #1936 --- .../ListBoxTests.cs | 108 +++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index a5f5f8d328..eb3a6cf0c0 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -1,16 +1,17 @@ // 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.Collections.ObjectModel; using System.Linq; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; -using Avalonia.Input; +using Avalonia.Data; using Avalonia.LogicalTree; using Avalonia.Styling; using Avalonia.UnitTests; using Avalonia.VisualTree; using Xunit; -using Avalonia.Collections; namespace Avalonia.Controls.UnitTests { @@ -170,6 +171,109 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new Size(100, 10), target.Scroll.Viewport); } + [Theory] + [InlineData(ItemVirtualizationMode.Simple)] + [InlineData(ItemVirtualizationMode.None)] + public void When_Added_Removed_AfterItems_Reset_Should_Work(ItemVirtualizationMode virtMode) + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var items = new ObservableCollection(); + + Action create = () => + { + foreach (var i in Enumerable.Range(1, 7)) + { + items.Add(i.ToString()); + } + }; + + create(); + + var wnd = new Window() { SizeToContent = SizeToContent.WidthAndHeight }; + + wnd.IsVisible = true; + + var target = new ListBox() { VirtualizationMode = virtMode }; + + wnd.Content = target; + + var lm = wnd.LayoutManager; + + target.Height = 110;//working fine when <=106 or >=119 + target.Width = 50; + + target.ItemTemplate = new FuncDataTemplate(c => + { + var tb = new TextBlock() { Height = 10, Width = 30 }; + tb.Bind(TextBlock.TextProperty, new Binding()); + return tb; + }, true); + + target.DataContext = items; + + lm.ExecuteInitialLayoutPass(wnd); + + target.Bind(ItemsControl.ItemsProperty, new Binding()); + + lm.ExecuteLayoutPass(); + + var panel = target.Presenter.Panel; + + Func itemsToString = () => + string.Join(",", panel.Children.OfType().Select(l => l.Content.ToString()).ToArray()); + + Action addafter = (item, newitem) => + { + items.Insert(items.IndexOf(item) + 1, newitem); + + lm.ExecuteLayoutPass(); + }; + + Action remove = item => + { + items.Remove(item); + lm.ExecuteLayoutPass(); + }; + + addafter("1", "1+");//expected 1,1+,2,3,4,5,6,7 + + addafter("2", "2+");//expected 1,1+,2,2+,3,4,5,6 + + remove("2+");//expected 1,1+,2,3,4,5,6,7 + + //Reset items + items.Clear(); + create(); + + addafter("1", "1+");//expected 1,1+,2,3,4,5,6,7 + + addafter("2", "2+");//expected 1,1+,2,2+,3,4,5,6 + + remove("2+");//expected 1,1+,2,3,4,5,6,7 + + var sti = itemsToString(); + + var lbItems = panel.Children.OfType().ToArray(); + + Assert.Equal("1", lbItems[0].Content); + Assert.Equal("1+", lbItems[1].Content); + Assert.Equal("2", lbItems[2].Content); + Assert.Equal("3", lbItems[3].Content); //bug it's 2+ instead + Assert.Equal("4", lbItems[4].Content); + + int lbi = 0; + + //ensure all items are fine + foreach (var lb in lbItems) + { + Assert.Equal(items[lbi++], lb.Content); + } + + //Assert.Equal("1,1+,2,3,4,5,6,7", sti); + } + } + private FuncControlTemplate ListBoxTemplate() { return new FuncControlTemplate(parent => From a865f6dddfcc7bfee925af7bdfc407601ead504b Mon Sep 17 00:00:00 2001 From: Andrey Kunchev Date: Sat, 6 Oct 2018 01:41:08 +0300 Subject: [PATCH 08/21] introduce local functions for listbox issue #1936 --- .../ListBoxTests.cs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index eb3a6cf0c0..c6968f19f8 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -180,13 +180,13 @@ namespace Avalonia.Controls.UnitTests { var items = new ObservableCollection(); - Action create = () => + void create() { foreach (var i in Enumerable.Range(1, 7)) { items.Add(i.ToString()); } - }; + } create(); @@ -220,21 +220,20 @@ namespace Avalonia.Controls.UnitTests var panel = target.Presenter.Panel; - Func itemsToString = () => - string.Join(",", panel.Children.OfType().Select(l => l.Content.ToString()).ToArray()); + string itemsToString() => + string.Join(",", panel.Children.OfType().Select(l => l.Content.ToString()).ToArray()); - Action addafter = (item, newitem) => - { - items.Insert(items.IndexOf(item) + 1, newitem); - - lm.ExecuteLayoutPass(); - }; + void addafter(string item, string newitem) + { + items.Insert(items.IndexOf(item) + 1, newitem); + lm.ExecuteLayoutPass(); + } - Action remove = item => + void remove(string item) { items.Remove(item); lm.ExecuteLayoutPass(); - }; + } addafter("1", "1+");//expected 1,1+,2,3,4,5,6,7 @@ -276,7 +275,7 @@ namespace Avalonia.Controls.UnitTests private FuncControlTemplate ListBoxTemplate() { - return new FuncControlTemplate(parent => + return new FuncControlTemplate(parent => new ScrollViewer { Name = "PART_ScrollViewer", @@ -293,7 +292,7 @@ namespace Avalonia.Controls.UnitTests private FuncControlTemplate ListBoxItemTemplate() { - return new FuncControlTemplate(parent => + return new FuncControlTemplate(parent => new ContentPresenter { Name = "PART_ContentPresenter", From 6efba9837db65f3a0eef0674a9277f1e345c1731 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 7 Oct 2018 00:59:50 +0200 Subject: [PATCH 09/21] Simplified test. Simplified @donandren's test for #1936. --- .../ListBoxTests.cs | 113 +++--------------- 1 file changed, 18 insertions(+), 95 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index c6968f19f8..1debccd3c5 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.ObjectModel; using System.Linq; +using Avalonia.Collections; using Avalonia.Controls.Presenters; using Avalonia.Controls.Templates; using Avalonia.Data; @@ -171,106 +172,28 @@ namespace Avalonia.Controls.UnitTests Assert.Equal(new Size(100, 10), target.Scroll.Viewport); } - [Theory] - [InlineData(ItemVirtualizationMode.Simple)] - [InlineData(ItemVirtualizationMode.None)] - public void When_Added_Removed_AfterItems_Reset_Should_Work(ItemVirtualizationMode virtMode) + [Fact] + public void Containers_Correct_After_Clear_Add_Remove() { - using (UnitTestApplication.Start(TestServices.StyledWindow)) + // Issue #1936 + var items = new AvaloniaList(Enumerable.Range(0, 11).Select(x => $"Item {x}")); + var target = new ListBox { - var items = new ObservableCollection(); - - void create() - { - foreach (var i in Enumerable.Range(1, 7)) - { - items.Add(i.ToString()); - } - } - - create(); - - var wnd = new Window() { SizeToContent = SizeToContent.WidthAndHeight }; - - wnd.IsVisible = true; - - var target = new ListBox() { VirtualizationMode = virtMode }; - - wnd.Content = target; - - var lm = wnd.LayoutManager; - - target.Height = 110;//working fine when <=106 or >=119 - target.Width = 50; - - target.ItemTemplate = new FuncDataTemplate(c => - { - var tb = new TextBlock() { Height = 10, Width = 30 }; - tb.Bind(TextBlock.TextProperty, new Binding()); - return tb; - }, true); - - target.DataContext = items; - - lm.ExecuteInitialLayoutPass(wnd); - - target.Bind(ItemsControl.ItemsProperty, new Binding()); - - lm.ExecuteLayoutPass(); - - var panel = target.Presenter.Panel; - - string itemsToString() => - string.Join(",", panel.Children.OfType().Select(l => l.Content.ToString()).ToArray()); - - void addafter(string item, string newitem) - { - items.Insert(items.IndexOf(item) + 1, newitem); - lm.ExecuteLayoutPass(); - } - - void remove(string item) - { - items.Remove(item); - lm.ExecuteLayoutPass(); - } - - addafter("1", "1+");//expected 1,1+,2,3,4,5,6,7 - - addafter("2", "2+");//expected 1,1+,2,2+,3,4,5,6 - - remove("2+");//expected 1,1+,2,3,4,5,6,7 - - //Reset items - items.Clear(); - create(); - - addafter("1", "1+");//expected 1,1+,2,3,4,5,6,7 - - addafter("2", "2+");//expected 1,1+,2,2+,3,4,5,6 - - remove("2+");//expected 1,1+,2,3,4,5,6,7 - - var sti = itemsToString(); - - var lbItems = panel.Children.OfType().ToArray(); - - Assert.Equal("1", lbItems[0].Content); - Assert.Equal("1+", lbItems[1].Content); - Assert.Equal("2", lbItems[2].Content); - Assert.Equal("3", lbItems[3].Content); //bug it's 2+ instead - Assert.Equal("4", lbItems[4].Content); + Template = ListBoxTemplate(), + Items = items, + ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }), + SelectedIndex = 0, + }; - int lbi = 0; + Prepare(target); - //ensure all items are fine - foreach (var lb in lbItems) - { - Assert.Equal(items[lbi++], lb.Content); - } + items.Clear(); + items.AddRange(Enumerable.Range(0, 11).Select(x => $"Item {x}")); + items.Remove("Item 2"); - //Assert.Equal("1,1+,2,3,4,5,6,7", sti); - } + Assert.Equal( + items, + target.Presenter.Panel.Children.Cast().Select(x => (string)x.Content)); } private FuncControlTemplate ListBoxTemplate() From c608163fe99412bf7ce1da9d898e08eeebef3341 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 7 Oct 2018 01:00:18 +0200 Subject: [PATCH 10/21] Ensure containers are ordered correctly. Fixes #1936. --- src/Avalonia.Controls/Generators/ItemContainerGenerator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs index 882d2f4ddd..f1a1f94a01 100644 --- a/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs +++ b/src/Avalonia.Controls/Generators/ItemContainerGenerator.cs @@ -15,7 +15,7 @@ namespace Avalonia.Controls.Generators /// public class ItemContainerGenerator : IItemContainerGenerator { - private Dictionary _containers = new Dictionary(); + private SortedDictionary _containers = new SortedDictionary(); /// /// Initializes a new instance of the class. @@ -246,4 +246,4 @@ namespace Avalonia.Controls.Generators Recycled?.Invoke(this, e); } } -} \ No newline at end of file +} From dd528260febb3cfc88e7e39eb39e37758fa84417 Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 02:18:10 +0300 Subject: [PATCH 11/21] feature: Support ReactiveUI WhenActivated --- .../AppBuilderExtensions.cs | 4 ++ .../AvaloniaActivationForViewFetcher.cs | 38 +++++++++++++++++++ 2 files changed, 42 insertions(+) create mode 100644 src/Avalonia.ReactiveUI/AvaloniaActivationForViewFetcher.cs 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 From d36a55f6158cdc28186ff3b33eec9cbdb6f842ce Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 11:45:04 +0300 Subject: [PATCH 12/21] Add ActivationForViewFetcher tests --- .../Avalonia.ReactiveUI.csproj | 5 +- .../Avalonia.ReactiveUI.UnitTests.csproj | 13 +++ .../AvaloniaActivationForViewFetcherTest.cs | 95 +++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) create mode 100644 tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj create mode 100644 tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs diff --git a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index ae1ef60464..83dbfa41f9 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -12,6 +12,7 @@ - - + + + \ No newline at end of file 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..88f4e08886 --- /dev/null +++ b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj @@ -0,0 +1,13 @@ + + + netcoreapp2.0 + + + + + + + + + + diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs new file mode 100644 index 0000000000..a3c6b1a44a --- /dev/null +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -0,0 +1,95 @@ +using System; +using System.Reactive.Concurrency; +using System.Reactive.Disposables; +using Avalonia.Controls; +using Avalonia.Rendering; +using Avalonia.Platform; +using Avalonia; +using ReactiveUI; +using DynamicData; +using Xunit; +using Splat; + +namespace Avalonia +{ + public class AvaloniaActivationForViewFetcherTest + { + public class TestUserControl : UserControl, IActivatable { } + + public class FakeRenderDecorator : Decorator, IRenderRoot + { + public Size ClientSize => new Size(100, 100); + + public IRenderer Renderer { get; } + + public double RenderScaling => 1; + + public IRenderTarget CreateRenderTarget() => null; + + public void Invalidate(Rect rect) { } + + public Point PointToClient(Point point) => point; + + public Point PointToScreen(Point point) => point; + } + + 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 VisualElementIsActivatedAndDeactivated() + { + var userControl = new TestUserControl(); + var activationForViewFetcher = new AvaloniaActivationForViewFetcher(); + + activationForViewFetcher + .GetActivationForView(userControl) + .ToObservableChangeSet(scheduler: ImmediateScheduler.Instance) + .Bind(out var activated) + .Subscribe(); + + var fakeRenderedDecorator = new FakeRenderDecorator(); + 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 ActivationForViewFetcherShouldSupportWhenActivated() + { + var locator = new ModernDependencyResolver(); + locator.InitializeSplat(); + locator.InitializeReactiveUI(); + locator.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher)); + using (locator.WithResolver()) + { + var userControl = new TestUserControlWithWhenActivated(); + Assert.False(userControl.Active); + + var fakeRenderedDecorator = new FakeRenderDecorator(); + fakeRenderedDecorator.Child = userControl; + Assert.True(userControl.Active); + + fakeRenderedDecorator.Child = null; + Assert.False(userControl.Active); + } + } + } +} \ No newline at end of file From accdd502b8ea73f616ed6d8042448be0b925c2d5 Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 11:48:24 +0300 Subject: [PATCH 13/21] Reference ReactiveUI the Avalonia way --- build/ReactiveUI.props | 2 +- src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) 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/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj index 83dbfa41f9..ae1ef60464 100644 --- a/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj +++ b/src/Avalonia.ReactiveUI/Avalonia.ReactiveUI.csproj @@ -12,7 +12,6 @@ - - - + + \ No newline at end of file From 0153c769979986220f22c0177235a339ecd2aeed Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 12:01:30 +0300 Subject: [PATCH 14/21] Add affinity tests --- .../AvaloniaActivationForViewFetcherTest.cs | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs index a3c6b1a44a..f07378fe0c 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -71,25 +71,35 @@ namespace Avalonia Assert.Equal(2, activated.Count); } + [Fact] + public void GetAffinityForViewShouldReturnNonZeroForVisualElements() + { + 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 ActivationForViewFetcherShouldSupportWhenActivated() { - var locator = new ModernDependencyResolver(); - locator.InitializeSplat(); - locator.InitializeReactiveUI(); - locator.RegisterConstant(new AvaloniaActivationForViewFetcher(), typeof(IActivationForViewFetcher)); - using (locator.WithResolver()) - { - var userControl = new TestUserControlWithWhenActivated(); - Assert.False(userControl.Active); + Locator.CurrentMutable.RegisterConstant( + new AvaloniaActivationForViewFetcher(), + typeof(IActivationForViewFetcher)); - var fakeRenderedDecorator = new FakeRenderDecorator(); - fakeRenderedDecorator.Child = userControl; - Assert.True(userControl.Active); + var userControl = new TestUserControlWithWhenActivated(); + Assert.False(userControl.Active); - fakeRenderedDecorator.Child = null; - Assert.False(userControl.Active); - } + var fakeRenderedDecorator = new FakeRenderDecorator(); + fakeRenderedDecorator.Child = userControl; + Assert.True(userControl.Active); + + fakeRenderedDecorator.Child = null; + Assert.False(userControl.Active); } } } \ No newline at end of file From cdc6033ed2a5eb54cee9d72b67332246a6f621f1 Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 12:02:53 +0300 Subject: [PATCH 15/21] Use Avalonia code style for test methods names --- .../AvaloniaActivationForViewFetcherTest.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs index f07378fe0c..694d75f5b7 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -49,7 +49,7 @@ namespace Avalonia } [Fact] - public void VisualElementIsActivatedAndDeactivated() + public void Visual_Element_Is_Activated_And_Deactivated() { var userControl = new TestUserControl(); var activationForViewFetcher = new AvaloniaActivationForViewFetcher(); @@ -72,7 +72,7 @@ namespace Avalonia } [Fact] - public void GetAffinityForViewShouldReturnNonZeroForVisualElements() + public void Get_Affinity_For_View_Should_Return_Non_Zero_For_Visual_Elements() { var userControl = new TestUserControl(); var activationForViewFetcher = new AvaloniaActivationForViewFetcher(); @@ -85,7 +85,7 @@ namespace Avalonia } [Fact] - public void ActivationForViewFetcherShouldSupportWhenActivated() + public void Activation_For_View_Fetcher_Should_Support_When_Activated() { Locator.CurrentMutable.RegisterConstant( new AvaloniaActivationForViewFetcher(), From ebcbe99c4e4d45204891dfcfdf4cc45cc04e76e4 Mon Sep 17 00:00:00 2001 From: artyom Date: Sun, 7 Oct 2018 14:03:38 +0300 Subject: [PATCH 16/21] Add tests for ReactiveUI 9.0.1 --- build.cake | 1 + .../ViewModels/MainWindowViewModel.cs | 1 + .../Avalonia.ReactiveUI.UnitTests.csproj | 3 ++- .../AvaloniaActivationForViewFetcherTest.cs | 22 +++---------------- 4 files changed, 7 insertions(+), 20 deletions(-) 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/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/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj index 88f4e08886..7c0ff79183 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj +++ b/tests/Avalonia.ReactiveUI.UnitTests/Avalonia.ReactiveUI.UnitTests.csproj @@ -8,6 +8,7 @@ - + + diff --git a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs index 694d75f5b7..97701f8437 100644 --- a/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs +++ b/tests/Avalonia.ReactiveUI.UnitTests/AvaloniaActivationForViewFetcherTest.cs @@ -4,6 +4,7 @@ using System.Reactive.Disposables; using Avalonia.Controls; using Avalonia.Rendering; using Avalonia.Platform; +using Avalonia.UnitTests; using Avalonia; using ReactiveUI; using DynamicData; @@ -16,23 +17,6 @@ namespace Avalonia { public class TestUserControl : UserControl, IActivatable { } - public class FakeRenderDecorator : Decorator, IRenderRoot - { - public Size ClientSize => new Size(100, 100); - - public IRenderer Renderer { get; } - - public double RenderScaling => 1; - - public IRenderTarget CreateRenderTarget() => null; - - public void Invalidate(Rect rect) { } - - public Point PointToClient(Point point) => point; - - public Point PointToScreen(Point point) => point; - } - public class TestUserControlWithWhenActivated : UserControl, IActivatable { public bool Active { get; private set; } @@ -60,7 +44,7 @@ namespace Avalonia .Bind(out var activated) .Subscribe(); - var fakeRenderedDecorator = new FakeRenderDecorator(); + var fakeRenderedDecorator = new TestRoot(); fakeRenderedDecorator.Child = userControl; Assert.True(activated[0]); Assert.Equal(1, activated.Count); @@ -94,7 +78,7 @@ namespace Avalonia var userControl = new TestUserControlWithWhenActivated(); Assert.False(userControl.Active); - var fakeRenderedDecorator = new FakeRenderDecorator(); + var fakeRenderedDecorator = new TestRoot(); fakeRenderedDecorator.Child = userControl; Assert.True(userControl.Active); From f907139a5c5195bf70da2b633cc30c8d3e1f9eb2 Mon Sep 17 00:00:00 2001 From: ahopper Date: Mon, 8 Oct 2018 12:54:23 +0100 Subject: [PATCH 17/21] make MONITORINFO a struct --- .../Avalonia.Win32/Interop/UnmanagedMethods.cs | 15 ++++++++++----- src/Windows/Avalonia.Win32/ScreenImpl.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index d6b95bc7b0..c7fce95fbc 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1055,12 +1055,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 NewMONITORINFO() + { + 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..6d56a5e213 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -26,7 +26,7 @@ namespace Avalonia.Win32 EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, (IntPtr monitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr data) => { - MONITORINFO monitorInfo = new MONITORINFO(); + MONITORINFO monitorInfo = MONITORINFO.NewMONITORINFO(); if (GetMonitorInfo(monitor, monitorInfo)) { RECT bounds = monitorInfo.rcMonitor; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 65580ff9c8..bd37aa6109 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -837,7 +837,7 @@ namespace Avalonia.Win32 if (monitor != IntPtr.Zero) { - MONITORINFO monitorInfo = new MONITORINFO(); + MONITORINFO monitorInfo = MONITORINFO.NewMONITORINFO(); if (GetMonitorInfo(monitor, monitorInfo)) { From 5786d51853d50d57a473949144a3bad39b01eac7 Mon Sep 17 00:00:00 2001 From: ahopper Date: Mon, 8 Oct 2018 13:51:38 +0100 Subject: [PATCH 18/21] pass MONITORINFO as ref --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 2 +- src/Windows/Avalonia.Win32/ScreenImpl.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index c7fce95fbc..a2801bb170 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -987,7 +987,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")] diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 6d56a5e213..81f6b0e65c 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -27,7 +27,7 @@ namespace Avalonia.Win32 (IntPtr monitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr data) => { MONITORINFO monitorInfo = MONITORINFO.NewMONITORINFO(); - if (GetMonitorInfo(monitor, monitorInfo)) + if (GetMonitorInfo(monitor,ref monitorInfo)) { RECT bounds = monitorInfo.rcMonitor; RECT workingArea = monitorInfo.rcWork; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index bd37aa6109..bbae965a22 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -839,7 +839,7 @@ namespace Avalonia.Win32 { MONITORINFO monitorInfo = MONITORINFO.NewMONITORINFO(); - if (GetMonitorInfo(monitor, monitorInfo)) + if (GetMonitorInfo(monitor,ref monitorInfo)) { RECT rcMonitorArea = monitorInfo.rcMonitor; From 314ada4e4a5d0d0a02599beec132257b78bdc602 Mon Sep 17 00:00:00 2001 From: ahopper Date: Tue, 9 Oct 2018 21:56:52 +0100 Subject: [PATCH 19/21] renamed NewMONITORINFO to CreateMONITORINFO --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 2 +- src/Windows/Avalonia.Win32/ScreenImpl.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index a2801bb170..c8059ee169 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1062,7 +1062,7 @@ namespace Avalonia.Win32.Interop public RECT rcWork; public int dwFlags; - public static MONITORINFO NewMONITORINFO() + public static MONITORINFO CreateMONITORINFO() { return new MONITORINFO() { cbSize = Marshal.SizeOf() }; } diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index 81f6b0e65c..b02eb64215 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -26,7 +26,7 @@ namespace Avalonia.Win32 EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, (IntPtr monitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr data) => { - MONITORINFO monitorInfo = MONITORINFO.NewMONITORINFO(); + MONITORINFO monitorInfo = MONITORINFO.CreateMONITORINFO(); if (GetMonitorInfo(monitor,ref monitorInfo)) { RECT bounds = monitorInfo.rcMonitor; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index bbae965a22..114a57eb6d 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -837,7 +837,7 @@ namespace Avalonia.Win32 if (monitor != IntPtr.Zero) { - MONITORINFO monitorInfo = MONITORINFO.NewMONITORINFO(); + MONITORINFO monitorInfo = MONITORINFO.CreateMONITORINFO(); if (GetMonitorInfo(monitor,ref monitorInfo)) { From 11d676ac0b816632f1163aafc92dae9d3aab01f8 Mon Sep 17 00:00:00 2001 From: ahopper Date: Tue, 9 Oct 2018 22:03:20 +0100 Subject: [PATCH 20/21] rename CreateMONITORINFO to Create --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 2 +- src/Windows/Avalonia.Win32/ScreenImpl.cs | 2 +- src/Windows/Avalonia.Win32/WindowImpl.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index c8059ee169..5fe889f41d 100644 --- a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs +++ b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs @@ -1062,7 +1062,7 @@ namespace Avalonia.Win32.Interop public RECT rcWork; public int dwFlags; - public static MONITORINFO CreateMONITORINFO() + public static MONITORINFO Create() { return new MONITORINFO() { cbSize = Marshal.SizeOf() }; } diff --git a/src/Windows/Avalonia.Win32/ScreenImpl.cs b/src/Windows/Avalonia.Win32/ScreenImpl.cs index b02eb64215..5cfceb68b7 100644 --- a/src/Windows/Avalonia.Win32/ScreenImpl.cs +++ b/src/Windows/Avalonia.Win32/ScreenImpl.cs @@ -26,7 +26,7 @@ namespace Avalonia.Win32 EnumDisplayMonitors(IntPtr.Zero, IntPtr.Zero, (IntPtr monitor, IntPtr hdcMonitor, ref Rect lprcMonitor, IntPtr data) => { - MONITORINFO monitorInfo = MONITORINFO.CreateMONITORINFO(); + MONITORINFO monitorInfo = MONITORINFO.Create(); if (GetMonitorInfo(monitor,ref monitorInfo)) { RECT bounds = monitorInfo.rcMonitor; diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 114a57eb6d..45120fa21b 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -837,7 +837,7 @@ namespace Avalonia.Win32 if (monitor != IntPtr.Zero) { - MONITORINFO monitorInfo = MONITORINFO.CreateMONITORINFO(); + MONITORINFO monitorInfo = MONITORINFO.Create(); if (GetMonitorInfo(monitor,ref monitorInfo)) { From 29d02f0ad2b21269148128988645c3a17363b76e Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 10 Oct 2018 12:44:46 +0100 Subject: [PATCH 21/21] Call SetProcessDPIAware() on Win32 backend so that the application is guarenteed to see the real dpi. --- src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs | 3 +++ src/Windows/Avalonia.Win32/Win32Platform.cs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs b/src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs index 5fe889f41d..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); 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)