Merge pull request #542 from WCKYWCKF/pr2
Fixed: Other windows closing due to error in handling the Drawer close event.
This commit is contained in:
@@ -16,75 +16,10 @@
|
|||||||
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
|
||||||
<vm:MainWindowViewModel />
|
<vm:MainWindowViewModel />
|
||||||
</Design.DataContext>
|
</Design.DataContext>
|
||||||
|
<u:UrsaView>
|
||||||
|
<Panel>
|
||||||
<Grid ColumnDefinitions="*,*"
|
<Button Content="???" Click="Button_OnClick"></Button>
|
||||||
RowDefinitions="7*,3*">
|
<u:OverlayDialogHost HostId="root"></u:OverlayDialogHost>
|
||||||
<ScrollViewer Grid.Column="0" Grid.Row="0">
|
</Panel>
|
||||||
<u:Form LabelAlignment="Left" LabelPosition="Left" LabelWidth="*" HorizontalAlignment="Stretch">
|
</u:UrsaView>
|
||||||
<TextBox Name="Title" u:FormItem.Label="Title"></TextBox>
|
|
||||||
<TextBox Name="SuggestedFileName" u:FormItem.Label="SuggestedFileName"></TextBox>
|
|
||||||
<TextBox Name="SuggestedStartPath" u:FormItem.Label="SuggestedStartPath"></TextBox>
|
|
||||||
<TextBox Name="FileFilter" u:FormItem.Label="FileFilter"></TextBox>
|
|
||||||
<TextBox Name="DefaultFileExtension" u:FormItem.Label="DefaultFileExtension"></TextBox>
|
|
||||||
<ToggleButton Name="AllowMultiple" Content="AllowMultiple" u:FormItem.NoLabel="True"
|
|
||||||
HorizontalAlignment="Stretch">
|
|
||||||
</ToggleButton>
|
|
||||||
<u:EnumSelector Name="UsePickerType" EnumType="u:UsePickerTypes" u:FormItem.Label="UsePickerType"></u:EnumSelector>
|
|
||||||
</u:Form>
|
|
||||||
</ScrollViewer>
|
|
||||||
<ScrollViewer Grid.Column="0" Grid.Row="1">
|
|
||||||
<StackPanel Spacing="2">
|
|
||||||
<HeaderedContentControl Header="Default">
|
|
||||||
<u:PathPicker Name="PathPicker"
|
|
||||||
Title="{Binding #Title.Text}"
|
|
||||||
SuggestedFileName="{Binding #SuggestedFileName.Text}"
|
|
||||||
SuggestedStartPath="{Binding #SuggestedStartPath.Text}"
|
|
||||||
FileFilter="{Binding #FileFilter.Text}"
|
|
||||||
DefaultFileExtension="{Binding #DefaultFileExtension.Text}"
|
|
||||||
AllowMultiple="{Binding #AllowMultiple.IsChecked}"
|
|
||||||
UsePickerType="{Binding #UsePickerType.Value}">
|
|
||||||
</u:PathPicker>
|
|
||||||
</HeaderedContentControl>
|
|
||||||
<HeaderedContentControl Header="PathPickerOnlyButton">
|
|
||||||
<u:PathPicker Name="PathPicker1"
|
|
||||||
Theme="{DynamicResource PathPickerOnlyButton}"
|
|
||||||
Title="{Binding #Title.Text}"
|
|
||||||
SuggestedFileName="{Binding #SuggestedFileName.Text}"
|
|
||||||
SuggestedStartPath="{Binding #SuggestedStartPath.Text}"
|
|
||||||
FileFilter="{Binding #FileFilter.Text}"
|
|
||||||
DefaultFileExtension="{Binding #DefaultFileExtension.Text}"
|
|
||||||
AllowMultiple="{Binding #AllowMultiple.IsChecked}"
|
|
||||||
UsePickerType="{Binding #UsePickerType.Value}">
|
|
||||||
</u:PathPicker>
|
|
||||||
</HeaderedContentControl>
|
|
||||||
<HeaderedContentControl Header="PathPickerForList">
|
|
||||||
<u:PathPicker Name="PathPicker2"
|
|
||||||
Theme="{DynamicResource PathPickerForList}"
|
|
||||||
Title="{Binding #Title.Text}"
|
|
||||||
SuggestedFileName="{Binding #SuggestedFileName.Text}"
|
|
||||||
SuggestedStartPath="{Binding #SuggestedStartPath.Text}"
|
|
||||||
FileFilter="{Binding #FileFilter.Text}"
|
|
||||||
DefaultFileExtension="{Binding #DefaultFileExtension.Text}"
|
|
||||||
AllowMultiple="{Binding #AllowMultiple.IsChecked}"
|
|
||||||
UsePickerType="{Binding #UsePickerType.Value}">
|
|
||||||
</u:PathPicker>
|
|
||||||
</HeaderedContentControl>
|
|
||||||
</StackPanel>
|
|
||||||
</ScrollViewer>
|
|
||||||
<ScrollViewer Grid.Column="1" Grid.Row="0" Grid.RowSpan="2">
|
|
||||||
<StackPanel Spacing="1">
|
|
||||||
<HeaderedContentControl Header="SelectedPath">
|
|
||||||
<TextBox Name="SelectedPath" u:FormItem.Label="SelectedPath" IsReadOnly="True"
|
|
||||||
Text="{Binding #PathPicker.SelectedPath}">
|
|
||||||
</TextBox>
|
|
||||||
</HeaderedContentControl>
|
|
||||||
<HeaderedContentControl Header="SelectedPaths">
|
|
||||||
<ListBox Name="SelectedPaths"
|
|
||||||
ItemsSource="{Binding #PathPicker.SelectedPaths}">
|
|
||||||
</ListBox>
|
|
||||||
</HeaderedContentControl>
|
|
||||||
</StackPanel>
|
|
||||||
</ScrollViewer>
|
|
||||||
</Grid>
|
|
||||||
</Window>
|
</Window>
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Ursa.Controls;
|
||||||
|
|
||||||
namespace Sandbox.Views;
|
namespace Sandbox.Views;
|
||||||
|
|
||||||
@@ -8,4 +10,9 @@ public partial class MainWindow : Window
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
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;
|
||||||
|
}
|
||||||
@@ -49,11 +49,6 @@ public abstract class DrawerControlBase: OverlayFeedbackElement
|
|||||||
internal bool? IsCloseButtonVisible { get; set; }
|
internal bool? IsCloseButtonVisible { get; set; }
|
||||||
|
|
||||||
protected internal bool CanLightDismiss { get; set; }
|
protected internal bool CanLightDismiss { get; set; }
|
||||||
|
|
||||||
static DrawerControlBase()
|
|
||||||
{
|
|
||||||
DataContextProperty.Changed.AddClassHandler<DrawerControlBase, object?>((o, e) => o.OnDataContextChange(e));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -63,23 +58,6 @@ public abstract class DrawerControlBase: OverlayFeedbackElement
|
|||||||
Button.ClickEvent.AddHandler(OnCloseButtonClick, _closeButton);
|
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();
|
private void OnCloseButtonClick(object? sender, RoutedEventArgs e) => Close();
|
||||||
|
|
||||||
public override void Close()
|
public override void Close()
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Avalonia;
|
using Avalonia;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
|
using Irihi.Avalonia.Shared.Contracts;
|
||||||
using Irihi.Avalonia.Shared.Helpers;
|
using Irihi.Avalonia.Shared.Helpers;
|
||||||
using Irihi.Avalonia.Shared.Shapes;
|
using Irihi.Avalonia.Shared.Shapes;
|
||||||
using Ursa.Controls.OverlayShared;
|
using Ursa.Controls.OverlayShared;
|
||||||
@@ -63,6 +65,8 @@ public partial class OverlayDialogHost
|
|||||||
private async void OnDialogControlClosing(object? sender, object? e)
|
private async void OnDialogControlClosing(object? sender, object? e)
|
||||||
{
|
{
|
||||||
if (sender is not DialogControlBase control) return;
|
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);
|
var layer = _layers.FirstOrDefault(a => a.Element == control);
|
||||||
if (layer is null) return;
|
if (layer is null) return;
|
||||||
_layers.Remove(layer);
|
_layers.Remove(layer);
|
||||||
@@ -116,11 +120,12 @@ public partial class OverlayDialogHost
|
|||||||
if (!IsAnimationDisabled) MaskAppearAnimation.RunAsync(mask);
|
if (!IsAnimationDisabled) MaskAppearAnimation.RunAsync(mask);
|
||||||
|
|
||||||
var element = control.GetVisualDescendants().OfType<InputElement>()
|
var element = control.GetVisualDescendants().OfType<InputElement>()
|
||||||
.FirstOrDefault(FocusHelper.GetDialogFocusHint);
|
.FirstOrDefault(FocusHelper.GetDialogFocusHint);
|
||||||
if (element is null)
|
if (element is null)
|
||||||
{
|
{
|
||||||
element = control.GetVisualDescendants().OfType<InputElement>().FirstOrDefault(a => a.Focusable);
|
element = control.GetVisualDescendants().OfType<InputElement>().FirstOrDefault(a => a.Focusable);
|
||||||
}
|
}
|
||||||
|
|
||||||
element?.Focus();
|
element?.Focus();
|
||||||
_modalCount++;
|
_modalCount++;
|
||||||
IsInModalStatus = _modalCount > 0;
|
IsInModalStatus = _modalCount > 0;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ using Avalonia.Controls;
|
|||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Styling;
|
using Avalonia.Styling;
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
|
using Irihi.Avalonia.Shared.Contracts;
|
||||||
using Irihi.Avalonia.Shared.Shapes;
|
using Irihi.Avalonia.Shared.Shapes;
|
||||||
using Ursa.Common;
|
using Ursa.Common;
|
||||||
using Ursa.Controls.OverlayShared;
|
using Ursa.Controls.OverlayShared;
|
||||||
@@ -22,9 +23,10 @@ public partial class OverlayDialogHost
|
|||||||
{
|
{
|
||||||
mask = CreateOverlayMask(false, true);
|
mask = CreateOverlayMask(false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
_layers.Add(new DialogPair(mask, control));
|
_layers.Add(new DialogPair(mask, control));
|
||||||
ResetZIndices();
|
ResetZIndices();
|
||||||
if(mask is not null)this.Children.Add(mask);
|
if (mask is not null) this.Children.Add(mask);
|
||||||
this.Children.Add(control);
|
this.Children.Add(control);
|
||||||
control.Measure(this.Bounds.Size);
|
control.Measure(this.Bounds.Size);
|
||||||
control.Arrange(new Rect(control.DesiredSize));
|
control.Arrange(new Rect(control.DesiredSize));
|
||||||
@@ -47,7 +49,7 @@ public partial class OverlayDialogHost
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal async void AddModalDrawer(DrawerControlBase control)
|
internal async void AddModalDrawer(DrawerControlBase control)
|
||||||
{
|
{
|
||||||
PureRectangle mask = CreateOverlayMask(true, control.CanLightDismiss);
|
PureRectangle mask = CreateOverlayMask(true, control.CanLightDismiss);
|
||||||
@@ -72,26 +74,28 @@ public partial class OverlayDialogHost
|
|||||||
}
|
}
|
||||||
|
|
||||||
var element = control.GetVisualDescendants().OfType<InputElement>()
|
var element = control.GetVisualDescendants().OfType<InputElement>()
|
||||||
.FirstOrDefault(FocusHelper.GetDialogFocusHint);
|
.FirstOrDefault(FocusHelper.GetDialogFocusHint);
|
||||||
if (element is null)
|
if (element is null)
|
||||||
{
|
{
|
||||||
element = control.GetVisualDescendants().OfType<InputElement>().FirstOrDefault(a => a.Focusable);
|
element = control.GetVisualDescendants().OfType<InputElement>().FirstOrDefault(a => a.Focusable);
|
||||||
}
|
}
|
||||||
|
|
||||||
element?.Focus();
|
element?.Focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetDrawerPosition(DrawerControlBase control)
|
private void SetDrawerPosition(DrawerControlBase control)
|
||||||
{
|
{
|
||||||
if(control.Position is Position.Left or Position.Right)
|
if (control.Position is Position.Left or Position.Right)
|
||||||
{
|
{
|
||||||
control.Height = this.Bounds.Height;
|
control.Height = this.Bounds.Height;
|
||||||
}
|
}
|
||||||
if(control.Position is Position.Top or Position.Bottom)
|
|
||||||
|
if (control.Position is Position.Top or Position.Bottom)
|
||||||
{
|
{
|
||||||
control.Width = this.Bounds.Width;
|
control.Width = this.Bounds.Width;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ResetDrawerPosition(DrawerControlBase control, Size newSize)
|
private static void ResetDrawerPosition(DrawerControlBase control, Size newSize)
|
||||||
{
|
{
|
||||||
if (control.Position == Position.Right)
|
if (control.Position == Position.Right)
|
||||||
@@ -112,7 +116,7 @@ public partial class OverlayDialogHost
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
control.Width = newSize.Width;
|
control.Width = newSize.Width;
|
||||||
SetTop(control, newSize.Height-control.Bounds.Height);
|
SetTop(control, newSize.Height - control.Bounds.Height);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,26 +136,28 @@ public partial class OverlayDialogHost
|
|||||||
source = appear ? Bounds.Width : Bounds.Width - elementBounds.Width;
|
source = appear ? Bounds.Width : Bounds.Width - elementBounds.Width;
|
||||||
target = appear ? Bounds.Width - elementBounds.Width : Bounds.Width;
|
target = appear ? Bounds.Width - elementBounds.Width : Bounds.Width;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (position == Position.Top)
|
if (position == Position.Top)
|
||||||
{
|
{
|
||||||
source = appear ? -elementBounds.Height : 0;
|
source = appear ? -elementBounds.Height : 0;
|
||||||
target = appear ? 0 : -elementBounds.Height;
|
target = appear ? 0 : -elementBounds.Height;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (position == Position.Bottom)
|
if (position == Position.Bottom)
|
||||||
{
|
{
|
||||||
source = appear ? Bounds.Height : Bounds.Height - elementBounds.Height;
|
source = appear ? Bounds.Height : Bounds.Height - elementBounds.Height;
|
||||||
target = appear ? Bounds.Height - elementBounds.Height : Bounds.Height;
|
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
|
var animation = new Animation
|
||||||
{
|
{
|
||||||
Easing = new CubicEaseOut(),
|
Easing = new CubicEaseOut(),
|
||||||
FillMode = FillMode.Forward
|
FillMode = FillMode.Forward
|
||||||
};
|
};
|
||||||
var keyFrame1 = new KeyFrame(){ Cue = new Cue(0.0) };
|
var keyFrame1 = new KeyFrame() { Cue = new Cue(0.0) };
|
||||||
keyFrame1.Setters.Add(new Setter()
|
keyFrame1.Setters.Add(new Setter()
|
||||||
{ Property = targetProperty, Value = source });
|
{ Property = targetProperty, Value = source });
|
||||||
var keyFrame2 = new KeyFrame() { Cue = new Cue(1.0) };
|
var keyFrame2 = new KeyFrame() { Cue = new Cue(1.0) };
|
||||||
@@ -162,16 +168,19 @@ public partial class OverlayDialogHost
|
|||||||
animation.Duration = TimeSpan.FromSeconds(0.3);
|
animation.Duration = TimeSpan.FromSeconds(0.3);
|
||||||
return animation;
|
return animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void OnDrawerControlClosing(object? sender, ResultEventArgs e)
|
private async void OnDrawerControlClosing(object? sender, ResultEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is DrawerControlBase control)
|
if (sender is DrawerControlBase control)
|
||||||
{
|
{
|
||||||
|
if (control.IsShowAsync is false)
|
||||||
|
e.Handled = true;
|
||||||
|
|
||||||
var layer = _layers.FirstOrDefault(a => a.Element == control);
|
var layer = _layers.FirstOrDefault(a => a.Element == control);
|
||||||
if(layer is null) return;
|
if (layer is null) return;
|
||||||
_layers.Remove(layer);
|
_layers.Remove(layer);
|
||||||
control.RemoveHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
|
control.RemoveHandler(OverlayFeedbackElement.ClosedEvent, OnDialogControlClosing);
|
||||||
control.RemoveHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
|
control.RemoveHandler(DialogControlBase.LayerChangedEvent, OnDialogLayerChanged);
|
||||||
if (layer.Mask is not null)
|
if (layer.Mask is not null)
|
||||||
{
|
{
|
||||||
_modalCount--;
|
_modalCount--;
|
||||||
@@ -181,8 +190,10 @@ public partial class OverlayDialogHost
|
|||||||
if (!IsAnimationDisabled)
|
if (!IsAnimationDisabled)
|
||||||
{
|
{
|
||||||
var disappearAnimation = CreateAnimation(control.Bounds.Size, control.Position, false);
|
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);
|
Children.Remove(layer.Mask);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -193,6 +204,7 @@ public partial class OverlayDialogHost
|
|||||||
await disappearAnimation.RunAsync(control);
|
await disappearAnimation.RunAsync(control);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Children.Remove(control);
|
Children.Remove(control);
|
||||||
ResetZIndices();
|
ResetZIndices();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Avalonia;
|
|||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.LogicalTree;
|
||||||
using Avalonia.Threading;
|
using Avalonia.Threading;
|
||||||
using Avalonia.VisualTree;
|
using Avalonia.VisualTree;
|
||||||
using Irihi.Avalonia.Shared.Contracts;
|
using Irihi.Avalonia.Shared.Contracts;
|
||||||
@@ -35,19 +36,13 @@ public abstract class OverlayFeedbackElement : ContentControl
|
|||||||
ClosedEvent.AddClassHandler<OverlayFeedbackElement>((o, e) => o.OnClosed(e));
|
ClosedEvent.AddClassHandler<OverlayFeedbackElement>((o, e) => o.OnClosed(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnDetachedFromVisualTree(e);
|
|
||||||
Content = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsClosed
|
public bool IsClosed
|
||||||
{
|
{
|
||||||
get => GetValue(IsClosedProperty);
|
get => GetValue(IsClosedProperty);
|
||||||
set => SetValue(IsClosedProperty, value);
|
set => SetValue(IsClosedProperty, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClosed(ResultEventArgs _)
|
private void OnClosed(ResultEventArgs args)
|
||||||
{
|
{
|
||||||
SetCurrentValue(IsClosedProperty, true);
|
SetCurrentValue(IsClosedProperty, true);
|
||||||
}
|
}
|
||||||
@@ -74,13 +69,25 @@ public abstract class OverlayFeedbackElement : ContentControl
|
|||||||
RaiseEvent(new ResultEventArgs(ClosedEvent, args));
|
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)
|
public Task<T?> ShowAsync<T>(CancellationToken? token = default)
|
||||||
{
|
{
|
||||||
|
IsShowAsync = true;
|
||||||
var tcs = new TaskCompletionSource<T?>();
|
var tcs = new TaskCompletionSource<T?>();
|
||||||
token?.Register(() => { Dispatcher.UIThread.Invoke(Close); });
|
token?.Register(() => { Dispatcher.UIThread.Invoke(Close); });
|
||||||
|
|
||||||
void OnCloseHandler(object? sender, ResultEventArgs? args)
|
void OnCloseHandler(object? sender, ResultEventArgs? args)
|
||||||
{
|
{
|
||||||
|
IsShowAsync = false;
|
||||||
|
if (args is not null)
|
||||||
|
args.Handled = true;
|
||||||
if (args?.Result is T result)
|
if (args?.Result is T result)
|
||||||
tcs.SetResult(result);
|
tcs.SetResult(result);
|
||||||
else
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<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="HeadlessTest.Ursa.Controls.DrawerCloseTestPopupControl">
|
||||||
|
<u:OverlayDialogHost x:Name="_overlayDialogHost" ></u:OverlayDialogHost>
|
||||||
|
</UserControl>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
using System;
|
||||||
|
using System.ComponentModel;
|
||||||
|
using System.IO;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Avalonia;
|
||||||
|
using Avalonia.Controls;
|
||||||
|
using Avalonia.Interactivity;
|
||||||
|
using Avalonia.Markup.Xaml;
|
||||||
|
using Ursa.Controls;
|
||||||
|
|
||||||
|
namespace HeadlessTest.Ursa.Controls;
|
||||||
|
|
||||||
|
public partial class DrawerCloseTestPopupControl : UserControl
|
||||||
|
{
|
||||||
|
public DrawerCloseTestPopupControl()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_overlayDialogHost.HostId = _hostid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly string _hostid = Path.GetRandomFileName();
|
||||||
|
public DrawerCloseTestPopupControl Popup { get; set; }
|
||||||
|
public int LResult { get; set; }
|
||||||
|
public int RResult { get; set; }
|
||||||
|
|
||||||
|
public async void OpenPopup()
|
||||||
|
{
|
||||||
|
var vm = new DrawerCloseTestPopupControlVM();
|
||||||
|
LResult = vm.Result;
|
||||||
|
RResult = await Drawer.ShowCustomModal<int>(Popup = new(), vm, _hostid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClosePopup()
|
||||||
|
{
|
||||||
|
(Popup.DataContext as DrawerCloseTestPopupControlVM)?.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
(DataContext as DrawerCloseTestPopupControlVM)?.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using Irihi.Avalonia.Shared.Contracts;
|
||||||
|
|
||||||
|
namespace HeadlessTest.Ursa.Controls;
|
||||||
|
|
||||||
|
public class DrawerCloseTestPopupControlVM : IDialogContext
|
||||||
|
{
|
||||||
|
public void Close()
|
||||||
|
{
|
||||||
|
RequestClose?.Invoke(this, Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int Result { get; } = Random.Shared.Next();
|
||||||
|
|
||||||
|
public event EventHandler<object?>? RequestClose;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user