From 4f13d504967aa9be3ccd2123189dece3591d32ad Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Thu, 28 Sep 2017 15:53:05 +0200 Subject: [PATCH 1/8] Added initial changes to allow the user to change the selected item while the dropdown is closed but selected and allows the user to open the dropdown by pressing down --- src/Avalonia.Controls/DropDown.cs | 24 ++++++++++++++++++++++++ src/Avalonia.Controls/ItemsControl.cs | 11 +++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/Avalonia.Controls/DropDown.cs b/src/Avalonia.Controls/DropDown.cs index 5349fb1ca7..63fd6726b4 100644 --- a/src/Avalonia.Controls/DropDown.cs +++ b/src/Avalonia.Controls/DropDown.cs @@ -114,6 +114,30 @@ namespace Avalonia.Controls IsDropDownOpen = false; e.Handled = true; } + + if (!IsDropDownOpen) + { + if (e.Key == Key.Right) + { + if (++SelectedIndex >= ItemCount) + SelectedIndex = 0; + + e.Handled = true; + } + else if (e.Key == Key.Left) + { + if (--SelectedIndex < 0) + SelectedIndex = ItemCount - 1; + e.Handled = true; + } + else if (e.Key == Key.Down) + { + IsDropDownOpen = true; + if (SelectedIndex == -1) + SelectedIndex = 0; + e.Handled = true; + } + } } } diff --git a/src/Avalonia.Controls/ItemsControl.cs b/src/Avalonia.Controls/ItemsControl.cs index aa209e0462..4366de1cd6 100644 --- a/src/Avalonia.Controls/ItemsControl.cs +++ b/src/Avalonia.Controls/ItemsControl.cs @@ -11,6 +11,7 @@ using Avalonia.Controls.Presenters; using Avalonia.Controls.Primitives; using Avalonia.Controls.Templates; using Avalonia.Controls.Utils; +using Avalonia.Input; using Avalonia.LogicalTree; using Avalonia.Metadata; @@ -106,6 +107,12 @@ namespace Avalonia.Controls set { SetAndRaise(ItemsProperty, ref _items, value); } } + public int ItemCount + { + get; + private set; + } + /// /// Gets or sets the panel used to display the items. /// @@ -352,6 +359,10 @@ namespace Avalonia.Controls RemoveControlItemsFromLogicalChildren(e.OldItems); break; } + + int? count = (Items as IList)?.Count; + if (count != null) + ItemCount = (int)count; var collection = sender as ICollection; PseudoClasses.Set(":empty", collection == null || collection.Count == 0); From 3081a07f1c64403a668049d8e0652255b26fc888 Mon Sep 17 00:00:00 2001 From: Jurjen Biewenga Date: Mon, 16 Oct 2017 11:27:06 +0200 Subject: [PATCH 2/8] Made the requested changes --- src/Avalonia.Controls/DropDown.cs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/Avalonia.Controls/DropDown.cs b/src/Avalonia.Controls/DropDown.cs index c38f9ffd9e..cdb179cac9 100644 --- a/src/Avalonia.Controls/DropDown.cs +++ b/src/Avalonia.Controls/DropDown.cs @@ -104,7 +104,7 @@ namespace Avalonia.Controls if (!e.Handled) { if (e.Key == Key.F4 || - (e.Key == Key.Down && ((e.Modifiers & InputModifiers.Alt) != 0))) + ((e.Key == Key.Down || e.Key == Key.Up) && ((e.Modifiers & InputModifiers.Alt) != 0))) { IsDropDownOpen = !IsDropDownOpen; e.Handled = true; @@ -117,24 +117,21 @@ namespace Avalonia.Controls if (!IsDropDownOpen) { - if (e.Key == Key.Right) + if (e.Key == Key.Down) { + if (SelectedIndex == -1) + SelectedIndex = 0; + if (++SelectedIndex >= ItemCount) SelectedIndex = 0; e.Handled = true; } - else if (e.Key == Key.Left) + else if (e.Key == Key.Up) { if (--SelectedIndex < 0) SelectedIndex = ItemCount - 1; - e.Handled = true; - } - else if (e.Key == Key.Down) - { - IsDropDownOpen = true; - if (SelectedIndex == -1) - SelectedIndex = 0; + e.Handled = true; } } From 2f4887318495bfbf1fffa772bf65e729a678eceb Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Thu, 19 Oct 2017 00:26:27 +0200 Subject: [PATCH 3/8] Select dropdown item on focus. Arrow keys within the dropdown are handled by the keyboard navigation handler, so detect when this focuses a new item and select it. --- src/Avalonia.Controls/DropDown.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Avalonia.Controls/DropDown.cs b/src/Avalonia.Controls/DropDown.cs index cdb179cac9..6b27c479ba 100644 --- a/src/Avalonia.Controls/DropDown.cs +++ b/src/Avalonia.Controls/DropDown.cs @@ -96,6 +96,16 @@ namespace Avalonia.Controls this.UpdateSelectionBoxItem(this.SelectedItem); } + protected override void OnGotFocus(GotFocusEventArgs e) + { + base.OnGotFocus(e); + + if (!e.Handled && e.NavigationMethod == NavigationMethod.Directional) + { + e.Handled = UpdateSelectionFromEventSource(e.Source); + } + } + /// protected override void OnKeyDown(KeyEventArgs e) { From 6dd497bbc82bbc6e7ddb429244dd72b8b8b31817 Mon Sep 17 00:00:00 2001 From: Goldy Date: Wed, 22 Nov 2017 14:46:08 +0300 Subject: [PATCH 4/8] Code improvement Removed unused or duplicated code --- src/Avalonia.Controls/Calendar/Calendar.cs | 2 +- src/Avalonia.Controls/Control.cs | 1 - .../Primitives/SelectingItemsControl.cs | 2 +- src/Avalonia.Controls/TreeView.cs | 5 +---- src/Avalonia.Visuals/Media/PathMarkupParser.cs | 2 +- .../Rendering/SceneGraph/SceneBuilder.cs | 2 +- .../Avalonia.Markup/Data/Parsers/ArgumentListParser.cs | 10 +--------- .../Avalonia.Direct2D1/Media/DrawingContextImpl.cs | 1 - 8 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/Avalonia.Controls/Calendar/Calendar.cs b/src/Avalonia.Controls/Calendar/Calendar.cs index 8c79e5dce5..59281c5ad0 100644 --- a/src/Avalonia.Controls/Calendar/Calendar.cs +++ b/src/Avalonia.Controls/Calendar/Calendar.cs @@ -549,7 +549,7 @@ namespace Avalonia.Controls } else { - if (addedDate.HasValue && !(SelectedDates.Count > 0 && SelectedDates[0] == addedDate.Value)) + if (!(SelectedDates.Count > 0 && SelectedDates[0] == addedDate.Value)) { foreach (DateTime item in SelectedDates) { diff --git a/src/Avalonia.Controls/Control.cs b/src/Avalonia.Controls/Control.cs index d4777b2f8a..6e1e1a05f1 100644 --- a/src/Avalonia.Controls/Control.cs +++ b/src/Avalonia.Controls/Control.cs @@ -621,7 +621,6 @@ namespace Avalonia.Controls Contract.Requires(property != null); Contract.Requires(selector != null); Contract.Requires(className != null); - Contract.Requires(property != null); if (string.IsNullOrWhiteSpace(className)) { diff --git a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs index ab09a4701d..563d394919 100644 --- a/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs +++ b/src/Avalonia.Controls/Primitives/SelectingItemsControl.cs @@ -297,7 +297,7 @@ namespace Avalonia.Controls.Primitives .OfType() .FirstOrDefault(x => x.LogicalParent == this && ItemContainerGenerator?.IndexFromContainer(x) != -1); - return item as IControl; + return item; } /// diff --git a/src/Avalonia.Controls/TreeView.cs b/src/Avalonia.Controls/TreeView.cs index 079e571d29..fa3ecdedef 100644 --- a/src/Avalonia.Controls/TreeView.cs +++ b/src/Avalonia.Controls/TreeView.cs @@ -176,10 +176,7 @@ namespace Avalonia.Controls SelectedItem = item; - if (SelectedItem != null) - { - MarkContainerSelected(container, true); - } + MarkContainerSelected(container, true); } } diff --git a/src/Avalonia.Visuals/Media/PathMarkupParser.cs b/src/Avalonia.Visuals/Media/PathMarkupParser.cs index fbc189546c..9e4a3cbeae 100644 --- a/src/Avalonia.Visuals/Media/PathMarkupParser.cs +++ b/src/Avalonia.Visuals/Media/PathMarkupParser.cs @@ -320,7 +320,7 @@ namespace Avalonia.Media if (c == 'E') { readSign = false; - readExponent = c == 'E'; + readExponent = true; } } else diff --git a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs index 90ef78de37..b5ac7e4077 100644 --- a/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs +++ b/src/Avalonia.Visuals/Rendering/SceneGraph/SceneBuilder.cs @@ -192,7 +192,7 @@ namespace Avalonia.Rendering.SceneGraph UpdateLayer(node, scene.Layers[node.LayerRoot]); } } - else if (!startLayer && node.LayerRoot == node.Visual && node.Parent != null) + else if (node.LayerRoot == node.Visual && node.Parent != null) { ClearLayer(scene, node); } diff --git a/src/Markup/Avalonia.Markup/Data/Parsers/ArgumentListParser.cs b/src/Markup/Avalonia.Markup/Data/Parsers/ArgumentListParser.cs index a824a38867..563b372c78 100644 --- a/src/Markup/Avalonia.Markup/Data/Parsers/ArgumentListParser.cs +++ b/src/Markup/Avalonia.Markup/Data/Parsers/ArgumentListParser.cs @@ -51,15 +51,7 @@ namespace Avalonia.Markup.Data.Parsers } } - if (!r.End) - { - r.Take(); - return result; - } - else - { - throw new ExpressionParseException(r.Position, "Expected ']'."); - } + throw new ExpressionParseException(r.Position, "Expected ']'."); } return null; diff --git a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs index 6a72923ce3..b1bfdcbfeb 100644 --- a/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs +++ b/src/Windows/Avalonia.Direct2D1/Media/DrawingContextImpl.cs @@ -54,7 +54,6 @@ namespace Avalonia.Direct2D1.Media _finishedCallback = finishedCallback; _directWriteFactory = directWriteFactory; _imagingFactory = imagingFactory; - _swapChain = swapChain; _renderTarget.BeginDraw(); } From d4a32764cad02f4b65a3dfa7b0d7a12137d35c1e Mon Sep 17 00:00:00 2001 From: Goldy Date: Wed, 22 Nov 2017 14:56:33 +0300 Subject: [PATCH 5/8] Add null check for parameter Remove unused variable --- src/Avalonia.Controls/VirtualizingStackPanel.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Avalonia.Controls/VirtualizingStackPanel.cs b/src/Avalonia.Controls/VirtualizingStackPanel.cs index 834f6d218b..409dd231ad 100644 --- a/src/Avalonia.Controls/VirtualizingStackPanel.cs +++ b/src/Avalonia.Controls/VirtualizingStackPanel.cs @@ -134,12 +134,14 @@ namespace Avalonia.Controls protected override IInputElement GetControlInDirection(NavigationDirection direction, IControl from) { + if (from == null) + return null; + var logicalScrollable = Parent as ILogicalScrollable; - var fromControl = from as IControl; - if (logicalScrollable?.IsLogicalScrollEnabled == true && fromControl != null) + if (logicalScrollable?.IsLogicalScrollEnabled == true) { - return logicalScrollable.GetControlInDirection(direction, fromControl); + return logicalScrollable.GetControlInDirection(direction, from); } else { From 3243e0fd158769e84656d63caa085e0aba9f4d75 Mon Sep 17 00:00:00 2001 From: Steven Kirk Date: Sat, 2 Dec 2017 12:04:55 +0100 Subject: [PATCH 6/8] Added failing test for #1303 `TemplateBinding_With_Null_Path_Works` is a failing test for #1303, also added a passing test for the same thing with `Binding`: `Binding_With_Null_Path_Works`. --- .../Data/BindingTests.cs | 23 +++++++++++++ .../Data/BindingTests_TemplatedParent.cs | 32 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs index 230e61f300..9a08073920 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests.cs @@ -13,6 +13,7 @@ using Moq; using Xunit; using System.ComponentModel; using System.Runtime.CompilerServices; +using Avalonia.UnitTests; namespace Avalonia.Markup.Xaml.UnitTests.Data { @@ -337,6 +338,28 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data Assert.Equal("foo", target.Content); } + [Fact] + public void Binding_With_Null_Path_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var textBlock = window.FindControl("textBlock"); + + window.DataContext = "foo"; + window.ApplyTemplate(); + + Assert.Equal("foo", textBlock.Text); + } + } + private class TwoWayBindingTest : Control { public static readonly StyledProperty TwoWayProperty = diff --git a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs index 197afe46ee..ccb13039f1 100644 --- a/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs +++ b/tests/Avalonia.Markup.Xaml.UnitTests/Data/BindingTests_TemplatedParent.cs @@ -11,6 +11,9 @@ using Avalonia.Markup.Xaml.Data; using Avalonia.Styling; using Xunit; using System.Reactive.Disposables; +using Avalonia.UnitTests; +using Avalonia.VisualTree; +using System.Linq; namespace Avalonia.Markup.Xaml.UnitTests.Data { @@ -56,6 +59,35 @@ namespace Avalonia.Markup.Xaml.UnitTests.Data BindingPriority.TemplatedParent)); } + [Fact] + public void TemplateBinding_With_Null_Path_Works() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var xaml = @" + + +"; + var loader = new AvaloniaXamlLoader(); + var window = (Window)loader.Load(xaml); + var button = window.FindControl