From f63a64e11b57417bb3a205d885eab7de97735394 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 7 Jan 2017 19:20:38 +0100 Subject: [PATCH 1/9] Remove pointless code. --- src/Avalonia.Controls/ScrollViewer.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/Avalonia.Controls/ScrollViewer.cs b/src/Avalonia.Controls/ScrollViewer.cs index 396905c26b..ad771d333e 100644 --- a/src/Avalonia.Controls/ScrollViewer.cs +++ b/src/Avalonia.Controls/ScrollViewer.cs @@ -157,10 +157,6 @@ namespace Avalonia.Controls /// public ScrollViewer() { - Observable.CombineLatest( - this.GetObservable(ExtentProperty), - this.GetObservable(ViewportProperty)) - .Select(x => new { Extent = x[0], Viewport = x[1] }); } /// From 44f6d12157fc16cd2df27fdf9b2fa95ae1542f41 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 7 Jan 2017 19:21:04 +0100 Subject: [PATCH 2/9] Add failing test for #834. --- .../ListBoxTests.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs index f8eea8c4eb..a588e88eb2 100644 --- a/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ListBoxTests.cs @@ -153,6 +153,23 @@ namespace Avalonia.Controls.UnitTests Assert.False(((ListBoxItem)target.Presenter.Panel.Children[0]).IsSelected); } + [Fact] + public void ScrollViewer_Should_Have_Correct_Extent_And_Viewport() + { + var target = new ListBox + { + Template = ListBoxTemplate(), + Items = Enumerable.Range(0, 20).Select(x => $"Item {x}").ToList(), + ItemTemplate = new FuncDataTemplate(x => new TextBlock { Width = 20, Height = 10 }), + SelectedIndex = 0, + }; + + Prepare(target); + + Assert.Equal(new Size(20, 20), target.Scroll.Extent); + Assert.Equal(new Size(100, 10), target.Scroll.Viewport); + } + private FuncControlTemplate ListBoxTemplate() { return new FuncControlTemplate(parent => @@ -233,6 +250,7 @@ namespace Avalonia.Controls.UnitTests i.InvalidateMeasure(); } + target.Measure(new Size(100, 100)); target.Arrange(new Rect(0, 0, 100, 100)); } From 49757372a90e1117f102d23faeea6b1ee0b467cf Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 7 Jan 2017 19:32:23 +0100 Subject: [PATCH 3/9] Fix for #834. Listen for bounds changes on the `VirtualizingPanel` and update the scroll accordingly. --- .../Presenters/ItemVirtualizer.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs index 3a2cb688cb..c4edaf5387 100644 --- a/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs +++ b/src/Avalonia.Controls/Presenters/ItemVirtualizer.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Collections.Specialized; +using System.Reactive.Linq; using Avalonia.Controls.Primitives; using Avalonia.Controls.Utils; using Avalonia.Input; @@ -17,6 +18,7 @@ namespace Avalonia.Controls.Presenters internal abstract class ItemVirtualizer : IVirtualizingController, IDisposable { private double _crossAxisOffset; + private IDisposable _subscriptions; /// /// Initializes a new instance of the class. @@ -27,6 +29,15 @@ namespace Avalonia.Controls.Presenters Owner = owner; Items = owner.Items; ItemCount = owner.Items.Count(); + + var panel = VirtualizingPanel; + + if (panel != null) + { + _subscriptions = panel.GetObservable(Panel.BoundsProperty) + .Skip(1) + .Subscribe(_ => InvalidateScroll()); + } } /// @@ -240,6 +251,9 @@ namespace Avalonia.Controls.Presenters /// public virtual void Dispose() { + _subscriptions?.Dispose(); + _subscriptions = null; + if (VirtualizingPanel != null) { VirtualizingPanel.Controller = null; From 799ffe72b8ca4197944c1f825de1cdd8fd663cd8 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 7 Jan 2017 19:32:42 +0100 Subject: [PATCH 4/9] Fix erroneous test expectations. --- .../Presenters/ItemsPresenterTests_Virtualization.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs index 847662e629..1ea64b915c 100644 --- a/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs +++ b/tests/Avalonia.Controls.UnitTests/Presenters/ItemsPresenterTests_Virtualization.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Avalonia.Collections; using Avalonia.Controls.Generators; using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; @@ -113,7 +112,7 @@ namespace Avalonia.Controls.UnitTests.Presenters var scroll = (ScrollContentPresenter)target.Parent; Assert.Equal(new Size(10, 20), scroll.Extent); - Assert.Equal(new Size(0, 10), scroll.Viewport); + Assert.Equal(new Size(100, 10), scroll.Viewport); } [Fact] @@ -255,7 +254,7 @@ namespace Avalonia.Controls.UnitTests.Presenters Assert.Equal(10, target.Panel.Children.Count); Assert.Equal(new Size(10, 20), scroll.Extent); - Assert.Equal(new Size(0, 10), scroll.Viewport); + Assert.Equal(new Size(100, 10), scroll.Viewport); target.VirtualizationMode = ItemVirtualizationMode.None; target.Measure(new Size(100, 100)); From 5e844308685ea679d3fca0eadf38ffb727a9649c Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 7 Jan 2017 20:21:01 +0100 Subject: [PATCH 5/9] Don't focus Win32 window in design mode. Fixes #837. --- src/Windows/Avalonia.Win32/WindowImpl.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Windows/Avalonia.Win32/WindowImpl.cs b/src/Windows/Avalonia.Win32/WindowImpl.cs index 2129090a64..db46538796 100644 --- a/src/Windows/Avalonia.Win32/WindowImpl.cs +++ b/src/Windows/Avalonia.Win32/WindowImpl.cs @@ -709,7 +709,10 @@ namespace Avalonia.Win32 MaximizeWithoutCoveringTaskbar(); } - SetFocus(_hwnd); + if (!Design.IsDesignMode) + { + SetFocus(_hwnd); + } } private void MaximizeWithoutCoveringTaskbar() From 7cf208208b2095d4af2c65df6a4256804fcfd259 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 8 Jan 2017 02:13:11 +0100 Subject: [PATCH 6/9] Don't show TextBox caret when control not focused. Fixes #836. --- src/Avalonia.Controls/Presenters/TextPresenter.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Avalonia.Controls/Presenters/TextPresenter.cs b/src/Avalonia.Controls/Presenters/TextPresenter.cs index d3cf4e5509..39759f78f1 100644 --- a/src/Avalonia.Controls/Presenters/TextPresenter.cs +++ b/src/Avalonia.Controls/Presenters/TextPresenter.cs @@ -173,10 +173,13 @@ namespace Avalonia.Controls.Presenters { if (this.GetVisualParent() != null) { - _caretBlink = true; - _caretTimer.Stop(); - _caretTimer.Start(); - InvalidateVisual(); + if (_caretTimer.IsEnabled) + { + _caretBlink = true; + _caretTimer.Stop(); + _caretTimer.Start(); + InvalidateVisual(); + } if (IsMeasureValid) { From 8f21388e28dbd39587df309dd28998538b13e024 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 8 Jan 2017 15:16:50 +0100 Subject: [PATCH 7/9] Added RelativeSource=Self binding mode. --- .../Avalonia.Markup.Xaml/Data/Binding.cs | 4 ++ .../Data/RelativeSource.cs | 1 + .../Data/BindingTests_Self.cs | 63 +++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Self.cs diff --git a/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs b/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs index 086257f24c..649596a74e 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Data/Binding.cs @@ -115,6 +115,10 @@ namespace Avalonia.Markup.Xaml.Data anchor, enableDataValidation); } + else if (RelativeSource.Mode == RelativeSourceMode.Self) + { + observer = CreateSourceObserver(target, pathInfo.Path, enableDataValidation); + } else if (RelativeSource.Mode == RelativeSourceMode.TemplatedParent) { observer = CreateTemplatedParentObserver(target, pathInfo.Path); diff --git a/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs b/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs index 6771aaf644..c0eb581af2 100644 --- a/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs +++ b/src/Markup/Avalonia.Markup.Xaml/Data/RelativeSource.cs @@ -5,6 +5,7 @@ namespace Avalonia.Markup.Xaml.Data { public enum RelativeSourceMode { + Self, DataContext, TemplatedParent, } diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Self.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Self.cs new file mode 100644 index 0000000000..e0d16a9563 --- /dev/null +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_Self.cs @@ -0,0 +1,63 @@ +// 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 Moq; +using Avalonia.Controls; +using Avalonia.Data; +using Avalonia.Markup.Xaml.Data; +using Avalonia.Styling; +using Xunit; +using System.Reactive.Disposables; + +namespace Avalonia.Markup.Xaml.UnitTests.Data +{ + public class BindingTests_Self + { + [Fact] + public void Binding_To_Property_On_Self_Should_Work() + { + var target = new TextBlock + { + Tag = "Hello World!", + [!TextBlock.TextProperty] = new Binding("Tag") + { + RelativeSource = new RelativeSource(RelativeSourceMode.Self) + }, + }; + + Assert.Equal("Hello World!", target.Text); + } + + [Fact] + public void TwoWay_Binding_To_Property_On_Self_Should_Work() + { + var target = new TextBlock + { + Tag = "Hello World!", + [!TextBlock.TextProperty] = new Binding("Tag", BindingMode.TwoWay) + { + RelativeSource = new RelativeSource(RelativeSourceMode.Self) + }, + }; + + Assert.Equal("Hello World!", target.Text); + target.Text = "Goodbye cruel world :("; + Assert.Equal("Goodbye cruel world :(", target.Text); + } + + private Mock CreateTarget( + ITemplatedControl templatedParent = null, + string text = null) + { + var result = new Mock(); + + result.Setup(x => x.GetValue(Control.TemplatedParentProperty)).Returns(templatedParent); + result.Setup(x => x.GetValue((AvaloniaProperty)Control.TemplatedParentProperty)).Returns(templatedParent); + result.Setup(x => x.GetValue((AvaloniaProperty)TextBox.TextProperty)).Returns(text); + result.Setup(x => x.Bind(It.IsAny(), It.IsAny>(), It.IsAny())) + .Returns(Disposable.Empty); + return result; + } + } +} From 7d6503a5f76a6f463ffcef23bafb2208d8bac172 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 8 Jan 2017 16:50:09 +0100 Subject: [PATCH 8/9] Added RelativeSource=Self XAML tests. --- .../Avalonia.Markup.Xaml.UnitTests.csproj | 1 + .../Xaml/BindingTests.cs | 44 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj index 4ad740bab0..f820f7d5ab 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Avalonia.Markup.Xaml.UnitTests.csproj @@ -96,6 +96,7 @@ + diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs index 868471466a..a66d6ac6d8 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BindingTests.cs @@ -145,5 +145,49 @@ namespace Avalonia.Markup.Xaml.UnitTests.Xaml Assert.Equal("foo", border.DataContext); } } + + [Fact(Skip = "OmniXaml doesn't support nested markup extensions. #119")] + public void Binding_To_Self_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = (TextBlock)window.Content; + + window.ApplyTemplate(); + + Assert.Equal("foo", textBlock.Text); + } + } + + [Fact] + public void Longform_Binding_To_Self_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + + + + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = (TextBlock)window.Content; + + window.ApplyTemplate(); + + Assert.Equal("foo", textBlock.Text); + } + } } } From 48a33cc415a7aae627d4bd86f1e265fd4482160f Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sun, 8 Jan 2017 16:50:25 +0100 Subject: [PATCH 9/9] Handle null in MarkupBindingChainException --- src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs b/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs index dab5756976..51afe1ffbf 100644 --- a/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs +++ b/src/Markup/Avalonia.Markup/Data/MarkupBindingChainException.cs @@ -26,7 +26,7 @@ namespace Avalonia.Markup.Data _nodes = null; } - public bool HasNodes => _nodes.Count > 0; + public bool HasNodes => _nodes?.Count > 0; public void AddNode(string node) => _nodes.Add(node); public void Commit(string expression)