2010-05-09 19 views
61

Necesito crear un cuadro de diálogo/Prompt que incluya TextBox para la entrada del usuario. Mi problema es cómo obtener el texto después de haber confirmado el diálogo. Usualmente haría una clase para esto que guardara el texto en una propiedad. Sin embargo, quiero diseñar el Diálogo utilizando XAML. Así que de alguna manera tendría que extender el código XAML para guardar el contenido del TextBox en una propiedad, pero supongo que eso no es posible con XAML puro. ¿Cuál sería la mejor manera de darme cuenta de lo que me gustaría hacer? ¿Cómo crear un diálogo que se puede definir desde XAML pero que de alguna manera puede devolver la entrada? Gracias por cualquier pista!WPF: Crear un cuadro de diálogo/solicitud

Respuesta

109

La respuesta "responsable" sería que sugiriera construir un ViewModel para el diálogo y usar un enlace de datos bidireccional en el TextBox para que ViewModel tuviera alguna propiedad de "ResponseText" o no. Esto es bastante fácil de hacer, pero probablemente exagerado.

La respuesta pragmática sería simplemente dar a su cuadro de texto una x: Nombre por lo que se convierte en un miembro y exponer el texto como una propiedad en su código detrás de la clase, así:

<!-- 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> 

Luego, en su código detrás ...

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; 
    } 
} 

después utilizarla ...

var dialog = new MyDialog(); 
if (dialog.ShowDialog() == true) { 
    MessageBox.Show("You said: " + dialog.ResponseText); 
} 
+0

Muchas gracias Josh y lo siento por mi respuesta tardía! Inicialmente me concentré demasiado en cargar XAML desde un archivo en lugar de simplemente crear una clase como la que muestra. –

+2

+1 para señalar un enfoque pragmático :) – Jedidja

+6

Debe manejar el evento Aceptar clic en el botón y establecer esto. DialogResult = true; para cerrar el diálogo y tener dialog.ShowDialog() == true. –

15

Gran respuesta de Josh, todo crédito a él, que él ligeramente modificado para esto, sin embargo:

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> 

Código MyDialog Detrás

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(); 
    } 

y lo llaman en otro lugar

var dialog = new MyDialog("test", "hello"); 
dialog.Show(); 
dialog.Closing += (sender,e) => 
{ 
    var d = sender as MyDialog; 
    if(!d.Canceled) 
     MessageBox.Show(d.InputText); 
} 
+0

Debe reemplazar (en su definición de cuadrícula xaml) 50 * y 50 * con * y * porque no es necesario 50. – Mafii

24

Acabo de añadir un método estático para llamarlo como un 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> 

Y el código subyacente:

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(); 
    } 
} 

lo tanto se le puede llamar como:

string repeatPassword = PromptDialog.Prompt("Repeat password", "Password confirm", inputType: PromptDialog.InputType.Password); 
+4

+1 Para implementar un método estático de estilo 'MessageBox'. Conciso, código reutilizable! –

+1

Tan pronto como vi la palabra "estática" ignoré las otras respuestas. ¡Gracias! :) – maplemale

2

No es necesario CUALQUIER de estas otras respuestas de lujo. A continuación se muestra un ejemplo simplista que no tiene todas las propiedades Margin, Height, Width establecidas en el XAML, pero debería ser suficiente para mostrar cómo hacerlo en un nivel básico.

XAML
construir una página Window como lo haría normalmente y añadir sus campos a ella, por ejemplo un control de Label y TextBox dentro de un StackPanel:

<StackPanel Orientation="Horizontal"> 
    <Label Name="lblUser" Content="User Name:" /> 
    <TextBox Name="txtUser" /> 
</StackPanel> 

A continuación, crear un estándar Button para la Presentación (" OK "o" Enviar ") y un botón" Cancelar "si lo desea:

<StackPanel Orientation="Horizontal"> 
    <Button Name="btnSubmit" Click="btnSubmit_Click" Content="Submit" /> 
    <Button Name="btnCancel" Click="btnCancel_Click" Content="Cancel" /> 
</StackPanel> 

de código subyacente
va a añadir las funciones de controlador de eventos Click en el código subyacente, pero cuando vas allí, en primer lugar, declarar una variable pública donde almacenará el valor de cuadro de texto:

public static string strUserName = String.Empty; 

Luego, para las funciones del controlador de eventos (haga clic con el botón derecho en la función Click en el botón XAML, seleccione "Ir a definición", se creará por usted), necesita una verificación para ver si su casilla está vacía. Usted lo almacena en la variable si no lo es, y cerrar la ventana:

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."); 
} 

de llamarlo desde otra página
usted está pensando, si cierro mi ventana con this.Close() que allá arriba, mi valor se ha ido, ¿verdad? ¡NO! descubrí esto desde otro sitio: http://www.dreamincode.net/forums/topic/359208-wpf-how-to-make-simple-popup-window-for-input/

Tenían un ejemplo similar a esto (lo limpié un poco) de cómo abrir su Window de otro y recuperar los valores:

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 
    } 
} 

Botón Cancelar
Estás pensando, bueno, ¿qué pasa con ese botón Cancelar? Por lo que sólo tiene que añadir otra variable pública de vuelta en la ventana emergente de código subyacente:

public static bool cancelled = false; 

y vamos a incluir nuestro gestor de eventos btnCancel_Click, y hacer un cambio a 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."); 
} 

Y luego acaba de leer esa variable en nuestra MainWindowbtnOpenPopup_Click evento:

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 
    } 
} 

respuesta de largo, pero quería mostrar lo fácil esto está usando las variables public static. No DialogResult, sin valores devueltos, nada. Simplemente abra la ventana, almacene sus valores con los eventos del botón en la ventana emergente, luego recupérelos luego en la función de la ventana principal.

+0

Hay muchas maneras de mejorar el código proporcionado: 1) no use estática para almacenar datos, de lo contrario ocasionalmente se encontrará con problemas con varios cuadros de diálogo; 2) hay DialogResult para "pasar" 'verdadero' a través de ShowDialog(); 3) El atributo IsCancel para un botón lo convierte en un verdadero botón Cancelar sin ningún código adicional ... – AntonK

+0

@AntonK 1) Usar objetos estáticos es cómo se pueden llamar variables en otras clases sin tener que crear instancias de ellas todo el tiempo. Para mí, las variables estáticas recortan todo eso y son mucho más preferibles. Nunca tuve un problema con ellos, porque se restablecerán cada vez que se abra el objeto (Ventana, Página) que los tiene. Si quiere varios cuadros de diálogo, cree un cuadro de diálogo para cada uno (no use el mismo una y otra vez o sí, eso es problemático) pero también una codificación incorrecta, porque ¿por qué quiere el mismo cuadro de diálogo 50 veces? – vapcguy

+0

@AntonK 2) No se puede devolver 'DialogResult' en WPF, es' MessageBoxResult', que encontré que solo funciona desde botones estándar en un cuadro de diálogo 'MessageBox.Show()' - no uno de un diálogo personalizado mostrado a ' .ShowDialog() '- y solo puede consultar los operadores estándar,' MessageBoxResult.OK', 'MessageBoxResult.Cancel'," Yes "," No ", etc. - no Booleanos o valores personalizados. 3) 'IsCancel' requeriría almacenarlo en un booleano y enviarlo de vuelta, de todos modos, así que esta era una solución única para todos. – vapcguy

Cuestiones relacionadas