WPF: Створення діалогового вікна / підказки


84

Мені потрібно створити діалогове вікно / запит, включаючи TextBox для введення користувачем. Моя проблема полягає в тому, як отримати текст після підтвердження діалогового вікна? Зазвичай я роблю для цього клас, який зберігає текст у властивості. Однак я хочу зробити дизайн діалогу за допомогою XAML. Тому мені якось довелося б розподілити код XAML, щоб зберегти вміст TextBox у властивості, але я думаю, це неможливо з чистим XAML. Що було б найкращим способом зрозуміти, що я хотів би зробити? Як побудувати діалогове вікно, яке можна визначити з XAML, але все одно може якось повернути введені дані? Дякуємо за будь-яку підказку!

Відповіді:


143

"Відповідальною" відповіддю було б, щоб я запропонував побудувати ViewModel для діалогового вікна та використовувати двосторонню прив'язку даних на TextBox, щоб ViewModel мав якусь властивість "ResponseText" чи ні. Зробити це досить просто, але, мабуть, надмірно.

Прагматичною відповіддю було б просто дати вашому текстовому полі x: Name, щоб воно стало членом, і виставити текст як властивість у вашому коді за класом так:

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

Тоді у вашому коді позаду ...

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

Тоді, щоб використовувати його ...

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

Щиро дякую Джошу і вибачте за мою пізню відповідь! Спочатку я надто зосереджувався на завантаженні XAML з файлу, а не просто на створенні класу, як показано вами.
stefan.at.wpf

7
Вам потрібно обробити подію натискання кнопки OK і встановити this.DialogResult = true; щоб закрити діалогове вікно і мати dialog.ShowDialog () == true.
Ервін Майер,

це все ще чудова відповідь.
tCoe

Я знайшов хорошу просте швидке діалог готові до використання посиланням
vinsa

Тут я бачу лише одну проблему: у цьому діалоговому вікні є кнопка розгортання та кнопка мінімізації .... Чи можна вимкнути ці кнопки?
Олексій Тимощенко

35

Я просто додаю статичний метод, щоб викликати його як 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>

І код позаду:

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

Тож ви можете назвати це так:

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

6
+1 Для реалізації MessageBoxстатичного методу в стилі. Короткий, багаторазовий код!
Аарон Бленкуш,

2
Як тільки я побачив слово "статичний", я проігнорував інші відповіді. Дякую! :)
maplemale

16

Чудова відповідь Джоша, всім йому честь, я трохи змінив його до цього, проте:

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 позаду

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

І назвати це десь ще

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

Ви повинні замінити (у вашому визначенні сітки xaml) 50 * і 50 * на * і *, оскільки немає необхідності в 50.
Mafii

Порада: налаштування WindowStyle="ToolWindow"на Вікно робить його ще приємнішим. Також WindowStartupLocation="CenterOwner"і dialog.Owner = this;
соло

2

Вам не потрібна БІЛЬКА з цих інших вигадливих відповідей. Нижче наведено спрощений приклад , який не має все Margin, Height, Widthвластивості , встановлені в XAML, але має бути достатньо , щоб показати , як отримати це зроблено на базовому рівні.

XAML
Створіть Windowсторінку, як зазвичай, і додайте на неї свої поля, скажіть a Labelта TextBoxкеруйте всередині a StackPanel:

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

Потім створіть стандарт Buttonдля подання ("OK" або "Submit") і кнопку "Скасувати", якщо хочете:

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

Код позаду
Ви додасте Clickфункції обробника подій у код позаду, але коли ви перейдете туди, спочатку оголосіть загальнодоступну змінну, де ви будете зберігати значення тексту:

public static string strUserName = String.Empty;

Потім, для функцій обробника подій (клацніть правою кнопкою миші Clickфункцію на кнопці XAML, виберіть «Перейти до визначення», вона створить її для вас), вам потрібно перевірити, чи не пусте поле. Ви зберігаєте його у своїй змінній, якщо ні, і закриваєте вікно:

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

Виклик його з іншої сторінки
Ви думаєте, якщо я закрию вікно тим, що this.Close()там нагорі, моє значення зникне, так? НЕМАЄ!! Я дізнався про це з іншого веб-сайту: http://www.dreamincode.net/forums/topic/359208-wpf-how-to-make-simple-popup-window-for-input/

У них був подібний приклад з цим (я трохи його очистив) про те, як відкрити свій Windowз іншого і отримати значення:

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

Кнопка "Скасувати"
Ви думаєте, ну а як щодо цієї кнопки Скасувати? Отже, ми просто додаємо ще одну загальнодоступну змінну назад до нашого спливаючого вікна:

public static bool cancelled = false;

І включимо наш btnCancel_Clickобробник подій та внесемо одну зміну в 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.");
}

І тоді ми просто читаємо цю змінну в нашому MainWindow btnOpenPopup_Clickвипадку:

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

Довга відповідь, але я хотів показати, наскільки легко це використовувати public staticзмінні. Ні DialogResult, немає повернутих значень, нічого. Просто відкрийте вікно, збережіть свої значення з подіями кнопок у спливаючому вікні, а потім отримайте їх у головному вікні.


Існує багато способів покращити наданий код: 1) не використовуйте статику для зберігання даних, інакше у вас будуть випадкові проблеми з декількома діалогами; 2) є DialogResult для "передачі" 'true' через ShowDialog (); 3) Атрибут IsCancel для кнопки робить її справжньою кнопкою Скасувати без зайвого коду ...
AntonK

@AntonK 1) Використання статичних об'єктів - це те, як ви можете викликати змінні в інших класах, не маючи необхідності створювати їх постійно. Для мене статичні змінні вирізають все це і є набагато кращим. Ніколи з ними не було проблем, тому що вони будуть скинуті в будь-який час, коли об’єкт (Вікно, Сторінка), який їх має, буде відкрито. Якщо ви хочете кілька діалогів, створіть діалогове вікно для кожного - не використовуйте одне і те ж знову і знову, чи так, це проблематично - але також погане кодування, бо чому ви хочете один і той же діалог 50 разів?
vapcguy

@AntonK 2) Ви не можете передати назад DialogResultв WPF, це MessageBoxResult, що я знайшов працює тільки від стандартних кнопок на MessageBox.Show()діалог - не один з діалогового вікна користувальницького показаного через .ShowDialog()- і може тільки запит для стандартних операторів MessageBoxResult.OK, MessageBoxResult.Cancel«Так» , "Ні" тощо - не логічні значення або спеціальні значення. 3) у будь-якому IsCancelвипадку потрібно буде зберегти його у логічній формі та відправити назад, тож це було універсальне рішення.
vapcguy
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.