Fixed: The propagation issue of OverlayFeedbackElement.ClosedEvent.
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Sandbox.Views.MainWindow"
|
||||
x:DataType="vm:MainWindowViewModel"
|
||||
xmlns:sys="using:System"
|
||||
Icon="/Assets/avalonia-logo.ico"
|
||||
Title="Sandbox">
|
||||
|
||||
@@ -15,19 +16,10 @@
|
||||
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||
<vm:MainWindowViewModel />
|
||||
</Design.DataContext>
|
||||
|
||||
<Grid>
|
||||
<u:Form>
|
||||
<u:FormItem Label="_Numeric">
|
||||
<u:NumericIntUpDown/>
|
||||
</u:FormItem>
|
||||
<u:FormItem Label="_AnotherNumeric">
|
||||
<u:NumericIntUpDown/>
|
||||
</u:FormItem>
|
||||
<u:FormItem Label="_TextBox">
|
||||
<TextBox/>
|
||||
</u:FormItem>
|
||||
</u:Form>
|
||||
</Grid>
|
||||
|
||||
<u:UrsaView>
|
||||
<Panel>
|
||||
<Button Content="???" Click="Button_OnClick"></Button>
|
||||
<u:OverlayDialogHost HostId="root"></u:OverlayDialogHost>
|
||||
</Panel>
|
||||
</u:UrsaView>
|
||||
</Window>
|
||||
@@ -1,4 +1,6 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Ursa.Controls;
|
||||
|
||||
namespace Sandbox.Views;
|
||||
|
||||
@@ -8,4 +10,9 @@ public partial class MainWindow : Window
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private async void Button_OnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
var res = await OverlayDialog.ShowModal(new TextBlock() { Text = "sdfksjdl" }, "root");
|
||||
}
|
||||
}
|
||||
15
demo/Sandbox/Views/PW.axaml
Normal file
15
demo/Sandbox/Views/PW.axaml
Normal file
@@ -0,0 +1,15 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:u="https://irihi.tech/ursa"
|
||||
MinHeight="200"
|
||||
MinWidth="500"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Sandbox.Views.PW">
|
||||
<Panel>
|
||||
<Button Content="???" Click="Button_OnClick"></Button>
|
||||
<Button Content="close" VerticalAlignment="Bottom" Click="Close"></Button>
|
||||
<u:OverlayDialogHost x:Name="_overlayDialogHost" ></u:OverlayDialogHost>
|
||||
</Panel>
|
||||
</UserControl>
|
||||
42
demo/Sandbox/Views/PW.axaml.cs
Normal file
42
demo/Sandbox/Views/PW.axaml.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Irihi.Avalonia.Shared.Contracts;
|
||||
using Sandbox.ViewModels;
|
||||
using Ursa.Controls;
|
||||
|
||||
namespace Sandbox.Views;
|
||||
|
||||
public partial class PW : UserControl
|
||||
{
|
||||
public PW()
|
||||
{
|
||||
InitializeComponent();
|
||||
_overlayDialogHost.HostId = _hostid;
|
||||
}
|
||||
|
||||
private string _hostid = Path.GetRandomFileName();
|
||||
|
||||
private async void Button_OnClick(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
Drawer.ShowCustom(new PW(), new TestVM(), _hostid);
|
||||
}
|
||||
|
||||
private void Close(object? sender, RoutedEventArgs e)
|
||||
{
|
||||
(DataContext as TestVM)?.Close();
|
||||
}
|
||||
}
|
||||
|
||||
public class TestVM : ViewModelBase, IDialogContext
|
||||
{
|
||||
public void Close()
|
||||
{
|
||||
RequestClose?.Invoke(this, 12456789);
|
||||
}
|
||||
|
||||
public event EventHandler<object?>? RequestClose;
|
||||
}
|
||||
@@ -50,11 +50,6 @@ public abstract class DrawerControlBase: OverlayFeedbackElement
|
||||
|
||||
protected internal bool CanLightDismiss { get; set; }
|
||||
|
||||
static DrawerControlBase()
|
||||
{
|
||||
DataContextProperty.Changed.AddClassHandler<DrawerControlBase, object?>((o, e) => o.OnDataContextChange(e));
|
||||
}
|
||||
|
||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||
{
|
||||
base.OnApplyTemplate(e);
|
||||
@@ -63,23 +58,6 @@ public abstract class DrawerControlBase: OverlayFeedbackElement
|
||||
Button.ClickEvent.AddHandler(OnCloseButtonClick, _closeButton);
|
||||
}
|
||||
|
||||
private void OnDataContextChange(AvaloniaPropertyChangedEventArgs<object?> args)
|
||||
{
|
||||
if(args.OldValue.Value is IDialogContext oldContext)
|
||||
{
|
||||
oldContext.RequestClose -= OnContextRequestClose;
|
||||
}
|
||||
if(args.NewValue.Value is IDialogContext newContext)
|
||||
{
|
||||
newContext.RequestClose += OnContextRequestClose;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnContextRequestClose(object? sender, object? e)
|
||||
{
|
||||
RaiseEvent(new ResultEventArgs(ClosedEvent, e));
|
||||
}
|
||||
|
||||
private void OnCloseButtonClick(object? sender, RoutedEventArgs e) => Close();
|
||||
|
||||
public override void Close()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.VisualTree;
|
||||
using Irihi.Avalonia.Shared.Contracts;
|
||||
using Irihi.Avalonia.Shared.Helpers;
|
||||
using Irihi.Avalonia.Shared.Shapes;
|
||||
using Ursa.Controls.OverlayShared;
|
||||
@@ -63,6 +65,8 @@ public partial class OverlayDialogHost
|
||||
private async void OnDialogControlClosing(object? sender, object? e)
|
||||
{
|
||||
if (sender is not DialogControlBase control) return;
|
||||
if (control.IsShowAsync is false && e is RoutedEventArgs args)
|
||||
args.Handled = true;
|
||||
var layer = _layers.FirstOrDefault(a => a.Element == control);
|
||||
if (layer is null) return;
|
||||
_layers.Remove(layer);
|
||||
@@ -121,6 +125,7 @@ public partial class OverlayDialogHost
|
||||
{
|
||||
element = control.GetVisualDescendants().OfType<InputElement>().FirstOrDefault(a => a.Focusable);
|
||||
}
|
||||
|
||||
element?.Focus();
|
||||
_modalCount++;
|
||||
IsInModalStatus = _modalCount > 0;
|
||||
|
||||
@@ -5,6 +5,7 @@ using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Styling;
|
||||
using Avalonia.VisualTree;
|
||||
using Irihi.Avalonia.Shared.Contracts;
|
||||
using Irihi.Avalonia.Shared.Shapes;
|
||||
using Ursa.Common;
|
||||
using Ursa.Controls.OverlayShared;
|
||||
@@ -22,6 +23,7 @@ public partial class OverlayDialogHost
|
||||
{
|
||||
mask = CreateOverlayMask(false, true);
|
||||
}
|
||||
|
||||
_layers.Add(new DialogPair(mask, control));
|
||||
ResetZIndices();
|
||||
if (mask is not null) this.Children.Add(mask);
|
||||
@@ -77,6 +79,7 @@ public partial class OverlayDialogHost
|
||||
{
|
||||
element = control.GetVisualDescendants().OfType<InputElement>().FirstOrDefault(a => a.Focusable);
|
||||
}
|
||||
|
||||
element?.Focus();
|
||||
}
|
||||
|
||||
@@ -86,6 +89,7 @@ public partial class OverlayDialogHost
|
||||
{
|
||||
control.Height = this.Bounds.Height;
|
||||
}
|
||||
|
||||
if (control.Position is Position.Top or Position.Bottom)
|
||||
{
|
||||
control.Width = this.Bounds.Width;
|
||||
@@ -145,7 +149,9 @@ public partial class OverlayDialogHost
|
||||
target = appear ? Bounds.Height - elementBounds.Height : Bounds.Height;
|
||||
}
|
||||
|
||||
var targetProperty = position==Position.Left || position==Position.Right ? Canvas.LeftProperty : Canvas.TopProperty;
|
||||
var targetProperty = position == Position.Left || position == Position.Right
|
||||
? Canvas.LeftProperty
|
||||
: Canvas.TopProperty;
|
||||
var animation = new Animation
|
||||
{
|
||||
Easing = new CubicEaseOut(),
|
||||
@@ -167,6 +173,9 @@ public partial class OverlayDialogHost
|
||||
{
|
||||
if (sender is DrawerControlBase control)
|
||||
{
|
||||
if (control.IsShowAsync is false)
|
||||
e.Handled = true;
|
||||
|
||||
var layer = _layers.FirstOrDefault(a => a.Element == control);
|
||||
if (layer is null) return;
|
||||
_layers.Remove(layer);
|
||||
@@ -181,8 +190,10 @@ public partial class OverlayDialogHost
|
||||
if (!IsAnimationDisabled)
|
||||
{
|
||||
var disappearAnimation = CreateAnimation(control.Bounds.Size, control.Position, false);
|
||||
await Task.WhenAll(disappearAnimation.RunAsync(control), MaskDisappearAnimation.RunAsync(layer.Mask));
|
||||
await Task.WhenAll(disappearAnimation.RunAsync(control),
|
||||
MaskDisappearAnimation.RunAsync(layer.Mask));
|
||||
}
|
||||
|
||||
Children.Remove(layer.Mask);
|
||||
}
|
||||
else
|
||||
@@ -193,6 +204,7 @@ public partial class OverlayDialogHost
|
||||
await disappearAnimation.RunAsync(control);
|
||||
}
|
||||
}
|
||||
|
||||
Children.Remove(control);
|
||||
ResetZIndices();
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.LogicalTree;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.VisualTree;
|
||||
using Irihi.Avalonia.Shared.Contracts;
|
||||
@@ -35,19 +36,13 @@ public abstract class OverlayFeedbackElement : ContentControl
|
||||
ClosedEvent.AddClassHandler<OverlayFeedbackElement>((o, e) => o.OnClosed(e));
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromVisualTree(e);
|
||||
Content = null;
|
||||
}
|
||||
|
||||
public bool IsClosed
|
||||
{
|
||||
get => GetValue(IsClosedProperty);
|
||||
set => SetValue(IsClosedProperty, value);
|
||||
}
|
||||
|
||||
private void OnClosed(ResultEventArgs _)
|
||||
private void OnClosed(ResultEventArgs args)
|
||||
{
|
||||
SetCurrentValue(IsClosedProperty, true);
|
||||
}
|
||||
@@ -74,13 +69,25 @@ public abstract class OverlayFeedbackElement : ContentControl
|
||||
RaiseEvent(new ResultEventArgs(ClosedEvent, args));
|
||||
}
|
||||
|
||||
protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e)
|
||||
{
|
||||
base.OnDetachedFromLogicalTree(e);
|
||||
Content = null;
|
||||
}
|
||||
|
||||
internal bool IsShowAsync { get; set; }
|
||||
|
||||
public Task<T?> ShowAsync<T>(CancellationToken? token = default)
|
||||
{
|
||||
IsShowAsync = true;
|
||||
var tcs = new TaskCompletionSource<T?>();
|
||||
token?.Register(() => { Dispatcher.UIThread.Invoke(Close); });
|
||||
|
||||
void OnCloseHandler(object? sender, ResultEventArgs? args)
|
||||
{
|
||||
IsShowAsync = false;
|
||||
if (args is not null)
|
||||
args.Handled = true;
|
||||
if (args?.Result is T result)
|
||||
tcs.SetResult(result);
|
||||
else
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Headless.XUnit;
|
||||
using Avalonia.Threading;
|
||||
using Avalonia.VisualTree;
|
||||
using Ursa.Controls;
|
||||
|
||||
namespace HeadlessTest.Ursa.Controls;
|
||||
|
||||
public class DrawerCloseEventTest
|
||||
{
|
||||
[AvaloniaFact]
|
||||
public async void Test()
|
||||
{
|
||||
UrsaWindow testWindow = new()
|
||||
{
|
||||
Content = new OverlayDialogHost() { HostId = "root" }
|
||||
};
|
||||
testWindow.Show();
|
||||
DrawerCloseTestPopupControl level1 = new();
|
||||
OverlayDialog.ShowCustomModal<object>(level1, new DrawerCloseTestPopupControlVM(), "root");
|
||||
level1.OpenPopup();
|
||||
var level2 = level1.Popup;
|
||||
level2.OpenPopup();
|
||||
var level3 = level2.Popup;
|
||||
level2.ClosePopup();
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
Assert.True(level1.IsAttachedToVisualTree()
|
||||
&& level2.IsAttachedToVisualTree()
|
||||
&& level3.IsAttachedToVisualTree() is false);
|
||||
|
||||
level1.ClosePopup();
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
Assert.True(level1.IsAttachedToVisualTree()
|
||||
&& level2.IsAttachedToVisualTree() is false
|
||||
&& level3.IsAttachedToVisualTree() is false);
|
||||
|
||||
level1.Close();
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
Assert.False(level1.IsAttachedToVisualTree()
|
||||
&& level2.IsAttachedToVisualTree()
|
||||
&& level3.IsAttachedToVisualTree());
|
||||
|
||||
Assert.Equal(level1.LResult, level1.RResult);
|
||||
Assert.Equal(level2.LResult, level2.RResult);
|
||||
Assert.Equal(level3.LResult, level3.RResult);
|
||||
|
||||
OverlayDialog.ShowCustomModal<object>(level1, new DrawerCloseTestPopupControlVM(), "root");
|
||||
level1.OpenPopup();
|
||||
level2 = level1.Popup;
|
||||
level2.OpenPopup();
|
||||
level3 = level2.Popup;
|
||||
level3.OpenPopup();
|
||||
level1.Close();
|
||||
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||
Dispatcher.UIThread.RunJobs();
|
||||
Assert.False(level1.IsAttachedToVisualTree()
|
||||
&& level2.IsAttachedToVisualTree()
|
||||
&& level3.IsAttachedToVisualTree());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user