WPF: Erstellen Sie einen Dialog / eine Eingabeaufforderung

81

Ich muss einen Dialog / eine Eingabeaufforderung einschließlich TextBox für Benutzereingaben erstellen. Mein Problem ist, wie man den Text erhält, nachdem man den Dialog bestätigt hat. Normalerweise würde ich dafür eine Klasse erstellen, die den Text in einer Eigenschaft speichert. Ich möchte den Dialog jedoch mit XAML entwerfen. Ich müsste also irgendwie den XAML-Code erweitern, um den Inhalt der TextBox in einer Eigenschaft zu speichern - aber ich denke, das ist mit reinem XAML nicht möglich. Was wäre der beste Weg, um zu realisieren, was ich tun möchte? Wie erstelle ich einen Dialog, der aus XAML definiert werden kann, aber dennoch irgendwie die Eingabe zurückgeben kann? Danke für jeden Hinweis!

stefan.at.wpf
quelle

Antworten:

141

Die "verantwortliche" Antwort wäre, dass ich vorschlage, ein ViewModel für den Dialog zu erstellen und die bidirektionale Datenbindung in der TextBox zu verwenden, damit das ViewModel eine "ResponseText" -Eigenschaft hat oder was nicht. Dies ist einfach genug, aber wahrscheinlich übertrieben.

Die pragmatische Antwort wäre, Ihrem Textfeld einfach einen x: Namen zu geben, damit es Mitglied wird, und den Text als Eigenschaft in Ihrem Code hinter der Klasse wie folgt verfügbar zu machen:

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

Dann in deinem Code dahinter ...

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

Dann, um es zu benutzen ...

var dialog = new MyDialog();
if (dialog.ShowDialog() == true) {
    MessageBox.Show("You said: " + dialog.ResponseText);
}
Josh
quelle
Vielen Dank Josh und Entschuldigung für meine späte Antwort! Ich habe mich anfangs zu sehr darauf konzentriert, XAML aus einer Datei zu laden, anstatt nur eine Klasse zu erstellen, wie Sie sie gezeigt haben.
stefan.at.wpf
7
Sie müssen das Klickereignis der Schaltfläche OK behandeln und dies festlegen. DialogResult = true; um den Dialog zu schließen und dialog.ShowDialog () == true zu haben.
Erwin Mayer
Das ist immer noch eine gute Antwort.
tCoe
Ich fand einen schönen einfachen Eingabeaufforderungsdialog, der bereit ist, den Link
vinsa
Nur ein Problem, das ich hier sehen kann, ist, dass dieser Dialog auch Maximierungs- und Minimierungsschaltfläche hat .... Ist es möglich, diese Schaltflächen zu deaktivieren?
Aleksey Timoshchenko
32

Ich füge nur eine statische Methode hinzu, um sie wie eine MessageBox aufzurufen:

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

Und der Code dahinter:

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

Sie können es also so nennen:

string repeatPassword = PromptDialog.Prompt("Repeat password", "Password confirm", inputType: PromptDialog.InputType.Password);
Pythonizo
quelle
6
+1 Zum Implementieren einer MessageBoxstatischen Methode im Stil. Prägnanter, wiederverwendbarer Code!
Aaron Blenkush
2
Sobald ich das Wort "statisch" sah, ignorierte ich die anderen Antworten. Danke dir! :)
maplemale
16

Tolle Antwort von Josh, alle Ehre ihm, ich habe es jedoch leicht geändert:

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 dahinter

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

Und nenn es woanders

var dialog = new MyDialog("test", "hello");
dialog.Show();
dialog.Closing += (sender,e) =>
{
    var d = sender as MyDialog;
    if(!d.Canceled)
        MessageBox.Show(d.InputText);
}
xtds
quelle
Sie sollten (in Ihrer Rasterdefinition xaml) 50 * und 50 * durch * und * ersetzen, da keine 50 erforderlich sind.
Mafii
Tipp: Wenn Sie WindowStyle="ToolWindow"das Fenster einstellen, sieht es noch schöner aus. Auch WindowStartupLocation="CenterOwner"und dialog.Owner = this;positioniert es in der Mitte des übergeordneten Fensters
Solo
2

Sie brauchen nicht ANY diese anderen ausgefallenen Antworten. Im Folgenden ein einfaches Beispiel ist , dass nicht alle hat Margin, Height, Widthin der XAML - Eigenschaften festlegen, sollten aber zeigen genug sein , wie dies auf einer grundlegenden Ebene getan.

XAML
Erstellen Sie eine WindowSeite wie gewohnt und fügen Sie Ihre Felder hinzu, z. B. a Labelund TextBoxsteuern Sie in a StackPanel:

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

Erstellen Sie dann einen Standard Buttonfür die Übermittlung ("OK" oder "Übermitteln") und eine Schaltfläche "Abbrechen", wenn Sie möchten:

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

Code-Behind
Sie fügen die ClickEreignishandlerfunktionen in den Code -Behind ein. Wenn Sie jedoch dorthin gehen, deklarieren Sie zunächst eine öffentliche Variable, in der Sie Ihren Textfeldwert speichern:

public static string strUserName = String.Empty;

Für die Ereignishandlerfunktionen (klicken Sie mit der rechten ClickMaustaste auf die Funktion auf der Schaltfläche XAML, wählen Sie "Gehe zu Definition", sie wird für Sie erstellt) müssen Sie überprüfen, ob Ihr Feld leer ist. Sie speichern es in Ihrer Variablen, wenn dies nicht der Fall ist, und schließen Ihr Fenster:

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

Aufrufen von einer anderen Seite
Sie denken, wenn ich dort oben mein Fenster damit schließe, this.Close()ist mein Wert weg, oder? NEIN!! Ich habe dies auf einer anderen Website herausgefunden: http://www.dreamincode.net/forums/topic/359208-wpf-how-to-make-simple-popup-window-for-input/

Sie hatten ein ähnliches Beispiel dafür (ich habe es ein wenig aufgeräumt), wie Sie Ihre Windowvon einem anderen öffnen und die Werte abrufen können:

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


Schaltfläche " Abbrechen" Sie denken, was ist mit dieser Schaltfläche "Abbrechen"? Also fügen wir einfach eine weitere öffentliche Variable in unser Popup-Fenster Code-Behind ein:

public static bool cancelled = false;

Lassen Sie uns unseren btnCancel_ClickEvent-Handler einbeziehen und eine Änderung vornehmen an 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.");
}

Und dann lesen wir einfach diese Variable in unserer MainWindow btnOpenPopup_ClickVeranstaltung:

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

Lange Antwort, aber ich wollte zeigen, wie einfach dies die Verwendung von public staticVariablen ist. Nein DialogResult, keine Rückgabewerte, nichts. Öffnen Sie einfach das Fenster, speichern Sie Ihre Werte mit den Schaltflächenereignissen im Popup-Fenster und rufen Sie sie anschließend in der Hauptfensterfunktion ab.

vapcguy
quelle
Es gibt viele Möglichkeiten, den bereitgestellten Code zu verbessern: 1) Verwenden Sie keine statischen Daten zum Speichern von Daten, da sonst gelegentlich Probleme mit mehreren Dialogfeldern auftreten. 2) es gibt DialogResult, um 'true' über ShowDialog () zu "übergeben"; 3) IsCancel-Attribut für eine Schaltfläche macht es zu einer echten Abbrechen-Schaltfläche ohne zusätzlichen Code ...
AntonK
@AntonK 1) Mit statischen Objekten können Sie Variablen in anderen Klassen aufrufen, ohne sie ständig instanziieren zu müssen. Für mich schneiden statische Variablen all das aus und sind weitaus vorzuziehen. Hatte nie ein Problem mit ihnen, da sie jedes Mal zurückgesetzt werden, wenn das Objekt (Fenster, Seite), das sie hat, geöffnet wird. Wenn Sie mehrere Dialoge möchten, erstellen Sie für jeden einen Dialog - verwenden Sie nicht immer wieder denselben oder ja, das ist problematisch - aber auch eine schlechte Codierung, denn warum sollten Sie denselben Dialog 50 Mal wollen?
Vapcguy
@AntonK 2) Sie können nicht passieren zurück DialogResultin WPF, es ist MessageBoxResult, die ich nur aus Standard - Tasten auf einem Werk gefunden MessageBox.Show()Dialog - nicht ein von einem Dialog benutzerdefinierten durch gezeigt .ShowDialog()- und nur Abfrage für die Standard - Operatoren kann, MessageBoxResult.OK, MessageBoxResult.Cancel„Ja“ , "Nein" usw. - keine Booleschen Werte oder benutzerdefinierten Werte. 3) IsCancelwürde es erfordern, es in einem Booleschen Wert zu speichern und es trotzdem zurückzusenden, so dass dies eine Einheitslösung war.
Vapcguy