diff --git a/src/Avalonia.Base/Threading/DispatcherPriority.cs b/src/Avalonia.Base/Threading/DispatcherPriority.cs
index ceda1c397f..a2b4b86bac 100644
--- a/src/Avalonia.Base/Threading/DispatcherPriority.cs
+++ b/src/Avalonia.Base/Threading/DispatcherPriority.cs
@@ -17,7 +17,7 @@ namespace Avalonia.Threading
SystemIdle = 1,
///
- /// The job will be processed when the application sis idle.
+ /// The job will be processed when the application is idle.
///
ApplicationIdle = 2,
diff --git a/src/Avalonia.Controls/TreeViewItem.cs b/src/Avalonia.Controls/TreeViewItem.cs
index a224ceaadd..d3bd45d13c 100644
--- a/src/Avalonia.Controls/TreeViewItem.cs
+++ b/src/Avalonia.Controls/TreeViewItem.cs
@@ -51,6 +51,7 @@ namespace Avalonia.Controls
SelectableMixin.Attach(IsSelectedProperty);
FocusableProperty.OverrideDefaultValue(true);
ItemsPanelProperty.OverrideDefaultValue(DefaultPanel);
+ ParentProperty.Changed.AddClassHandler((o, e) => o.OnParentChanged(e));
RequestBringIntoViewEvent.AddClassHandler((x, e) => x.OnRequestBringIntoView(e));
}
@@ -179,5 +180,16 @@ namespace Avalonia.Controls
return logical != null ? result : @default;
}
+
+ private void OnParentChanged(AvaloniaPropertyChangedEventArgs e)
+ {
+ if (!((ILogical)this).IsAttachedToLogicalTree && e.NewValue is null)
+ {
+ // If we're not attached to the logical tree, then OnDetachedFromLogicalTree isn't going to be
+ // called when the item is removed. This results in the item not being removed from the index,
+ // causing #3551. In this case, update the index when Parent is changed to null.
+ ItemContainerGenerator.UpdateIndex();
+ }
+ }
}
}
diff --git a/src/Avalonia.Input/FocusManager.cs b/src/Avalonia.Input/FocusManager.cs
index bcae8a3c53..011ae6ce6b 100644
--- a/src/Avalonia.Input/FocusManager.cs
+++ b/src/Avalonia.Input/FocusManager.cs
@@ -53,11 +53,11 @@ namespace Avalonia.Input
///
/// The control to focus.
/// The method by which focus was changed.
- /// Any input modifiers active at the time of focus.
+ /// Any key modifiers active at the time of focus.
public void Focus(
IInputElement control,
NavigationMethod method = NavigationMethod.Unspecified,
- InputModifiers modifiers = InputModifiers.None)
+ KeyModifiers keyModifiers = KeyModifiers.None)
{
if (control != null)
{
@@ -67,7 +67,7 @@ namespace Avalonia.Input
if (scope != null)
{
Scope = scope;
- SetFocusedElement(scope, control, method, modifiers);
+ SetFocusedElement(scope, control, method, keyModifiers);
}
}
else if (Current != null)
@@ -95,7 +95,7 @@ namespace Avalonia.Input
/// The focus scope.
/// The element to focus. May be null.
/// The method by which focus was changed.
- /// Any input modifiers active at the time of focus.
+ /// Any key modifiers active at the time of focus.
///
/// If the specified scope is the current then the keyboard focus
/// will change.
@@ -104,7 +104,7 @@ namespace Avalonia.Input
IFocusScope scope,
IInputElement element,
NavigationMethod method = NavigationMethod.Unspecified,
- InputModifiers modifiers = InputModifiers.None)
+ KeyModifiers keyModifiers = KeyModifiers.None)
{
Contract.Requires(scope != null);
@@ -123,7 +123,7 @@ namespace Avalonia.Input
if (Scope == scope)
{
- KeyboardDevice.Instance?.SetFocusedElement(element, method, modifiers);
+ KeyboardDevice.Instance?.SetFocusedElement(element, method, keyModifiers);
}
}
@@ -195,7 +195,7 @@ namespace Avalonia.Input
{
if (element is IInputElement inputElement && CanFocus(inputElement))
{
- Instance?.Focus(inputElement, NavigationMethod.Pointer, ev.InputModifiers);
+ Instance?.Focus(inputElement, NavigationMethod.Pointer, ev.KeyModifiers);
break;
}
diff --git a/src/Avalonia.Input/IFocusManager.cs b/src/Avalonia.Input/IFocusManager.cs
index 84cd791ee0..9122cc428d 100644
--- a/src/Avalonia.Input/IFocusManager.cs
+++ b/src/Avalonia.Input/IFocusManager.cs
@@ -20,11 +20,11 @@ namespace Avalonia.Input
///
/// The control to focus.
/// The method by which focus was changed.
- /// Any input modifiers active at the time of focus.
+ /// Any key modifiers active at the time of focus.
void Focus(
- IInputElement control,
+ IInputElement control,
NavigationMethod method = NavigationMethod.Unspecified,
- InputModifiers modifiers = InputModifiers.None);
+ KeyModifiers keyModifiers = KeyModifiers.None);
///
/// Notifies the focus manager of a change in focus scope.
diff --git a/src/Avalonia.Input/IKeyboardDevice.cs b/src/Avalonia.Input/IKeyboardDevice.cs
index 2725638b9a..ba7e0484ee 100644
--- a/src/Avalonia.Input/IKeyboardDevice.cs
+++ b/src/Avalonia.Input/IKeyboardDevice.cs
@@ -63,6 +63,6 @@ namespace Avalonia.Input
void SetFocusedElement(
IInputElement element,
NavigationMethod method,
- InputModifiers modifiers);
+ KeyModifiers modifiers);
}
}
diff --git a/src/Avalonia.Input/IKeyboardNavigationHandler.cs b/src/Avalonia.Input/IKeyboardNavigationHandler.cs
index 4d0ae7e85d..88d00b3b50 100644
--- a/src/Avalonia.Input/IKeyboardNavigationHandler.cs
+++ b/src/Avalonia.Input/IKeyboardNavigationHandler.cs
@@ -19,10 +19,10 @@ namespace Avalonia.Input
///
/// The current element.
/// The direction to move.
- /// Any input modifiers active at the time of focus.
+ /// Any key modifiers active at the time of focus.
void Move(
IInputElement element,
NavigationDirection direction,
- InputModifiers modifiers = InputModifiers.None);
+ KeyModifiers keyModifiers = KeyModifiers.None);
}
-}
\ No newline at end of file
+}
diff --git a/src/Avalonia.Input/KeyboardDevice.cs b/src/Avalonia.Input/KeyboardDevice.cs
index 006a6b12d9..0321b0bdf3 100644
--- a/src/Avalonia.Input/KeyboardDevice.cs
+++ b/src/Avalonia.Input/KeyboardDevice.cs
@@ -35,7 +35,7 @@ namespace Avalonia.Input
public void SetFocusedElement(
IInputElement element,
NavigationMethod method,
- InputModifiers modifiers)
+ KeyModifiers keyModifiers)
{
if (element != FocusedElement)
{
@@ -53,7 +53,7 @@ namespace Avalonia.Input
{
RoutedEvent = InputElement.GotFocusEvent,
NavigationMethod = method,
- InputModifiers = modifiers,
+ KeyModifiers = keyModifiers,
});
}
}
diff --git a/src/Avalonia.Input/KeyboardNavigationHandler.cs b/src/Avalonia.Input/KeyboardNavigationHandler.cs
index 323a225b50..c425eeeedb 100644
--- a/src/Avalonia.Input/KeyboardNavigationHandler.cs
+++ b/src/Avalonia.Input/KeyboardNavigationHandler.cs
@@ -91,11 +91,11 @@ namespace Avalonia.Input
///
/// The current element.
/// The direction to move.
- /// Any input modifiers active at the time of focus.
+ /// Any key modifiers active at the time of focus.
public void Move(
IInputElement element,
NavigationDirection direction,
- InputModifiers modifiers = InputModifiers.None)
+ KeyModifiers keyModifiers = KeyModifiers.None)
{
Contract.Requires(element != null);
@@ -106,7 +106,7 @@ namespace Avalonia.Input
var method = direction == NavigationDirection.Next ||
direction == NavigationDirection.Previous ?
NavigationMethod.Tab : NavigationMethod.Directional;
- FocusManager.Instance.Focus(next, method, modifiers);
+ FocusManager.Instance.Focus(next, method, keyModifiers);
}
}
@@ -123,7 +123,7 @@ namespace Avalonia.Input
{
var direction = (e.KeyModifiers & KeyModifiers.Shift) == 0 ?
NavigationDirection.Next : NavigationDirection.Previous;
- Move(current, direction, e.Modifiers);
+ Move(current, direction, e.KeyModifiers);
e.Handled = true;
}
}
diff --git a/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs b/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs
index fe626f4d38..abace92f08 100644
--- a/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs
+++ b/src/Windows/Avalonia.Win32.Interop/WinForms/WinFormsAvaloniaControlHost.cs
@@ -45,7 +45,7 @@ namespace Avalonia.Win32.Embedding
focused = focused.VisualParent;
if (focused == _root)
- KeyboardDevice.Instance.SetFocusedElement(null, NavigationMethod.Unspecified, InputModifiers.None);
+ KeyboardDevice.Instance.SetFocusedElement(null, NavigationMethod.Unspecified, KeyModifiers.None);
}
private void PlatformImpl_LostFocus()
diff --git a/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs b/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs
index f1123e3958..1258bb0109 100644
--- a/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs
+++ b/src/Windows/Avalonia.Win32/Input/WindowsKeyboardDevice.cs
@@ -44,7 +44,7 @@ namespace Avalonia.Win32.Input
public void WindowActivated(Window window)
{
- SetFocusedElement(window, NavigationMethod.Unspecified, InputModifiers.None);
+ SetFocusedElement(window, NavigationMethod.Unspecified, KeyModifiers.None);
}
public string StringFromVirtualKey(uint virtualKey)
diff --git a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
index 32b09e2c47..bd303a81cd 100644
--- a/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/TreeViewTests.cs
@@ -1002,6 +1002,35 @@ namespace Avalonia.Controls.UnitTests
Assert.Equal(1, child2Node.Presenter.Panel.Children.Count);
}
+ [Fact]
+ public void Clearing_TreeView_Items_Clears_Index()
+ {
+ // Issue #3551
+ var tree = CreateTestTreeData();
+ var target = new TreeView
+ {
+ Template = CreateTreeViewTemplate(),
+ Items = tree,
+ };
+
+ var root = new TestRoot();
+ root.Child = target;
+
+ CreateNodeDataTemplate(target);
+ ApplyTemplates(target);
+
+ var rootNode = tree[0];
+ var container = (TreeViewItem)target.ItemContainerGenerator.Index.ContainerFromItem(rootNode);
+
+ Assert.NotNull(container);
+
+ root.Child = null;
+
+ tree.Clear();
+
+ Assert.Empty(target.ItemContainerGenerator.Index.Containers);
+ }
+
private void ApplyTemplates(TreeView tree)
{
tree.ApplyTemplate();
diff --git a/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs b/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs
index 3c8e800fca..df0a077c7f 100644
--- a/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs
+++ b/tests/Avalonia.Input.UnitTests/KeyboardDeviceTests.cs
@@ -35,7 +35,7 @@ namespace Avalonia.Input.UnitTests
target.SetFocusedElement(
focused.Object,
NavigationMethod.Unspecified,
- InputModifiers.None);
+ KeyModifiers.None);
target.ProcessRawEvent(
new RawKeyEventArgs(
@@ -75,7 +75,7 @@ namespace Avalonia.Input.UnitTests
target.SetFocusedElement(
focused.Object,
NavigationMethod.Unspecified,
- InputModifiers.None);
+ KeyModifiers.None);
target.ProcessRawEvent(
new RawTextInputEventArgs(