Search code examples
c#winui-3contentdialog

MVVM ContentDialog


I'm trying to write some ContentDialog with ViewModel. I'm not using default buttons. I'm using my customized set of buttons. (Including close button placed on top right corner) So I need to close this dialog somehow in the ViewModel but I can't do this because I should pass the ContentDialog into the ViewModel.

Here is my ContentDialogs content:

<Grid HorizontalAlignment="Center">
    <Grid.RowDefinitions>
        <RowDefinition Height="auto" />
        <RowDefinition Height="1*" />
        <RowDefinition Height="auto" />
    </Grid.RowDefinitions>
    
    <Grid Grid.Row="0" Height="32">
        <Button Width="32px" Height="32px"
                HorizontalAlignment="Right"
                Command="{x:Bind ViewModel.CancelCommand, Mode=OneWay}"
                CommandParameter="{Binding ElementName=addDeviceDialog}"
                Style="{ThemeResource CommandBarFlyoutEllipsisButtonStyle}">
            <FontIcon Width="16px" Height="16px" FontSize="10px" Glyph="&#xE8BB;" />
        </Button>
    </Grid>
    
    <Grid Grid.Row="1">
        
    </Grid>

    <Grid Grid.Row="2" Margin="0 20 0 0">
        <Button Width="219"
                HorizontalAlignment="Right"
                Command="{x:Bind ViewModel.AddCommand}"
                Content="Add"
                Style="{ThemeResource AccentButtonStyle}" />
    </Grid>
</Grid>

and here is my ViewModel:

public partial class AddDeviceDialogViewModel : ObservableRecipient
{
    public IRelayCommand AddCommand
    {
        get;
    }

    public IRelayCommand CancelCommand
    {
        get;
    }

    public AddDeviceDialogViewModel()
    {
        AddCommand = new RelayCommand(AddDevice);
        CancelCommand = new RelayCommand(Cancel);
    }

    public void AddDevice() // AddDevice(ContentDialog dialog)
    {
        // Here I need to add my business logic 
        if (AddedSuccessfully)
        {
          // dialog.Hide(); I don't wanna pass ContentDialog to it's ViewModel.

          // Close this dialog somehow.
        }
        else 
        {
          // Show validation errors.
        }
    }

    public async void Cancel() // Cancel(ContentDialog dialog)
    {
        // dialog.Hide(); I don't wanna pass ContentDialog to it's ViewModel.

        // Close dialog somehow.
    }
}

I think default implementation of the ContentDialog has an internal EventHandler which calls .Hide() to close this dialog.


Solution

  • As I mentioned in my comment, I'd do this without a ViewModel, but if you the ViewModel anyway, this is one way to close the ContentDialog from its ViewModel.

    ContentDialogContent.xaml

    <Page
        x:Class="ContentDialogWithViewModelExample.ContentDialogContent"
        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:local="using:ContentDialogWithViewModelExample"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
        mc:Ignorable="d">
    
        <Grid>
            <Button
                Command="{x:Bind ViewModel.CloseDialogCommand}"
                Content="Close" />
        </Grid>
    </Page>
    

    ContentDialogContent.xaml.cs

    public sealed partial class ContentDialogContent : Page
    {
        public ContentDialogContent()
        {
            InitializeComponent();
            ViewModel.CloseRequested += ViewModel_CloseRequested;
        }
    
        public event EventHandler? CloseRequested;
    
        public ContentDialogContentViewModel ViewModel { get; } = new();
    
        private void ViewModel_CloseRequested(object? sender, EventArgs e)
        {
            CloseRequested?.Invoke(this, EventArgs.Empty);
        }
    }
    

    ContentDialogContentViewModel.cs I'm using the CommunitToolkit.Mvvm NuGet package here.

    public partial class ContentDialogContentViewModel : ObservableObject
    {
        public event EventHandler? CloseRequested;
    
        [RelayCommand]
        private void CloseDialog()
        {
            CloseRequested?.Invoke(this, EventArgs.Empty);
        }
    }
    

    Then use the dialog like this:

    private async void ShowDialog_Click(object sender, RoutedEventArgs e)
    {
        ContentDialog dialog = new()
        {
            XamlRoot = XamlRoot
        };
    
        ContentDialogContent contentDialogContent = new();
        contentDialogContent.CloseRequested += (s, e) => dialog.Hide();
        dialog.Content = contentDialogContent;
    
        ContentDialogResult result = await dialog.ShowAsync();
    }