From 493b6ab29e7993ccd0b7f4c0c9ec2f1959e351b3 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 29 Jul 2020 14:28:08 -0300 Subject: [PATCH 1/5] add failing unit test. --- .../ToolTipTests.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs b/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs index 9d7bc6af74..67df6343af 100644 --- a/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs @@ -64,6 +64,40 @@ namespace Avalonia.Controls.UnitTests Assert.False(ToolTip.GetIsOpen(target)); } } + + [Fact] + public void Should_Close_When_Tip_Is_Changed() + { + using (UnitTestApplication.Start(TestServices.StyledWindow)) + { + var window = new Window(); + + var panel = new Panel(); + + var target = new Decorator() + { + [ToolTip.TipProperty] = "Tip", + [ToolTip.ShowDelayProperty] = 0 + }; + + panel.Children.Add(target); + + window.Content = panel; + + window.ApplyTemplate(); + window.Presenter.ApplyTemplate(); + + Assert.True((target as IVisual).IsAttachedToVisualTree); + + _mouseHelper.Enter(target); + + Assert.True(ToolTip.GetIsOpen(target)); + + ToolTip.SetTip(target, ""); + + Assert.False(ToolTip.GetIsOpen(target)); + } + } [Fact] public void Should_Open_On_Pointer_Enter() From d5e0634cc7a654931ef8da338c93cccb317d7c9b Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Wed, 29 Jul 2020 14:28:52 -0300 Subject: [PATCH 2/5] force tooltip to close if the datacontext and therefore the tip property change. --- src/Avalonia.Controls/ToolTipService.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Avalonia.Controls/ToolTipService.cs b/src/Avalonia.Controls/ToolTipService.cs index 569697304f..587bbb6aa6 100644 --- a/src/Avalonia.Controls/ToolTipService.cs +++ b/src/Avalonia.Controls/ToolTipService.cs @@ -29,6 +29,7 @@ namespace Avalonia.Controls control.PointerEnter -= ControlPointerEnter; control.PointerLeave -= ControlPointerLeave; control.DetachedFromVisualTree -= ControlDetaching; + Close(control); } if (e.NewValue != null) From 037e63d9ab7145893b684b28d2ec62ed614a8a92 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Jul 2020 10:48:29 -0300 Subject: [PATCH 3/5] add unit test for specific scenario where datacontext changes tip before remvoed from visual tree. --- .../ToolTipTests.cs | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs b/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs index 67df6343af..e52e7a487b 100644 --- a/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs +++ b/tests/Avalonia.Controls.UnitTests/ToolTipTests.cs @@ -1,5 +1,6 @@ using System; using System.Reactive.Disposables; +using Avalonia.Markup.Xaml; using Avalonia.Platform; using Avalonia.Threading; using Avalonia.UnitTests; @@ -66,34 +67,33 @@ namespace Avalonia.Controls.UnitTests } [Fact] - public void Should_Close_When_Tip_Is_Changed() + public void Should_Close_When_Tip_Is_Opened_And_Detached_From_Visual_Tree() { using (UnitTestApplication.Start(TestServices.StyledWindow)) { - var window = new Window(); - - var panel = new Panel(); - - var target = new Decorator() - { - [ToolTip.TipProperty] = "Tip", - [ToolTip.ShowDelayProperty] = 0 - }; + var xaml = @" + + + + +"; + var window = (Window)AvaloniaRuntimeXamlLoader.Load(xaml); - panel.Children.Add(target); - - window.Content = panel; - + window.DataContext = new ToolTipViewModel(); window.ApplyTemplate(); window.Presenter.ApplyTemplate(); + var target = window.Find("PART_target"); + var panel = window.Find("PART_panel"); + Assert.True((target as IVisual).IsAttachedToVisualTree); _mouseHelper.Enter(target); Assert.True(ToolTip.GetIsOpen(target)); - - ToolTip.SetTip(target, ""); + + panel.Children.Remove(target); Assert.False(ToolTip.GetIsOpen(target)); } @@ -242,4 +242,9 @@ namespace Avalonia.Controls.UnitTests } } } + + internal class ToolTipViewModel + { + public string Tip => "Tip"; + } } From eb132105e43adef9ce6d2feb4675dbab30b1904d Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Jul 2020 10:52:47 -0300 Subject: [PATCH 4/5] restore tooltipservice. failing unit test. --- src/Avalonia.Controls/ToolTipService.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Avalonia.Controls/ToolTipService.cs b/src/Avalonia.Controls/ToolTipService.cs index 587bbb6aa6..569697304f 100644 --- a/src/Avalonia.Controls/ToolTipService.cs +++ b/src/Avalonia.Controls/ToolTipService.cs @@ -29,7 +29,6 @@ namespace Avalonia.Controls control.PointerEnter -= ControlPointerEnter; control.PointerLeave -= ControlPointerLeave; control.DetachedFromVisualTree -= ControlDetaching; - Close(control); } if (e.NewValue != null) From 49e41b959438fa4ccab96fcc4b18cef696b859c9 Mon Sep 17 00:00:00 2001 From: Dan Walmsley Date: Thu, 30 Jul 2020 13:00:05 -0300 Subject: [PATCH 5/5] prevent tooltipgetting stuck. --- src/Avalonia.Controls/ToolTip.cs | 1 + src/Avalonia.Controls/ToolTipService.cs | 15 ++++++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/Avalonia.Controls/ToolTip.cs b/src/Avalonia.Controls/ToolTip.cs index b458b15c64..cf0652247f 100644 --- a/src/Avalonia.Controls/ToolTip.cs +++ b/src/Avalonia.Controls/ToolTip.cs @@ -66,6 +66,7 @@ namespace Avalonia.Controls static ToolTip() { TipProperty.Changed.Subscribe(ToolTipService.Instance.TipChanged); + IsOpenProperty.Changed.Subscribe(ToolTipService.Instance.TipOpenChanged); IsOpenProperty.Changed.Subscribe(IsOpenChanged); } diff --git a/src/Avalonia.Controls/ToolTipService.cs b/src/Avalonia.Controls/ToolTipService.cs index 569697304f..e2a0f9e50c 100644 --- a/src/Avalonia.Controls/ToolTipService.cs +++ b/src/Avalonia.Controls/ToolTipService.cs @@ -28,20 +28,33 @@ namespace Avalonia.Controls { control.PointerEnter -= ControlPointerEnter; control.PointerLeave -= ControlPointerLeave; - control.DetachedFromVisualTree -= ControlDetaching; } if (e.NewValue != null) { control.PointerEnter += ControlPointerEnter; control.PointerLeave += ControlPointerLeave; + } + } + + internal void TipOpenChanged(AvaloniaPropertyChangedEventArgs e) + { + var control = (Control)e.Sender; + + if (e.OldValue is false && e.NewValue is true) + { control.DetachedFromVisualTree += ControlDetaching; } + else if(e.OldValue is true && e.NewValue is false) + { + control.DetachedFromVisualTree -= ControlDetaching; + } } private void ControlDetaching(object sender, VisualTreeAttachmentEventArgs e) { var control = (Control)sender; + control.DetachedFromVisualTree -= ControlDetaching; Close(control); }