WPF: Create a dialog / prompt
Solution 1:
The "responsible" answer would be for me to suggest building a ViewModel for the dialog and use two-way databinding on the TextBox so that the ViewModel had some "ResponseText" property or what not. This is easy enough to do but probably overkill.
The pragmatic answer would be to just give your text box an x:Name so that it becomes a member and expose the text as a property in your code behind class like so:
<!-- Incredibly simplified XAML -->
<Window x:Class="MyDialog">
<StackPanel>
<TextBlock Text="Enter some text" />
<TextBox x:Name="ResponseTextBox" />
<Button Content="OK" Click="OKButton_Click" />
</StackPanel>
</Window>
Then in your code behind...
partial class MyDialog : Window {
public MyDialog() {
InitializeComponent();
}
public string ResponseText {
get { return ResponseTextBox.Text; }
set { ResponseTextBox.Text = value; }
}
private void OKButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
DialogResult = true;
}
}
Then to use it...
var dialog = new MyDialog();
if (dialog.ShowDialog() == true) {
MessageBox.Show("You said: " + dialog.ResponseText);
}
Solution 2:
Edit: Can be installed with nuget https://www.nuget.org/packages/PromptDialog/
I just add a static method to call it like a MessageBox:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
x:Class="utils.PromptDialog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
SizeToContent="WidthAndHeight"
MinWidth="300"
MinHeight="100"
WindowStyle="SingleBorderWindow"
ResizeMode="CanMinimize">
<StackPanel Margin="5">
<TextBlock Name="txtQuestion" Margin="5"/>
<TextBox Name="txtResponse" Margin="5"/>
<PasswordBox Name="txtPasswordResponse" />
<StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right">
<Button Content="_Ok" IsDefault="True" Margin="5" Name="btnOk" Click="btnOk_Click" />
<Button Content="_Cancel" IsCancel="True" Margin="5" Name="btnCancel" Click="btnCancel_Click" />
</StackPanel>
</StackPanel>
</Window>
And the code behind:
public partial class PromptDialog : Window
{
public enum InputType
{
Text,
Password
}
private InputType _inputType = InputType.Text;
public PromptDialog(string question, string title, string defaultValue = "", InputType inputType = InputType.Text)
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(PromptDialog_Loaded);
txtQuestion.Text = question;
Title = title;
txtResponse.Text = defaultValue;
_inputType = inputType;
if (_inputType == InputType.Password)
txtResponse.Visibility = Visibility.Collapsed;
else
txtPasswordResponse.Visibility = Visibility.Collapsed;
}
void PromptDialog_Loaded(object sender, RoutedEventArgs e)
{
if (_inputType == InputType.Password)
txtPasswordResponse.Focus();
else
txtResponse.Focus();
}
public static string Prompt(string question, string title, string defaultValue = "", InputType inputType = InputType.Text)
{
PromptDialog inst = new PromptDialog(question, title, defaultValue, inputType);
inst.ShowDialog();
if (inst.DialogResult == true)
return inst.ResponseText;
return null;
}
public string ResponseText
{
get
{
if (_inputType == InputType.Password)
return txtPasswordResponse.Password;
else
return txtResponse.Text;
}
}
private void btnOk_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
So you can call it like:
string repeatPassword = PromptDialog.Prompt("Repeat password", "Password confirm", inputType: PromptDialog.InputType.Password);
Solution 3:
Great answer of Josh, all credit to him, I slightly modified it to this however:
MyDialog Xaml
<StackPanel Margin="5,5,5,5">
<TextBlock Name="TitleTextBox" Margin="0,0,0,10" />
<TextBox Name="InputTextBox" Padding="3,3,3,3" />
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Name="BtnOk" Content="OK" Grid.Column="0" Margin="0,0,5,0" Padding="8" Click="BtnOk_Click" />
<Button Name="BtnCancel" Content="Cancel" Grid.Column="1" Margin="5,0,0,0" Padding="8" Click="BtnCancel_Click" />
</Grid>
</StackPanel>
MyDialog Code Behind
public MyDialog()
{
InitializeComponent();
}
public MyDialog(string title,string input)
{
InitializeComponent();
TitleText = title;
InputText = input;
}
public string TitleText
{
get { return TitleTextBox.Text; }
set { TitleTextBox.Text = value; }
}
public string InputText
{
get { return InputTextBox.Text; }
set { InputTextBox.Text = value; }
}
public bool Canceled { get; set; }
private void BtnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
{
Canceled = true;
Close();
}
private void BtnOk_Click(object sender, System.Windows.RoutedEventArgs e)
{
Canceled = false;
Close();
}
And call it somewhere else
var dialog = new MyDialog("test", "hello");
dialog.Show();
dialog.Closing += (sender,e) =>
{
var d = sender as MyDialog;
if(!d.Canceled)
MessageBox.Show(d.InputText);
}
Solution 4:
You don't need ANY of these other fancy answers. Below is a simplistic example that doesn't have all the Margin
, Height
, Width
properties set in the XAML, but should be enough to show how to get this done at a basic level.
XAML
Build a Window
page like you would normally and add your fields to it, say a Label
and TextBox
control inside a StackPanel
:
<StackPanel Orientation="Horizontal">
<Label Name="lblUser" Content="User Name:" />
<TextBox Name="txtUser" />
</StackPanel>
Then create a standard Button
for Submission ("OK" or "Submit") and a "Cancel" button if you like:
<StackPanel Orientation="Horizontal">
<Button Name="btnSubmit" Click="btnSubmit_Click" Content="Submit" />
<Button Name="btnCancel" Click="btnCancel_Click" Content="Cancel" />
</StackPanel>
Code-Behind
You'll add the Click
event handler functions in the code-behind, but when you go there, first, declare a public variable where you will store your textbox value:
public static string strUserName = String.Empty;
Then, for the event handler functions (right-click the Click
function on the button XAML, select "Go To Definition", it will create it for you), you need a check to see if your box is empty. You store it in your variable if it is not, and close your window:
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
if (!String.IsNullOrEmpty(txtUser.Text))
{
strUserName = txtUser.Text;
this.Close();
}
else
MessageBox.Show("Must provide a user name in the textbox.");
}
Calling It From Another Page
You're thinking, if I close my window with that this.Close()
up there, my value is gone, right? NO!! I found this out from another site: http://www.dreamincode.net/forums/topic/359208-wpf-how-to-make-simple-popup-window-for-input/
They had a similar example to this (I cleaned it up a bit) of how to open your Window
from another and retrieve the values:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnOpenPopup_Click(object sender, RoutedEventArgs e)
{
MyPopupWindow popup = new MyPopupWindow(); // this is the class of your other page
//ShowDialog means you can't focus the parent window, only the popup
popup.ShowDialog(); //execution will block here in this method until the popup closes
string result = popup.strUserName;
UserNameTextBlock.Text = result; // should show what was input on the other page
}
}
Cancel Button
You're thinking, well what about that Cancel button, though? So we just add another public variable back in our pop-up window code-behind:
public static bool cancelled = false;
And let's include our btnCancel_Click
event handler, and make one change to btnSubmit_Click
:
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
cancelled = true;
strUserName = String.Empty;
this.Close();
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
if (!String.IsNullOrEmpty(txtUser.Text))
{
strUserName = txtUser.Text;
cancelled = false; // <-- I add this in here, just in case
this.Close();
}
else
MessageBox.Show("Must provide a user name in the textbox.");
}
And then we just read that variable in our MainWindow
btnOpenPopup_Click
event:
private void btnOpenPopup_Click(object sender, RoutedEventArgs e)
{
MyPopupWindow popup = new MyPopupWindow(); // this is the class of your other page
//ShowDialog means you can't focus the parent window, only the popup
popup.ShowDialog(); //execution will block here in this method until the popup closes
// **Here we find out if we cancelled or not**
if (popup.cancelled == true)
return;
else
{
string result = popup.strUserName;
UserNameTextBlock.Text = result; // should show what was input on the other page
}
}
Long response, but I wanted to show how easy this is using public static
variables. No DialogResult
, no returning values, nothing. Just open the window, store your values with the button events in the pop-up window, then retrieve them afterwards in the main window function.