Calling Forms from Dialogs

Calling Forms from Dialogs

In order to initiate a FormDialog you can just do:

var myform = new FormDialog<CreateNewLeadForm>(new CreateNewLeadForm(), CreateNewLeadForm.BuildForm, FormOptions.PromptInStart, null);

context.Call<CreateNewLeadForm>(myform, FormCompleteCallback);

Take a look to the PizzaBot for an example.

To initiate new dialogs within a Dialog you can do:

  • context.Call passing the instance of the new dialog and the completion callback (as in the form)
  • context.Forward where you can forward the message to the child dialog

    context.Forward(new MyChildDialog(), ResumeAfterChildDialog, message, CancellationToken.None);

Calling form from another form in Bot Framework

I reproduced your issue when set context.Wait in the ResumeAfter method of the first formflow dialog. This will make the second dialog ends when user sets the first filed of formflow dialog 2. Since you didn't post all code, I assume this is the problem.

Then to solve this problem, we need to wait for the result of whole formflow dialog instead of waiting for the first filed of formflow dialog, means we need to correctly use context.Wait method, do not wait for use input if you want call the second formflow in the ResumeAfter method of first formflow dialog.

I will call the first and second dialog both from a RootDialog here as an example, if you want to use your first formflow dialog as a root dialog, it's a similar case.

My two FormFlow dialog is like this:

[Serializable]
public class FFDialog1
{
public string Test { get; set; }
public string Description { get; set; }
public static IForm<FFDialog1> BuildForm()
{
return new FormBuilder<FFDialog1>()
.Message("Welcome to the first FormFlow dialog")
.Field(nameof(Test))
.Field(nameof(Description))
.Build();
}
}

public enum YesOrNo
{
Yes,
No
}

[Serializable]
public class FFDialog2
{
public YesOrNo? Confirmation;
public string Input { get; set; }
public static IForm<FFDialog2> BuildForm()
{
return new FormBuilder<FFDialog2>()
.Message("Welcome to the second FormFlow dialog")
.Field(nameof(Confirmation))
.Field(nameof(Input))
.Build();
}
}

And in RootDialog, I call them like this:

[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);

return Task.CompletedTask;
}

private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;

//call the first formflow dialog
var form = new FormDialog<FFDialog1>(new FFDialog1(), FFDialog1.BuildForm, FormOptions.PromptInStart, null);
context.Call(form, ResumeAfterFirstFFDialog);
}

private async Task ResumeAfterFirstFFDialog(IDialogContext context, IAwaitable<object> result)
{
var dialog = await result as FFDialog1;

//call the second formflow dialog according to the result of first one.
if (dialog.Test != null && dialog.Description != null)
{
var form = new FormDialog<FFDialog2>(new FFDialog2(), FFDialog2.BuildForm, FormOptions.PromptInStart, null);
context.Call(form, ResumeAfterSecondFFDialog);
}
}

private async Task ResumeAfterSecondFFDialog(IDialogContext context, IAwaitable<object> result)
{
//get result of formflow dialog 2.
var dialog = await result as FFDialog2;

await context.PostAsync("Work is done!");

context.Wait(MessageReceivedAsync);
}
}

Here I didn't set OnCompletion of first formflow dialog cause the result will be submit when calling ResumeAfterFirstFFDialog, it's the same to call second one in this method.

WinForms - Show dialog and still use the calling form

Call the Show method instead of ShowDialog.

This method is a non-blocking call (unlike ShowDialog, it will return immediately, not after the new form closes) and will not show the form modally.

You'll probably want to pass the parent form as the parameter so that it will show as a child form.

Which is a better way to call Form.ShowDialog()?

Neither one is "better" than the other; they are perfectly equivalent!

However, in this particular case, both are wrong. The ShowDialog method requires you to call the Dispose method on the form. Unlike the Show and Close combination, this is not done automatically. From MSDN:

When a form is displayed as a modal dialog box, clicking the Close button (the button with an X at the upper-right corner of the form) causes the form to be hidden and the DialogResult property to be set to DialogResult.Cancel. Unlike non-modal forms, the Close method is not called by the .NET Framework when the user clicks the close form button of a dialog box or sets the value of the DialogResult property. Instead the form is hidden and can be shown again without creating a new instance of the dialog box. Because a form displayed as a dialog box is hidden instead of closed, you must call the Dispose method of the form when the form is no longer needed by your application.

Thus, you should choose between one of these (equivalent) forms:

using (Form1 frm = new Form1())
{
frm.ShowDialog();
}

or

Form1 frm = new Form1();
frm.ShowDialog();
frm.Dispose();

The reason that ShowDialog doesn't automatically dispose the form is simple enough, if not immediately obvious. It turns out that applications often wish to read values from an instance of a modal dialog form after the form has been closed, such as settings specified in the form's controls. If the form were automatically disposed, you would be unable to read those values by accessing properties of the form object. Thus, the programmer is responsible for disposing forms shown as modal dialogs when (s)he is finished with them.

Passing value from dialog form to main form

Create public property in FormTask

public string Opgave { get {return textBoxOpgave.Text;}}

And check it after ShowDialog();

FormTask formTask = new FormTask(exerciseType);
formOpgaveInvoer.ShowDialog();
formOpgaveInvoer.Opgave; // here it is

MS Bot with Multiple Form Dialog

To call a dialog, you need to use context.Call as explained in this post. However, I'm not fully sure if this will work in the OnCompletion event of a Form.

If it doesn't work, my recommendation would be to encapsulate the RootForm into a IDialog<object> dialog and use that dialog as the starting point for the Conversation.SendAsync of the controller.

Open a form from modal form that opened from parent form

Display your "child" form by setting TopLevel to false:

Form f = new Form();
f.ShowInTaskbar = false;
f.BackColor = Color.Black;
f.Size = this.Size;
f.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None;
f.StartPosition = FormStartPosition.CenterParent;
f.Opacity = 0.6;

notificationSize nds = new notificationSize();
nds.TopLevel = false;
nds.FormBorderStyle = ... // you may want to set this to none
nds.Dock = ... // you may want to set this to fill
f.Controls.Add(nds);
nds.Show(); // Open another form on Modal Dialog

f.ShowDialog(); // Open Modal window


Related Topics



Leave a reply



Submit