diff --git a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
index 11d50afe93..c4f4362537 100644
--- a/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
+++ b/src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
@@ -33,7 +33,14 @@ namespace Avalonia.Data.Converters
if (typeof(ICommand).IsAssignableFrom(targetType) && value is Delegate d && d.Method.GetParameters().Length <= 1)
{
- return new MethodToCommandConverter(d);
+ if (d.Method.IsPrivate == false)
+ {
+ return new MethodToCommandConverter(d);
+ }
+ else
+ {
+ return new BindingNotification(new InvalidCastException("You can't bind to private methods!"), BindingErrorType.Error);
+ }
}
if (TypeUtilities.TryConvert(targetType, value, culture, out var result))
diff --git a/src/Avalonia.Controls/ContextMenu.cs b/src/Avalonia.Controls/ContextMenu.cs
index 2b122d4174..7b35e35278 100644
--- a/src/Avalonia.Controls/ContextMenu.cs
+++ b/src/Avalonia.Controls/ContextMenu.cs
@@ -450,6 +450,11 @@ namespace Avalonia.Controls
if (sender is Control control
&& control.ContextMenu is ContextMenu contextMenu)
{
+ if (contextMenu._popup?.Parent == control)
+ {
+ ((ISetLogicalParent)contextMenu._popup).SetParent(null);
+ }
+
contextMenu.Close();
}
}
diff --git a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
index d024f86b32..dfbd3f9a36 100644
--- a/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
+++ b/src/Avalonia.Controls/Flyouts/FlyoutBase.cs
@@ -175,7 +175,8 @@ namespace Avalonia.Controls.Primitives
IsOpen = false;
Popup.IsOpen = false;
-
+ ((ISetLogicalParent)Popup).SetParent(null);
+
// Ensure this isn't active
_transientDisposable?.Dispose();
_transientDisposable = null;
diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor
index 3dd98f8cd3..3fe4c299cf 100644
--- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor
+++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor
@@ -1,14 +1,12 @@
+ onkeyup="@OnKeyUp"
+ onpointerdown="@OnPointerDown"
+ onpointerup="@OnPointerUp"
+ onpointermove="@OnPointerMove">
diff --git a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs
index be58d9d49c..1f411d0cee 100644
--- a/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs
+++ b/src/Web/Avalonia.Web.Blazor/AvaloniaView.razor.cs
@@ -56,90 +56,91 @@ namespace Avalonia.Web.Blazor
{
return _nativeControlHost ?? throw new InvalidOperationException("Blazor View wasn't initialized yet");
}
-
- private void OnTouchStart(TouchEventArgs e)
+
+ private void OnTouchCancel(TouchEventArgs e)
{
foreach (var touch in e.ChangedTouches)
{
- _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchBegin, new Point(touch.ClientX, touch.ClientY),
+ _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchCancel, new Point(touch.ClientX, touch.ClientY),
GetModifiers(e), touch.Identifier);
}
}
- private void OnTouchEnd(TouchEventArgs e)
+ private void OnTouchMove(TouchEventArgs e)
{
foreach (var touch in e.ChangedTouches)
{
- _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchEnd, new Point(touch.ClientX, touch.ClientY),
+ _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchUpdate, new Point(touch.ClientX, touch.ClientY),
GetModifiers(e), touch.Identifier);
}
}
- private void OnTouchCancel(TouchEventArgs e)
+ private void OnPointerMove(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
- foreach (var touch in e.ChangedTouches)
+ if (e.PointerType != "touch")
{
- _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchCancel, new Point(touch.ClientX, touch.ClientY),
- GetModifiers(e), touch.Identifier);
+ _topLevelImpl.RawMouseEvent(RawPointerEventType.Move, new Point(e.ClientX, e.ClientY), GetModifiers(e));
}
}
- private void OnTouchMove(TouchEventArgs e)
+ private void OnPointerUp(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
- foreach (var touch in e.ChangedTouches)
+ if (e.PointerType == "touch")
{
- _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchUpdate, new Point(touch.ClientX, touch.ClientY),
- GetModifiers(e), touch.Identifier);
+ _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchEnd, new Point(e.ClientX, e.ClientY),
+ GetModifiers(e), e.PointerId);
}
- }
-
- private void OnMouseMove(MouseEventArgs e)
- {
- _topLevelImpl.RawMouseEvent(RawPointerEventType.Move, new Point(e.ClientX, e.ClientY), GetModifiers(e));
- }
+ else
+ {
+ RawPointerEventType type = default;
- private void OnMouseUp(MouseEventArgs e)
- {
- RawPointerEventType type = default;
+ switch (e.Button)
+ {
+ case 0:
+ type = RawPointerEventType.LeftButtonUp;
+ break;
- switch (e.Button)
- {
- case 0:
- type = RawPointerEventType.LeftButtonUp;
- break;
+ case 1:
+ type = RawPointerEventType.MiddleButtonUp;
+ break;
- case 1:
- type = RawPointerEventType.MiddleButtonUp;
- break;
+ case 2:
+ type = RawPointerEventType.RightButtonUp;
+ break;
+ }
- case 2:
- type = RawPointerEventType.RightButtonUp;
- break;
+ _topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e));
}
-
- _topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e));
}
- private void OnMouseDown(MouseEventArgs e)
+ private void OnPointerDown(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
- RawPointerEventType type = default;
-
- switch (e.Button)
+ if (e.PointerType == "touch")
{
- case 0:
- type = RawPointerEventType.LeftButtonDown;
- break;
+ _topLevelImpl.RawTouchEvent(RawPointerEventType.TouchBegin, new Point(e.ClientX, e.ClientY),
+ GetModifiers(e), e.PointerId);
+ }
+ else
+ {
+ RawPointerEventType type = default;
+
+ switch (e.Button)
+ {
+ case 0:
+ type = RawPointerEventType.LeftButtonDown;
+ break;
- case 1:
- type = RawPointerEventType.MiddleButtonDown;
- break;
+ case 1:
+ type = RawPointerEventType.MiddleButtonDown;
+ break;
- case 2:
- type = RawPointerEventType.RightButtonDown;
- break;
- }
+ case 2:
+ type = RawPointerEventType.RightButtonDown;
+ break;
+ }
- _topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e));
+ _topLevelImpl.RawMouseEvent(type, new Point(e.ClientX, e.ClientY), GetModifiers(e));
+ }
}
private void OnWheel(WheelEventArgs e)
@@ -189,7 +190,7 @@ namespace Avalonia.Web.Blazor
return modifiers;
}
- private static RawInputModifiers GetModifiers(MouseEventArgs e)
+ private static RawInputModifiers GetModifiers(Microsoft.AspNetCore.Components.Web.PointerEventArgs e)
{
var modifiers = RawInputModifiers.None;
diff --git a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
index ba01f3db40..b63cbd286e 100644
--- a/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/ContextMenuTests.cs
@@ -446,6 +446,27 @@ namespace Avalonia.Controls.UnitTests
}
}
+ [Fact]
+ public void Should_Reset_Popup_Parent_On_Target_Detached()
+ {
+ using (Application())
+ {
+ var userControl = new UserControl();
+ var window = PreparedWindow(userControl);
+ window.Show();
+
+ var menu = new ContextMenu();
+ userControl.ContextMenu = menu;
+ menu.Open();
+
+ var popup = Assert.IsType
(menu.Parent);
+ Assert.NotNull(popup.Parent);
+
+ window.Content = null;
+ Assert.Null(popup.Parent);
+ }
+ }
+
[Fact]
public void Context_Menu_In_Resources_Can_Be_Shared()
{
diff --git a/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs
index c2dd8cf01a..8b77074960 100644
--- a/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs
+++ b/tests/Avalonia.Controls.UnitTests/FlyoutTests.cs
@@ -432,6 +432,26 @@ namespace Avalonia.Controls.UnitTests
}
}
+ [Fact]
+ public void Should_Reset_Popup_Parent_On_Target_Detached()
+ {
+ using (CreateServicesWithFocus())
+ {
+ var userControl = new UserControl();
+ var window = PreparedWindow(userControl);
+ window.Show();
+
+ var flyout = new TestFlyout();
+ flyout.ShowAt(userControl);
+
+ var popup = Assert.IsType(flyout.Popup);
+ Assert.NotNull(popup.Parent);
+
+ window.Content = null;
+ Assert.Null(popup.Parent);
+ }
+ }
+
[Fact]
public void ContextFlyout_Can_Be_Set_In_Styles()
{
@@ -549,5 +569,10 @@ namespace Avalonia.Controls.UnitTests
new PointerPointProperties(RawInputModifiers.None, PointerUpdateKind.LeftButtonPressed),
KeyModifiers.None);
}
+
+ public class TestFlyout : Flyout
+ {
+ public new Popup Popup => base.Popup;
+ }
}
}
diff --git a/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Method.cs b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Method.cs
new file mode 100644
index 0000000000..e613a178d5
--- /dev/null
+++ b/tests/Avalonia.Markup.UnitTests/Data/BindingTests_Method.cs
@@ -0,0 +1,32 @@
+using Avalonia.Controls;
+using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Interactivity;
+using Xunit;
+
+namespace Avalonia.Markup.UnitTests.Data
+{
+ public class BindingTests_Method
+ {
+ [Fact]
+ public void Binding_To_Private_Methods_Shouldnt_Work()
+ {
+ var vm = new TestClass();
+ var target = new Button
+ {
+ DataContext = vm,
+ [!Button.CommandProperty] = new Binding("MyMethod"),
+ };
+ target.RaiseEvent(new RoutedEventArgs(AccessKeyHandler.AccessKeyPressedEvent));
+
+ Assert.False(vm.IsSet);
+ }
+
+
+ class TestClass
+ {
+ public bool IsSet { get; set; }
+ private void MyMethod() => IsSet = true;
+ }
+ }
+}