Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is there a mean to call Show DialogHost synchronously ? #1661

Closed
gitjsdr26 opened this issue Feb 16, 2020 · 3 comments
Closed

Is there a mean to call Show DialogHost synchronously ? #1661

gitjsdr26 opened this issue Feb 16, 2020 · 3 comments
Labels
question Old items with this tag will be automatically closed.

Comments

@gitjsdr26
Copy link

gitjsdr26 commented Feb 16, 2020

Hi,

Is there a mean to call Show method DialogHost synchronously ?
Indeed, I my case, I don't need the UI to be responsive until the dialog box is closed.

Thank you
Best regards

@Keboo Keboo added the question Old items with this tag will be automatically closed. label Feb 16, 2020
@Keboo
Copy link
Member

Keboo commented Feb 16, 2020

I assume you mean synchronously, since the existing DialogHost.Show method is asynchronous.
Right now the answer is no, there is only the asynchronous option. This is because you do need your UI to be responsive. If it were not responsive it would not be possible to close the dialog.

@gitjsdr26 gitjsdr26 changed the title Is there a mean to call Show DialogHost asynchronously ? Is there a mean to call Show DialogHost synchronously ? Feb 16, 2020
@gitjsdr26
Copy link
Author

gitjsdr26 commented Feb 16, 2020

I found a solution to wait for a dialogbox without blocking the calling thread.
It's far from the best solution but it works for me. If someone can enhance it...
This solution solves also the issue #1660 using a IsMessageDialogOpen property.

My solution :

  • In the main Window XAML
    DialogContent is Binded to the MainViewModel to a property MessageDialogContent
<Grid>
  <!-- See Help :
  https://github.com/MaterialDesignInXAML/MaterialDesignInXamlToolkit/wiki/Dialogs 
  https://intellitect.com/material-design-in-xaml-dialog-host/  -->
  <materialDesign:DialogHost Identifier="RootDialog" IsOpen="{Binding IsMessageDialogOpen}" DialogContent="{Binding MessageDialogContent}" CloseOnClickAway="False" DialogClosing="DialogHost_OnDialogClosing">
  • In the Main Window Code behind
/// <summary>
/// Manage DialogHost buttons clicked result
/// </summary>
/// <param name="sender"></param>
/// <param name="eventArgs"></param>
private void DialogHost_OnDialogClosing(object sender, DialogClosingEventArgs eventArgs)
{
  if ((sender is DialogHost) && ((sender as DialogHost).DialogContent is MessageDialog))
  {
    ((sender as DialogHost).DialogContent as MessageDialog).SetMessageDialogResult(eventArgs);
  }
  else
  {
    // Using NLog library
    _logger.Error("DialogHost_OnDialogClosing, unexpected sender type = ", sender);
  }
}
  • In the MainViewModel
    Property MessageDialogContent Binded to the MainWindow XAML materialDesign:DialogHost
/// <summary>
/// Modal message box View Model content
/// </summary>
private MessageDialog _messageDialogContent;
public MessageDialog MessageDialogContent
{
  get => _messageDialogContent;
  set => Set(ref _messageDialogContent, value);
}

/// <summary>
/// Display modal message box
/// </summary>
private bool _isMessageDialogOpen;
public bool IsMessageDialogOpen
{
	get => _isMessageDialogOpen;
	set => Set(ref _isMessageDialogOpen, value);
}

And the asynchronous method to show the message DialogBox

/// <summary>
/// Show a modal message dialog
/// </summary>
/// <param name="message"></param>
public async Task<System.Windows.Forms.DialogResult> ShowMessageDialog(string message, System.Windows.Forms.MessageBoxButtons messageBoxButtons = System.Windows.Forms.MessageBoxButtons.OK)
{
  if (IsMessageDialogOpen)
  {
    // If Dialog box already opened, add the new message to previous one
    MessageDialogContent = new MessageDialog((MessageDialogContent as MessageDialog).messageExternalAccess + "\n\n" + message, messageBoxButtons);
  }
  else
  {
    MessageDialogContent = new MessageDialog(message, messageBoxButtons);
    IsMessageDialogOpen = true;
  }
  
  await Task.Factory.StartNew(() =>
  {
    while (IsMessageDialogOpen)
    {
      Thread.Sleep(10);
    }
  });
  
  return MessageDialogContent.DialogResult;
}
  • The XAML Message Dialog Box
<UserControl x:Class="ConfJasAdr.Views.MessageDialog"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             MaxWidth="400">
	
    <Grid Margin="16">
		
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
		
		<TextBlock x:Name="Message" Margin="0 6 0 6" Grid.Row="0" Style="{StaticResource MaterialDesignButtonTextBlock}" TextWrapping="WrapWithOverflow"/>

		<!-- Buttons -->
		<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Grid.Row="1" Margin="0 10 0 0">
			<Button x:Name="OKButton" IsDefault="False" Style="{DynamicResource MaterialDesignFlatButton}" HorizontalAlignment="Center" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" CommandParameter="OKButton">
				<TextBlock>OK</TextBlock>
			</Button>
			<Button x:Name="YesButton" IsDefault="False" Style="{DynamicResource MaterialDesignFlatButton}" HorizontalAlignment="Center" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" CommandParameter="YesButton">
				<TextBlock>Yes</TextBlock>
			</Button>
			<Button x:Name="NoButton" IsDefault="True" Style="{DynamicResource MaterialDesignFlatButton}" HorizontalAlignment="Center" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" CommandParameter="NoButton">
				<TextBlock>No</TextBlock>
			</Button>
			<Button x:Name="CancelButton" IsDefault="True" Style="{DynamicResource MaterialDesignFlatButton}" HorizontalAlignment="Center" Command="{x:Static materialDesign:DialogHost.CloseDialogCommand}" CommandParameter="CancelButton">
			    <TextBlock>Cancel</TextBlock>
		    </Button>
		</StackPanel>
		
    </Grid>
</UserControl>
  • The C# Message Dialog Box code behind
using MaterialDesignThemes.Wpf;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ConfJasAdr.Views
{
    /// <summary>
    /// Interaction logic for SampleMessageDialog.xaml
    /// </summary>
    public partial class MessageDialog : UserControl
    {
        public string messageExternalAccess;
        public System.Windows.Forms.DialogResult DialogResult { get; private set; }

        public MessageDialog(string message, System.Windows.Forms.MessageBoxButtons messageBoxButtons = System.Windows.Forms.MessageBoxButtons.OK)
        {
            InitializeComponent();

            // Reset Buttons default state
            OKButton.Visibility = Visibility.Collapsed;
            YesButton.Visibility = Visibility.Collapsed;
            NoButton.Visibility = Visibility.Collapsed;
            CancelButton.Visibility = Visibility.Collapsed;

            switch (messageBoxButtons)
            {
                case System.Windows.Forms.MessageBoxButtons.OK:
                    OKButton.Visibility = Visibility.Visible;
                    break;
                case System.Windows.Forms.MessageBoxButtons.OKCancel:
                    OKButton.Visibility = Visibility.Visible;
                    CancelButton.Visibility = Visibility.Visible;
                    break;
                case System.Windows.Forms.MessageBoxButtons.YesNoCancel:
                    YesButton.Visibility = Visibility.Visible;
                    CancelButton.Visibility = Visibility.Visible;
                    break;
                case System.Windows.Forms.MessageBoxButtons.YesNo:
                    YesButton.Visibility = Visibility.Visible;
                    NoButton.Visibility = Visibility.Visible;
                    break;
                default:
                    break;
            }

            messageExternalAccess = message;
            Message.Text = message;
        }

        /// <summary>
        /// Set the result of the closing Dialog Box
        /// </summary>
        /// <param name="eventArgs"></param>
        public void SetMessageDialogResult(DialogClosingEventArgs eventArgs)
        {
            switch (eventArgs.Parameter)
            {
                case "OKButton":
                    DialogResult = System.Windows.Forms.DialogResult.OK;
                    break;
                case "YesButton":
                    DialogResult = System.Windows.Forms.DialogResult.Yes;
                    break;
                case "NoButton":
                    DialogResult = System.Windows.Forms.DialogResult.No;
                    break;
                case "CancelButton":
                    DialogResult = System.Windows.Forms.DialogResult.Cancel;
                    break;
                default:
                    break;
            }
        }

    }
}
  • Then to finish, displaying synchonously the DialogBox :
System.Windows.Forms.DialogResult result = await ShowMessageDialog("Do you want to continue ?", System.Windows.Forms.MessageBoxButtons.OKCancel);
if (result == System.Windows.Forms.DialogResult.Cancel)
{
	return;
}

// Next line of code not executing until Dialogbox is closed
...

This last line of code blocks on the line if (result == System.Windows.Forms.DialogResult.Cancel)
but the calling thread is not blocked and the UI remains responsive.

Hoping it could help someone.

Best regards

@HClausing
Copy link
Contributor

HClausing commented Sep 4, 2020

On our company's current framework (we are moving to a new with MD) we used to work with System.Threading.DispatcherFrame, something like this:

private DispatcherFrame _frame;

public void ShowDialog()
{
    _frame = new DispatcherFrame(true);
    Dispatcher.PushFrame(_frame);
}
private void DialogResolved()
{
    _frame.Continue = false;
}

DialogResolved() is called by clicking on buttons (OK, Cancel, etc). We have an ViewModel action that runs assyncronous, and raises a MessageRequest Event. The view has a handler for this event and call the ShowDialog() from Messagebox clas. The ViewModel action keeps stops until the DialogResolved() is called.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Old items with this tag will be automatically closed.
Projects
None yet
Development

No branches or pull requests

3 participants