using System.Windows.Controls; using WPFluent.Input; // ReSharper disable once CheckNamespace namespace WPFluent.Controls; /// /// Dialogue displayed inside the application covering its internals, displaying some content. /// /// /// /// <ContentPresenter x:Name="RootContentDialogPresenter" Grid.Row="0" /> /// /// /// var contentDialog = new ContentDialog(RootContentDialogPresenter); /// /// contentDialog.SetCurrentValue(ContentDialog.TitleProperty, "Hello World"); /// contentDialog.SetCurrentValue(ContentControl.ContentProperty, "This is a message"); /// contentDialog.SetCurrentValue(ContentDialog.CloseButtonTextProperty, "Close this dialog"); /// /// await contentDialog.ShowAsync(cancellationToken); /// /// /// var contentDialogService = new ContentDialogService(); /// contentDialogService.SetContentPresenter(RootContentDialogPresenter); /// /// await _contentDialogService.ShowSimpleDialogAsync( /// new SimpleContentDialogCreateOptions() /// { /// Title = "The cake?", /// Content = "IS A LIE!", /// PrimaryButtonText = "Save", /// SecondaryButtonText = "Don't Save", /// CloseButtonText = "Cancel" /// } /// ); /// /// public class ContentDialog : ContentControl { /// Identifies the dependency property. public static readonly DependencyProperty TitleProperty = DependencyProperty.Register( nameof(Title), typeof(object), typeof(ContentDialog), new PropertyMetadata(null) ); /// Identifies the dependency property. public static readonly DependencyProperty TitleTemplateProperty = DependencyProperty.Register( nameof(TitleTemplate), typeof(DataTemplate), typeof(ContentDialog), new PropertyMetadata(null) ); /// Identifies the dependency property. public static readonly DependencyProperty DialogWidthProperty = DependencyProperty.Register( nameof(DialogWidth), typeof(double), typeof(ContentDialog), new PropertyMetadata(double.PositiveInfinity) ); /// Identifies the dependency property. public static readonly DependencyProperty DialogHeightProperty = DependencyProperty.Register( nameof(DialogHeight), typeof(double), typeof(ContentDialog), new PropertyMetadata(double.PositiveInfinity) ); /// Identifies the dependency property. public static readonly DependencyProperty DialogMaxWidthProperty = DependencyProperty.Register( nameof(DialogMaxWidth), typeof(double), typeof(ContentDialog), new PropertyMetadata(double.PositiveInfinity) ); /// Identifies the dependency property. public static readonly DependencyProperty DialogMaxHeightProperty = DependencyProperty.Register( nameof(DialogMaxHeight), typeof(double), typeof(ContentDialog), new PropertyMetadata(double.PositiveInfinity) ); /// Identifies the dependency property. public static readonly DependencyProperty DialogMarginProperty = DependencyProperty.Register( nameof(DialogMargin), typeof(Thickness), typeof(ContentDialog) ); /// Identifies the dependency property. public static readonly DependencyProperty PrimaryButtonTextProperty = DependencyProperty.Register( nameof(PrimaryButtonText), typeof(string), typeof(ContentDialog), new PropertyMetadata(string.Empty) ); /// Identifies the dependency property. public static readonly DependencyProperty SecondaryButtonTextProperty = DependencyProperty.Register( nameof(SecondaryButtonText), typeof(string), typeof(ContentDialog), new PropertyMetadata(string.Empty) ); /// Identifies the dependency property. public static readonly DependencyProperty CloseButtonTextProperty = DependencyProperty.Register( nameof(CloseButtonText), typeof(string), typeof(ContentDialog), new PropertyMetadata("Close") ); /// Identifies the dependency property. public static readonly DependencyProperty PrimaryButtonIconProperty = DependencyProperty.Register( nameof(PrimaryButtonIcon), typeof(IconElement), typeof(ContentDialog), new PropertyMetadata(null) ); /// Identifies the dependency property. public static readonly DependencyProperty SecondaryButtonIconProperty = DependencyProperty.Register( nameof(SecondaryButtonIcon), typeof(IconElement), typeof(ContentDialog), new PropertyMetadata(null) ); /// Identifies the dependency property. public static readonly DependencyProperty CloseButtonIconProperty = DependencyProperty.Register( nameof(CloseButtonIcon), typeof(IconElement), typeof(ContentDialog), new PropertyMetadata(null) ); /// Identifies the dependency property. public static readonly DependencyProperty IsPrimaryButtonEnabledProperty = DependencyProperty.Register( nameof(IsPrimaryButtonEnabled), typeof(bool), typeof(ContentDialog), new PropertyMetadata(true) ); /// Identifies the dependency property. public static readonly DependencyProperty IsPrimaryButtonAvailableProperty = DependencyProperty.Register( nameof(IsPrimaryButtonAvailable), typeof(bool), typeof(ContentDialog), new PropertyMetadata(true) ); /// Identifies the dependency property. public static readonly DependencyProperty IsSecondaryButtonEnabledProperty = DependencyProperty.Register( nameof(IsSecondaryButtonEnabled), typeof(bool), typeof(ContentDialog), new PropertyMetadata(true) ); /// Identifies the dependency property. public static readonly DependencyProperty PrimaryButtonAppearanceProperty = DependencyProperty.Register( nameof(PrimaryButtonAppearance), typeof(ControlAppearance), typeof(ContentDialog), new PropertyMetadata(ControlAppearance.Primary) ); /// Identifies the dependency property. public static readonly DependencyProperty SecondaryButtonAppearanceProperty = DependencyProperty.Register( nameof(SecondaryButtonAppearance), typeof(ControlAppearance), typeof(ContentDialog), new PropertyMetadata(ControlAppearance.Secondary) ); /// Identifies the dependency property. public static readonly DependencyProperty CloseButtonAppearanceProperty = DependencyProperty.Register( nameof(CloseButtonAppearance), typeof(ControlAppearance), typeof(ContentDialog), new PropertyMetadata(ControlAppearance.Secondary) ); /// Identifies the dependency property. public static readonly DependencyProperty DefaultButtonProperty = DependencyProperty.Register( nameof(DefaultButton), typeof(ContentDialogButton), typeof(ContentDialog), new PropertyMetadata(ContentDialogButton.Primary) ); /// Identifies the dependency property. public static readonly DependencyProperty IsFooterVisibleProperty = DependencyProperty.Register( nameof(IsFooterVisible), typeof(bool), typeof(ContentDialog), new PropertyMetadata(true) ); /// Identifies the dependency property. public static readonly DependencyProperty TemplateButtonCommandProperty = DependencyProperty.Register( nameof(TemplateButtonCommand), typeof(IRelayCommand), typeof(ContentDialog), new PropertyMetadata(null) ); /// Identifies the routed event. public static readonly RoutedEvent OpenedEvent = EventManager.RegisterRoutedEvent( nameof(Opened), RoutingStrategy.Bubble, typeof(TypedEventHandler), typeof(ContentDialog) ); /// Identifies the routed event. public static readonly RoutedEvent ClosingEvent = EventManager.RegisterRoutedEvent( nameof(Closing), RoutingStrategy.Bubble, typeof(TypedEventHandler), typeof(ContentDialog) ); /// Identifies the routed event. public static readonly RoutedEvent ClosedEvent = EventManager.RegisterRoutedEvent( nameof(Closed), RoutingStrategy.Bubble, typeof(TypedEventHandler), typeof(ContentDialog) ); /// Identifies the routed event. public static readonly RoutedEvent ButtonClickedEvent = EventManager.RegisterRoutedEvent( nameof(ButtonClicked), RoutingStrategy.Bubble, typeof(TypedEventHandler), typeof(ContentDialog) ); /// /// Gets or sets the title of the . /// public object? Title { get => GetValue(TitleProperty); set => SetValue(TitleProperty, value); } /// /// Gets or sets the title template of the . /// public DataTemplate? TitleTemplate { get => (DataTemplate?)GetValue(TitleTemplateProperty); set => SetValue(TitleTemplateProperty, value); } /// /// Gets or sets the width of the . /// public double DialogWidth { get => (double)GetValue(DialogWidthProperty); set => SetValue(DialogWidthProperty, value); } /// /// Gets or sets the height of the . /// public double DialogHeight { get => (double)GetValue(DialogHeightProperty); set => SetValue(DialogHeightProperty, value); } /// /// Gets or sets the max width of the . /// public double DialogMaxWidth { get => (double)GetValue(DialogMaxWidthProperty); set => SetValue(DialogMaxWidthProperty, value); } /// /// Gets or sets the max height of the . /// public double DialogMaxHeight { get => (double)GetValue(DialogMaxHeightProperty); set => SetValue(DialogMaxHeightProperty, value); } /// /// Gets or sets the margin of the . /// public Thickness DialogMargin { get => (Thickness)GetValue(DialogMarginProperty); set => SetValue(DialogMarginProperty, value); } /// /// Gets or sets the text to display on the primary button. /// public string PrimaryButtonText { get => (string)GetValue(PrimaryButtonTextProperty); set => SetValue(PrimaryButtonTextProperty, value); } /// /// Gets or sets the text to be displayed on the secondary button. /// public string SecondaryButtonText { get => (string)GetValue(SecondaryButtonTextProperty); set => SetValue(SecondaryButtonTextProperty, value); } /// /// Gets or sets the text to display on the close button. /// public string CloseButtonText { get => (string)GetValue(CloseButtonTextProperty); set => SetValue(CloseButtonTextProperty, value); } /// /// Gets or sets the on the secondary button. /// public IconElement? PrimaryButtonIcon { get => (IconElement?)GetValue(PrimaryButtonIconProperty); set => SetValue(PrimaryButtonIconProperty, value); } /// /// Gets or sets the on the primary button. /// public IconElement? SecondaryButtonIcon { get => (IconElement?)GetValue(SecondaryButtonIconProperty); set => SetValue(SecondaryButtonIconProperty, value); } /// /// Gets or sets the on the close button. /// public IconElement? CloseButtonIcon { get => (IconElement?)GetValue(CloseButtonIconProperty); set => SetValue(CloseButtonIconProperty, value); } /// /// Gets or sets a value indicating whether the primary button is enabled. /// public bool IsPrimaryButtonEnabled { get => (bool)GetValue(IsPrimaryButtonEnabledProperty); set => SetValue(IsPrimaryButtonEnabledProperty, value); } /// /// Gets or sets a value indicating whether the primary button is available. /// public bool IsPrimaryButtonAvailable { get => (bool)GetValue(IsPrimaryButtonAvailableProperty); set => SetValue(IsPrimaryButtonAvailableProperty, value); } /// /// Gets or sets a value indicating whether the secondary button is enabled. /// public bool IsSecondaryButtonEnabled { get => (bool)GetValue(IsSecondaryButtonEnabledProperty); set => SetValue(IsSecondaryButtonEnabledProperty, value); } /// /// Gets or sets the to apply to the primary button. /// public ControlAppearance PrimaryButtonAppearance { get => (ControlAppearance)GetValue(PrimaryButtonAppearanceProperty); set => SetValue(PrimaryButtonAppearanceProperty, value); } /// /// Gets or sets the to apply to the secondary button. /// public ControlAppearance SecondaryButtonAppearance { get => (ControlAppearance)GetValue(SecondaryButtonAppearanceProperty); set => SetValue(SecondaryButtonAppearanceProperty, value); } /// /// Gets or sets the to apply to the close button. /// public ControlAppearance CloseButtonAppearance { get => (ControlAppearance)GetValue(CloseButtonAppearanceProperty); set => SetValue(CloseButtonAppearanceProperty, value); } /// /// Gets or sets a value that indicates which button on the dialog is the default action. /// public ContentDialogButton DefaultButton { get => (ContentDialogButton)GetValue(DefaultButtonProperty); set => SetValue(DefaultButtonProperty, value); } /// /// Gets or sets a value indicating whether the footer buttons are visible. /// public bool IsFooterVisible { get => (bool)GetValue(IsFooterVisibleProperty); set => SetValue(IsFooterVisibleProperty, value); } /// /// Gets command triggered after clicking the button in the template. /// internal IRelayCommand TemplateButtonCommand => (IRelayCommand)GetValue(TemplateButtonCommandProperty); /// /// Occurs after the dialog is opened. /// public event TypedEventHandler Opened { add => AddHandler(OpenedEvent, value); remove => RemoveHandler(OpenedEvent, value); } /// /// Occurs after the dialog starts to close, but before it is closed and before the event occurs. /// public event TypedEventHandler Closing { add => AddHandler(ClosingEvent, value); remove => RemoveHandler(ClosingEvent, value); } /// /// Occurs after the dialog is closed. /// public event TypedEventHandler Closed { add => AddHandler(ClosedEvent, value); remove => RemoveHandler(ClosedEvent, value); } /// /// Occurs after the has been tapped. /// public event TypedEventHandler ButtonClicked { add => AddHandler(ButtonClickedEvent, value); remove => RemoveHandler(ButtonClickedEvent, value); } /// /// Initializes a new instance of the class. /// public ContentDialog() { SetValue(TemplateButtonCommandProperty, new RelayCommand(OnButtonClick)); Loaded += static (sender, _) => { var self = (ContentDialog)sender; self.OnLoaded(); }; } /// /// Initializes a new instance of the class. /// /// inside of which the dialogue will be placed. The new will replace the current . public ContentDialog(ContentPresenter? dialogHost) { if (dialogHost is null) { throw new ArgumentNullException(nameof(dialogHost)); } DialogHost = dialogHost; SetValue(TemplateButtonCommandProperty, new RelayCommand(OnButtonClick)); Loaded += static (sender, _) => { var self = (ContentDialog)sender; self.OnLoaded(); }; } /// /// Gets or sets inside of which the dialogue will be placed. The new will replace the current . /// public ContentPresenter? DialogHost { get; set; } = default; protected TaskCompletionSource? Tcs { get; set; } /// /// Shows the dialog /// [System.Diagnostics.CodeAnalysis.SuppressMessage( "WpfAnalyzers.DependencyProperty", "WPF0041:Set mutable dependency properties using SetCurrentValue", Justification = "SetCurrentValue(ContentProperty, ...) will not work" )] public async Task ShowAsync(CancellationToken cancellationToken = default) { if (DialogHost is null) { throw new InvalidOperationException("DialogHost was not set"); } Tcs = new TaskCompletionSource(); CancellationTokenRegistration tokenRegistration = cancellationToken.Register( o => Tcs.TrySetCanceled((CancellationToken)o!), cancellationToken ); ContentDialogResult result = ContentDialogResult.None; try { DialogHost.Content = this; result = await Tcs.Task; return result; } finally { #if NET6_0_OR_GREATER await tokenRegistration.DisposeAsync(); #else tokenRegistration.Dispose(); #endif DialogHost.Content = null; OnClosed(result); } } /// /// Hides the dialog with result /// public virtual void Hide(ContentDialogResult result = ContentDialogResult.None) { var closingEventArgs = new ContentDialogClosingEventArgs(ClosingEvent, this) { Result = result }; RaiseEvent(closingEventArgs); if (!closingEventArgs.Cancel) { Tcs?.TrySetResult(result); } } /// /// Occurs after ContentPresenter.Content = null /// protected virtual void OnClosed(ContentDialogResult result) { var closedEventArgs = new ContentDialogClosedEventArgs(ClosedEvent, this) { Result = result }; RaiseEvent(closedEventArgs); } /// /// Invoked when a is clicked. /// /// The button that was clicked. protected virtual void OnButtonClick(ContentDialogButton button) { var buttonClickEventArgs = new ContentDialogButtonClickEventArgs(ButtonClickedEvent, this) { Button = button, }; RaiseEvent(buttonClickEventArgs); ContentDialogResult result = button switch { ContentDialogButton.Primary => ContentDialogResult.Primary, ContentDialogButton.Secondary => ContentDialogResult.Secondary, _ => ContentDialogResult.None, }; Hide(result); } // protected override Size MeasureOverride(Size availableSize) // { // var rootElement = (UIElement)GetVisualChild(0)!; // // rootElement.Measure(availableSize); // Size desiredSize = rootElement.DesiredSize; // // Size newSize = GetNewDialogSize(desiredSize); // // SetCurrentValue(DialogHeightProperty, newSize.Height); // SetCurrentValue(DialogWidthProperty, newSize.Width); // // ResizeWidth(rootElement); // ResizeHeight(rootElement); // // return desiredSize; // } /// /// Occurs after Loaded event /// protected virtual void OnLoaded() { Focus(); RaiseEvent(new RoutedEventArgs(OpenedEvent)); } // private Size GetNewDialogSize(Size desiredSize) // { // // TODO: Handle negative values // var paddingWidth = Padding.Left + Padding.Right; // var paddingHeight = Padding.Top + Padding.Bottom; // // var marginHeight = DialogMargin.Bottom + DialogMargin.Top; // var marginWidth = DialogMargin.Left + DialogMargin.Right; // // var width = desiredSize.Width - marginWidth + paddingWidth; // var height = desiredSize.Height - marginHeight + paddingHeight; // // return new Size(width, height); // } // private void ResizeWidth(UIElement element) // { // if (DialogWidth <= DialogMaxWidth) // { // return; // } // // SetCurrentValue(DialogWidthProperty, DialogMaxWidth); // element.UpdateLayout(); // // SetCurrentValue(DialogHeightProperty, element.DesiredSize.Height); // // if (DialogHeight > DialogMaxHeight) // { // SetCurrentValue(DialogMaxHeightProperty, DialogHeight); // /*Debug.WriteLine($"DEBUG | {GetType()} | WARNING | DialogHeight > DialogMaxHeight after resizing width!");*/ // } // } // private void ResizeHeight(UIElement element) // { // if (DialogHeight <= DialogMaxHeight) // { // return; // } // // SetCurrentValue(DialogHeightProperty, DialogMaxHeight); // element.UpdateLayout(); // // SetCurrentValue(DialogWidthProperty, element.DesiredSize.Width); // // if (DialogWidth > DialogMaxWidth) // { // SetCurrentValue(DialogMaxWidthProperty, DialogWidth); // /*Debug.WriteLine($"DEBUG | {GetType()} | WARNING | DialogWidth > DialogMaxWidth after resizing height!");*/ // } // } }