diff --git a/DialogHost.Avalonia/DialogClosingEventArgs.cs b/DialogHost.Avalonia/DialogClosingEventArgs.cs index 241ee0e..9f6f325 100644 --- a/DialogHost.Avalonia/DialogClosingEventArgs.cs +++ b/DialogHost.Avalonia/DialogClosingEventArgs.cs @@ -6,11 +6,25 @@ namespace DialogHostAvalonia; /// /// Event args contains info about dialog closing /// -public class DialogClosingEventArgs(DialogSession session, RoutedEvent routedEvent) : RoutedEventArgs(routedEvent) { +public class DialogClosingEventArgs(DialogSession session, RoutedEvent routedEvent, bool canBeCancelled) + : RoutedEventArgs(routedEvent) { + /// + [Obsolete("Use constructor with canBeCancelled parameter")] + public DialogClosingEventArgs(DialogSession session, RoutedEvent routedEvent) : this(session, routedEvent, true) { + } + /// /// Cancel the close. /// - public void Cancel() => IsCancelled = true; + public void Cancel() { + if (!CanBeCancelled) { + throw new InvalidOperationException( + $"Cannot cancel dialog closing after {nameof(DialogHost)}.{nameof(DialogHost.IsOpen)} " + + $"property has been set to {bool.FalseString}"); + } + + IsCancelled = true; + } /// /// Indicates if the close has already been cancelled. @@ -26,4 +40,12 @@ public class DialogClosingEventArgs(DialogSession session, RoutedEvent routedEve /// Allows interaction with the current dialog session. /// public DialogSession Session { get; } = session ?? throw new ArgumentNullException(nameof(session)); + + /// + /// Indicates if the close can be canceled. + /// + /// + /// Typically this is true unless closing is triggered by setting to false. + /// + public bool CanBeCancelled { get; } = canBeCancelled; } \ No newline at end of file diff --git a/DialogHost.Avalonia/DialogHost.axaml.cs b/DialogHost.Avalonia/DialogHost.axaml.cs index b278732..ac8d0be 100644 --- a/DialogHost.Avalonia/DialogHost.axaml.cs +++ b/DialogHost.Avalonia/DialogHost.axaml.cs @@ -687,11 +687,13 @@ private void IsOpenPropertyChangedCallback(bool newValue) { OnDialogOpened(dialogOpenedEventArgs); DialogOpenedCallback?.Invoke(this, dialogOpenedEventArgs); CurrentSession?.ShowOpened(this, dialogOpenedEventArgs); - - // dialogHost._overlayPopupHost?.ConfigurePosition(dialogHost._root, PlacementMode.AnchorAndGravity, new Point()); } else { - RemoveAllHost(); + for (var i = _overlayPopupHosts.Count - 1; i >= 0; i--) + { + var session = _overlayPopupHosts[i].Session; + InternalClose(session, session.CloseParameter, false); + } _restoreFocusDialogClose?.Focus(); } @@ -799,25 +801,9 @@ public void RemoveDispose(DialogOverlayPopupHost host) { return host.DialogTaskCompletionSource; } - private void RemoveHost(DialogOverlayPopupHost? host) { - if (host == null) { - return; - } - - var session = host.Session; - if (!session.IsEnded) { - session.Close(session.CloseParameter); - return; - } - - //DialogSession.Close may attempt to cancel the closing of the dialog. - //When the dialog is closed in this manner it is not valid - if (!session.IsEnded) { - throw new InvalidOperationException($"Cannot cancel dialog closing after {nameof(IsOpen)} property has been set to {bool.FalseString}"); - } - + private void RemoveHost(DialogOverlayPopupHost host) { //NB: _dialogTaskCompletionSource is only set in the case where the dialog is shown with Show - host.DialogTaskCompletionSource.TrySetResult(session.CloseParameter); + host.DialogTaskCompletionSource.TrySetResult(host.Session.CloseParameter); host.IsOpen = false; host.Content = null; @@ -830,12 +816,6 @@ private void RemoveHost(DialogOverlayPopupHost? host) { } } - private void RemoveAllHost() { - foreach (var host in _overlayPopupHosts.ToArray().Reverse()) { - RemoveHost(host); - } - } - private void ContentCoverGrid_OnPointerReleased(object sender, PointerReleasedEventArgs e) { if (CloseOnClickAway && CurrentSession != null) { InternalClose(CloseOnClickAwayParameter); @@ -873,10 +853,10 @@ public event EventHandler DialogClosing { internal void InternalClose(object? parameter) { var session = CurrentSession ?? throw new InvalidOperationException($"{nameof(DialogHost)} does not have a current session"); - InternalClose(session, parameter); + InternalClose(session, parameter, true); } - internal void InternalClose(DialogSession session, object? parameter) { + internal void InternalClose(DialogSession session, object? parameter, bool canBeCancelled) { session.CloseParameter = parameter; session.IsEnded = true; @@ -884,7 +864,7 @@ internal void InternalClose(DialogSession session, object? parameter) { // * routed event // * straight forward IsOpen dependency property // * handler provided to the async show method - var dialogClosingEventArgs = new DialogClosingEventArgs(session, DialogClosingEvent); + var dialogClosingEventArgs = new DialogClosingEventArgs(session, DialogClosingEvent, canBeCancelled); OnDialogClosing(dialogClosingEventArgs); DialogClosingCallback?.Invoke(this, dialogClosingEventArgs); session.ShowClosing(this, dialogClosingEventArgs); diff --git a/DialogHost.Avalonia/DialogSession.cs b/DialogHost.Avalonia/DialogSession.cs index bcfa5d7..6b6d822 100644 --- a/DialogHost.Avalonia/DialogSession.cs +++ b/DialogHost.Avalonia/DialogSession.cs @@ -58,7 +58,7 @@ public void Close() { if (IsEnded) throw new InvalidOperationException("Dialog session has ended."); - _owner.InternalClose(this, null); + _owner.InternalClose(this, null, true); } /// @@ -70,7 +70,7 @@ public void Close(object? parameter) { if (IsEnded) throw new InvalidOperationException("Dialog session has ended."); - _owner.InternalClose(this, parameter); + _owner.InternalClose(this, parameter, true); } internal void ShowOpened(object obj, DialogOpenedEventArgs args) {