1

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.

I wanna ask you the best thing I'm able to do.

tnx for any help in advance

RezaNoei
  • 1,266
  • 1
  • 8
  • 24
  • You can pass a the content dialog as a parameter to the bound command using `CommandParameter` property, declare `CancelCommand` as a `RelayCommand` for example and implement `Cancel(object parameter)` to close/hide whatever "closable thing" is passed in. – Simon Mourier Aug 06 '23 at 15:30
  • I'm using it @SimonMourier already but it looks so Ugly. `RelayCommand` actually. – RezaNoei Aug 06 '23 at 15:33
  • Your code doesn't show *that*. You should post all what you've done and avoid wasting everyone's time – Simon Mourier Aug 06 '23 at 16:22
  • 1
    Sry for that @SimonMourier but I thought that it was an stupid solution, so I removed it from my question. I was looking for a perfect solution. I've updated my question. – RezaNoei Aug 06 '23 at 17:29
  • 1
    Creating a custom `ContentDialog` and do stuff in its code-behind would be easier and simple. If you need to do a complex logic in the dialog, instead of using a ViewModel, just pass a class with that logic and use it in the code-behind. – Andrew KeepCoding Aug 06 '23 at 22:35

1 Answers1

2

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();
}
Andrew KeepCoding
  • 7,040
  • 2
  • 14
  • 21