Search code examples
c#wpfprism

Return parameter on Close[X] button with DialogResult


I have a dialog which returns a IList value for the key CreatedGroups.

I can already return said string when the user presses a certain button on the dialog to close it.

private void CloseDialogOK()
{
    CanCloseDialog = true;

    DialogParameters parm = new DialogParameters();
    parm.Add("CreatedGroups", _createdGroups);
    RequestClose?.Invoke(new DialogResult(ButtonResult.OK, parm));
}

However, as I cannot remove the [X] Close button located on the dialog's top right, how can I attach the same method above to the [X] Close button?


Solution

  • Prism has an attached property to set a style for the dialog window in the dialog UserControl markup. There, you can set the WindowStyle to None to remove the buttons and title.

    <UserControl ...>
       <mvvm:Dialog.WindowStyle>
          <Style TargetType="Window">
             <Setter Property="WindowStyle" Value="None"/>
          </Style>
       </mvvm:Dialog.WindowStyle>
       <!-- ...other markup. -->
    </UserControl>
    

    If this does not fulfill your requirements, you will have to create a custom dialog window.

    There you could customize everything, even create your own buttons and title bar, e.g.:

    <Window x:Class="MCOSMOS.Infrastructure.Interactivity.Confirmations.ConfirmationWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            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:telerik="http://schemas.telerik.com/2008/xaml/presentation"
            mc:Ignorable="d"
            SizeToContent="WidthAndHeight"
            WindowStartupLocation="CenterOwner">
       <WindowChrome.WindowChrome>
          <WindowChrome GlassFrameThickness="1"
                        CaptionHeight="0"
                        ResizeBorderThickness="1"
                        UseAeroCaptionButtons="False"
                        NonClientFrameEdges="Bottom"/>
       </WindowChrome.WindowChrome>
       <Window.Template>
          <ControlTemplate TargetType="{x:Type Window}">
             <DockPanel Background="White">
                <Grid DockPanel.Dock="Top">
                   <Grid.ColumnDefinitions>
                      <ColumnDefinition Width="*"/>
                      <ColumnDefinition Width="Auto"/>
                   </Grid.ColumnDefinitions>
                   <TextBlock Grid.Column="0"
                              Text="{Binding Title, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
                   <Button Grid.Column="1"
                           Focusable="False"
                           Content="X">
                   </Button>
                </Grid>
                <ContentPresenter DockPanel.Dock="Bottom"/>
             </DockPanel>
          </ControlTemplate>
       </Window.Template>
    </Window>
    

    In this case, your could either add a Click event handler to the close Button or bind a command to it in order to add your custom behavior.

    Another option of using the default window style, but changing how the close button works is to create a custom dialog window that adds a handler to the Closed event. Then you can put your custom behavior there. It will be called before the DialogService from Prism handles the event.

    public partial class YourCustomDialogWindow : Window, IDialogWindow
    {
       public YourCustomDialogWindow()
       {
          InitializeComponent();
          Closed += OnClosed;
       }
       
       public IDialogResult Result { get; set; }
       
       private void OnClosed(object sender, EventArgs e)
       {
          // Check for null to make sure no other button caused closing.
          if (DialogResult is null)
          {
             var dataContext = (YourDataContext)DataContext;
             // ...do something, call a method and set a result.
          }
       }
    }
    

    In general, instead of casting to a concrete data context type, you could provide your own interface to make the dialog with this mechanism reusable instead of hard-wired to a single data context.

    Yet another option to solve the issue in a general way is to create your own DialogService by deriving from the default implementation and changing how closing is handled. Overwrite the ConfigureDialogWindowEvents method, copy the default implementation here and customize the closedHandler, e.g.:

    EventHandler closedHandler = null;
    closedHandler = (o, e) =>
        {
            // ...
    
            if (dialogWindow.Result == null)
                dialogWindow.Result = // ...get and set your result.
    
            // ...
        };
    dialogWindow.Closed += closedHandler;
    

    Please be aware that these approaches assume that all other methods of closing the dialog will set an IDialogResult, so that you can be sure that a value of null means the dialog was closed through an external mechanism like the close button.