Share via


對話方塊概觀

獨立應用程式 (Standalone Application) 通常都有主視窗,除了用來顯示應用程式操作的主要資料,還可透過user interface (UI) 機制 (例如功能表列、工具列及狀態列) 公開處理該資料的功能。 非一般性 (Non-Trivial) 應用程式也可能會顯示其他視窗,以執行下列動作:

  • 顯示特定資訊給使用者。

  • 蒐集使用者的資訊。

  • 顯示並蒐集資訊。

這些類型的視窗稱為「對話方塊」(Dialog Box),並有兩種類型:強制回應 (Modal) 和非強制回應 (Modeless)。

當函式需要使用者的其他資料才能繼續時,函式會顯示「強制回應」(Modal) 對話方塊。 因為函式需依賴強制回應對話方塊來蒐集資料,所以強制回應對話方塊在開啟狀態下會同時防止使用者啟動應用程式中的其他視窗。 在大部分情況下,強制回應對話方塊可讓使用者透過按 [確定] 或 [取消] 按鈕,發出已完成強制回應對話方塊的信號。 按 [確定] 按鈕表示使用者已輸入資料,而且想要函式繼續處理該資料。 按 [取消] 按鈕則表示使用者想要停止函式的執行。 最常見的強制回應對話方塊範例是開啟、儲存和列印資料。

而「非強制回應」(Modeless) 對話方塊在開啟時則不會防止使用者啟動其他視窗。 例如,如果使用者想要在文件中尋找出現的特定文字,則主視窗通常會開啟一個對話方塊,詢問使用者要尋找的文字。 不過,因為尋找文字不會讓使用者無法編輯文件,所以對話方塊不需要是強制回應對話方塊。 非強制回應對話方塊至少會提供 [關閉] 按鈕來關閉對話方塊,而且可能會提供其他按鈕來執行特定的函式 (例如 [找下一個] 按鈕,可尋找下一個符合文字搜尋之搜尋準則的文字)。

Windows Presentation Foundation (WPF) 可讓您建立多種類型的對話方塊,包括訊息方塊、通用對話方塊 (Common Dialog Box) 和自訂對話方塊。 本主題會討論每種類型的對話方塊,而對話方塊範例 (英文) 則提供相符範例。

這個主題包含下列章節。

  • 訊息方塊
  • 通用對話方塊
  • 自訂對話方塊
  • 相關主題

訊息方塊

「訊息方塊」(Message Box) 是一個對話方塊,可以用來顯示文字資訊,而且允許使用者使用按鈕進行決策。 下圖顯示的訊息方塊可顯示文字資訊、詢問問題,並提供使用者三個按鈕以回答問題。

文書處理器對話方塊

若要建立訊息方塊,可以使用 MessageBox 類別。 MessageBox 可讓您使用下列程式碼以設定訊息方塊文字、標題、圖示及按鈕。

' Configure the message box to be displayed
Dim messageBoxText As String = "Do you want to save changes?"
Dim caption As String = "Word Processor"
Dim button As MessageBoxButton = MessageBoxButton.YesNoCancel
Dim icon As MessageBoxImage = MessageBoxImage.Warning
// Configure the message box to be displayed
string messageBoxText = "Do you want to save changes?";
string caption = "Word Processor";
MessageBoxButton button = MessageBoxButton.YesNoCancel;
MessageBoxImage icon = MessageBoxImage.Warning;

若要顯示訊息方塊,請呼叫 static Show 方法,如下列程式碼所示。

' Display message box
MessageBox.Show(messageBoxText, caption, button, icon)
// Display message box
MessageBox.Show(messageBoxText, caption, button, icon);

當顯示訊息方塊的程式碼需要偵測及處理使用者的決策時 (按哪個按鈕),程式碼可以檢查訊息方塊結果,如下列程式碼所示。

' Display message box
Dim result As MessageBoxResult = MessageBox.Show(messageBoxText, caption, button, icon)

' Process message box results
Select Case result
    Case MessageBoxResult.Yes
        ' User pressed Yes button
        ' ...
    Case MessageBoxResult.No
        ' User pressed No button
        ' ...
    Case MessageBoxResult.Cancel
        ' User pressed Cancel button
        ' ...
End Select
// Display message box
MessageBoxResult result = MessageBox.Show(messageBoxText, caption, button, icon);

// Process message box results
switch (result)
{
    case MessageBoxResult.Yes:
        // User pressed Yes button
        // ...
        break;
    case MessageBoxResult.No:
        // User pressed No button
        // ...
        break;
    case MessageBoxResult.Cancel:
        // User pressed Cancel button
        // ...
        break;
}

如需使用訊息方塊的詳細資訊,請參閱 MessageBoxMessageBox 範例 (英文) 和對話方塊範例 (英文)。

雖然 MessageBox 提供的只是簡單的對話方塊使用者經驗,但是使用 MessageBox 的優點是,這是在部分信任安全性沙箱內執行之應用程式唯一能顯示的視窗類型 (請參閱 安全性 (WPF)),例如 (XAML browser applications (XBAPs))。

大部分對話方塊顯示並蒐集的資料會比訊息方塊的結果還複雜,包括文字、選項 (核取方塊)、互斥選項 (選項按鈕) 以及清單選項 (清單方塊、下拉式方塊、下拉式清單方塊)。 針對這些項目,Windows Presentation Foundation (WPF) 提供多種通用對話方塊,而且可讓您建立自己的對話方塊,但是上述任一種使用都受限於以完全信任執行的應用程式。

通用對話方塊

Windows 會實作所有應用程式通用的多種可重複使用對話方塊,包括用來開啟檔案、儲存檔案以及進行列印的對話方塊。 因為這些對話方塊是由作業系統實作的,所以在作業系統上執行的所有應用程式都能共用這些對話方塊,這樣有助於呈現一致的使用者經驗。而當使用者熟悉在某個應用程式中使用作業系統提供的對話方塊後,就不需要再學習於其他應用程式中使用該對話方塊的方式。 因為這些對話方塊適用於所有應用程式,而且它們有助於提供一致的使用者經驗,所以稱為「通用對話方塊」(Common Dialog Box)。

Windows Presentation Foundation (WPF) 會封裝開啟檔案、儲存檔案以及列印的通用對話方塊,並將它們公開為 Managed 類別,讓您可以用於獨立應用程式中。 本主題提供這三個通用對話方塊的簡短概觀。

開啟檔案對話方塊

下圖中所示的開啟檔案對話方塊,是供檔案開啟功能用來擷取所要開啟之檔案的名稱。

開啟對話方塊

通用的開啟檔案對話方塊是實作為 OpenFileDialog 類別,而且位在 Microsoft.Win32 命名空間 (Namespace) 中。 下列程式碼顯示如何建立、設定及顯示一個對話方塊,以及如何處理結果。

' Configure open file dialog box
Dim dlg As New Microsoft.Win32.OpenFileDialog()
dlg.FileName = "Document" ' Default file name
dlg.DefaultExt = ".txt" ' Default file extension
dlg.Filter = "Text documents (.txt)|*.txt" ' Filter files by extension

' Show open file dialog box
Dim result? As Boolean = dlg.ShowDialog()

' Process open file dialog box results
If result = True Then
    ' Open document
    Dim filename As String = dlg.FileName
End If
// Configure open file dialog box
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
dlg.FileName = "Document"; // Default file name
dlg.DefaultExt = ".txt"; // Default file extension
dlg.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension

// Show open file dialog box
Nullable<bool> result = dlg.ShowDialog();

// Process open file dialog box results
if (result == true)
{
    // Open document
    string filename = dlg.FileName;
}

如需開啟檔案對話方塊的詳細資訊,請參閱 Microsoft.Win32.OpenFileDialog

注意事項注意事項

以部分信任執行的應用程式,可以使用 OpenFileDialog 來安全擷取檔案名稱 (請參閱 安全性 (WPF))。

儲存檔案對話方塊

下圖中所示的儲存檔案對話方塊,是供檔案儲存功能用來擷取所要儲存之檔案的名稱。

另存新檔對話方塊

通用的儲存檔案對話方塊是實作為 SaveFileDialog 類別,而且位在 Microsoft.Win32 命名空間中。 下列程式碼顯示如何建立、設定及顯示一個對話方塊,以及如何處理結果。

' Configure save file dialog box
Dim dlg As New Microsoft.Win32.SaveFileDialog()
dlg.FileName = "Document" ' Default file name
dlg.DefaultExt = ".text" ' Default file extension
dlg.Filter = "Text documents (.txt)|*.txt" ' Filter files by extension

' Show save file dialog box
Dim result? As Boolean = dlg.ShowDialog()

' Process save file dialog box results
If result = True Then
    ' Save document
    Dim filename As String = dlg.FileName
End If
// Configure save file dialog box
Microsoft.Win32.SaveFileDialog dlg = new Microsoft.Win32.SaveFileDialog();
dlg.FileName = "Document"; // Default file name
dlg.DefaultExt = ".text"; // Default file extension
dlg.Filter = "Text documents (.txt)|*.txt"; // Filter files by extension

// Show save file dialog box
Nullable<bool> result = dlg.ShowDialog();

// Process save file dialog box results
if (result == true)
{
    // Save document
    string filename = dlg.FileName;
}

如需儲存檔案對話方塊的詳細資訊,請參閱 Microsoft.Win32.SaveFileDialog

列印對話方塊

下圖中所示的列印對話方塊,是供列印功能用來選擇及設定使用者想要列印資料的印表機。

列印對話方塊

通用的列印對話方塊是實作為 PrintDialog 類別,而且位在 System.Windows.Controls 命名空間中。 下列程式碼顯示如何建立、設定及顯示一個對話方塊。

' Configure printer dialog box
Dim dlg As New PrintDialog()
dlg.PageRangeSelection = PageRangeSelection.AllPages
dlg.UserPageRangeEnabled = True

' Show save file dialog box
Dim result? As Boolean = dlg.ShowDialog()

' Process save file dialog box results
If result = True Then
    ' Print document
End If
// Configure printer dialog box
System.Windows.Controls.PrintDialog dlg = new System.Windows.Controls.PrintDialog();
dlg.PageRangeSelection = PageRangeSelection.AllPages;
dlg.UserPageRangeEnabled = true;

// Show save file dialog box
Nullable<bool> result = dlg.ShowDialog();

// Process save file dialog box results
if (result == true)
{
    // Print document
}

如需列印對話方塊的詳細資訊,請參閱 System.Windows.Controls.PrintDialog。 如需在 WPF 中列印的詳細討論,請參閱列印概觀

自訂對話方塊

雖然通用對話方塊很有用,也應該盡可能使用,但它們並不支援網域特定對話方塊的需求。 在這種情況下,您就需要建立自己的對話方塊。 我們接下來會看到,對話方塊是具有特殊行為的視窗。 Window 會實作那些行為,因此您可以使用 Window 來建立自訂的強制回應及非強制回應對話方塊。

建立強制回應自訂對話方塊

本主題顯示如何使用 Window 來建立典型的強制回應對話方塊實作,其中使用 [Margins] 對話方塊做為範例 (請參閱對話方塊範例 (英文))。 下圖顯示 [Margins] 對話方塊。

邊界對話方塊

設定強制回應對話方塊

典型對話方塊的使用者介面包括下列項目:

  • 蒐集所要資料需要的各種控制項。

  • 顯示 [確定] 按鈕,使用者按一下即可關閉對話方塊、返回函式,並繼續處理。

  • 顯示 [取消] 按鈕,使用者按一下即可關閉對話方塊,並停止函式的進一步處理。

  • 在標題列中顯示 [關閉] 按鈕。

  • 顯示圖示。

  • 顯示 [最小化]、[最大化] 及 [還原] 按鈕。

  • 顯示 [系統] 功能表,用以最小化、最大化、還原和關閉對話方塊。

  • 在開啟對話方塊的視窗上層中央開啟。

  • 對話方塊應該盡可能是可以調整大小的,因此,為了防止對話方塊過小,並提供使用者好用的預設大小,您需要分別設定預設和最小維度 (Dimension)。

  • 按 ESC 鍵的動作,應該設定為按 [取消] 按鈕的鍵盤快速鍵。 只要將 [取消] 按鈕的 IsCancel 屬性設為 true 即可達成。

  • 按 ENTER (或 RETURN) 鍵的動作,應該設定為按 [確定] 按鈕的鍵盤快速鍵。 只要將 [確定] 按鈕的 IsDefault 屬性設為 true 即可達成。

下列程式碼示範這個組態。


<Window 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.MarginsDialogBox"
    xmlns:local="clr-namespace:SDKSample"
    Title="Margins"
    Height="190"
    Width="300"
    MinHeight="10"
    MinWidth="300"
    ResizeMode="CanResizeWithGrip"
    ShowInTaskbar="False"
    WindowStartupLocation="CenterOwner" 
    FocusManager.FocusedElement="{Binding ElementName=leftMarginTextBox}">

  <Grid>


...


    <!-- Accept or Cancel -->
    <StackPanel Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="4">
      <Button Name="okButton" Click="okButton_Click" IsDefault="True">OK</Button>
      <Button Name="cancelButton" IsCancel="True">Cancel</Button>
    </StackPanel>

  </Grid >

</Window>
Imports System.Windows ' Window, RoutedEventArgs, IInputElement, DependencyObject
Imports System.Windows.Controls ' Validation
Imports System.Windows.Input ' Keyboard

Namespace SDKSample


Public Class MarginsDialogBox
    Inherits Window
    Public Sub New()
        Me.InitializeComponent()
    End Sub


...


End Class

End Namespace
using System.Windows; // Window, RoutedEventArgs, IInputElement, DependencyObject
using System.Windows.Controls; // Validation
using System.Windows.Input; // Keyboard

namespace SDKSample
{
    public partial class MarginsDialogBox : Window
    {
        public MarginsDialogBox()
        {
            InitializeComponent();
        }


...


    }
}

對話方塊的使用者經驗也會延伸至開啟對話方塊之視窗的功能表列。 如果功能表項目執行的函式需要透過對話方塊與使用者互動才能繼續進行,則在函式功能表項目的標頭中會有省略符號,如以下所示。

<!--Main Window-->


...


<MenuItem Name="formatMarginsMenuItem" Header="_Margins..." Click="formatMarginsMenuItem_Click" />

如果功能表項目執行的函式,所顯示的是不需要使用者互動的對話方塊 (例如 [關於] 對話方塊),則不需要省略符號。

開啟強制回應對話方塊

對話方塊一般是顯示為使用者選取功能表項目以執行網域特定函式 (例如設定文書處理器中的文件邊界) 的結果。 將視窗顯示為對話方塊就與顯示標準視窗類似,但是需要額外對話方塊特定的組態。 具現化 (Instantiate)、設定及開啟對話方塊的整個處理序,如下列程式碼所示。


Imports System '  EventArgs
Imports System.ComponentModel '  CancelEventArgs
Imports System.Windows '  Window, MessageBoxXxx, RoutedEventArgs
Imports System.Windows.Controls '  TextChangedEventArgs
Imports Microsoft.Win32 '  OpenFileDialog

Namespace SDKSample

Public Class MainWindow
    Inherits Window


...


    Private Sub formatMarginsMenuItem_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' Instantiate the dialog box
        Dim dlg As New MarginsDialogBox

        ' Configure the dialog box
        dlg.Owner = Me
        dlg.DocumentMargin = Me.documentTextBox.Margin

        ' Open the dialog box modally 
        dlg.ShowDialog()



...


End Sub


...


End Class

End Namespace
using System; // EventArgs
using System.ComponentModel; // CancelEventArgs
using System.Windows; // Window, MessageBoxXxx, RoutedEventArgs
using System.Windows.Controls; // TextChangedEventArgs
using Microsoft.Win32; // OpenFileDialog

namespace SDKSample
{
    public partial class MainWindow : Window
    {


...


        void formatMarginsMenuItem_Click(object sender, RoutedEventArgs e)
        {
            // Instantiate the dialog box
            MarginsDialogBox dlg = new MarginsDialogBox();

            // Configure the dialog box
            dlg.Owner = this;
            dlg.DocumentMargin = this.documentTextBox.Margin;

            // Open the dialog box modally 
            dlg.ShowDialog();



...


}


...


    }
}

在這裡,程式碼會傳遞預設資訊 (目前邊界) 給對話方塊。 它也會使用連往顯示對話方塊之視窗的參照,來設定 Window.Owner 屬性。 一般而言,一律應該設定對話方塊的擁有者 (Owner),以便提供所有對話方塊都適用的視窗狀態相關行為 (如需詳細資訊,請參閱 WPF 視窗概觀)。

注意事項注意事項

您必須提供擁有者,才能支援對話方塊的user interface (UI) 自動化 (請參閱 UI 自動化概觀)。

設定對話方塊之後,對話方塊會透過呼叫 ShowDialog 方法以強制回應方式顯示。

驗證使用者提供的資料

對話方塊已開啟,使用者也提供了必要資料後,基於下列原因,對話方塊會負責確保所提供的資料是有效的:

  • 從安全性角度來看,所有輸入都應該驗證。

  • 從網域特定角度來看,資料驗證可防止程式碼處理到錯誤的資料,而這接著可能會擲回例外狀況 (Exception)。

  • 從使用者經驗角度來看,對話方塊可以顯示使用者輸入的哪些資料無效,協助使用者操作。

  • 從效能角度來看,多層應用程式中的資料驗證可以減少用戶端與應用程式層之間的來回行程次數,特別是在應用程式是由 Web 服務或伺服器端資料庫組成時。

若要在 WPF 中驗證繫結控制項,則需要定義驗證規則,並將它與繫結產生關聯。驗證規則是一種衍生自 ValidationRule 的自訂類別。 下列範例顯示驗證規則 MarginValidationRule,此規則會檢查繫結值是否為 Double 並且位在所指定的範圍內。

Imports System.Globalization
Imports System.Windows.Controls

Namespace SDKSample

Public Class MarginValidationRule
    Inherits ValidationRule

    Private _maxMargin As Double
    Private _minMargin As Double

    Public Property MaxMargin() As Double
        Get
            Return Me._maxMargin
        End Get
        Set(ByVal value As Double)
            Me._maxMargin = value
        End Set
    End Property

    Public Property MinMargin() As Double
        Get
            Return Me._minMargin
        End Get
        Set(ByVal value As Double)
            Me._minMargin = value
        End Set
    End Property

    Public Overrides Function Validate(ByVal value As Object, ByVal cultureInfo As CultureInfo) As ValidationResult

        Dim margin As Double

        ' Is a number?
        If Not Double.TryParse(CStr(value), margin) Then
            Return New ValidationResult(False, "Not a number.")
        End If

        ' Is in range?
        If ((margin < Me.MinMargin) OrElse (margin > Me.MaxMargin)) Then
            Dim msg As String = String.Format("Margin must be between {0} and {1}.", Me.MinMargin, Me.MaxMargin)
            Return New ValidationResult(False, msg)
        End If

        ' Number is valid
        Return New ValidationResult(True, Nothing)

    End Function

End Class

End Namespace
using System.Globalization;
using System.Windows.Controls;

namespace SDKSample
{
    public class MarginValidationRule : ValidationRule
    {
        double minMargin;
        double maxMargin;

        public double MinMargin
        {
            get { return this.minMargin; }
            set { this.minMargin = value; }
        }

        public double MaxMargin
        {
            get { return this.maxMargin; }
            set { this.maxMargin = value; }
        }

        public override ValidationResult Validate(object value, CultureInfo cultureInfo)
        {
            double margin;

            // Is a number?
            if (!double.TryParse((string)value, out margin))
            {
                return new ValidationResult(false, "Not a number.");
            }

            // Is in range?
            if ((margin < this.minMargin) || (margin > this.maxMargin))
            {
                string msg = string.Format("Margin must be between {0} and {1}.", this.minMargin, this.maxMargin);
                return new ValidationResult(false, msg);
            }

            // Number is valid
            return new ValidationResult(true, null);
        }
    }
}

在這個程式碼中,是透過覆寫 Validate 方法來實作驗證規則的驗證邏輯,這個方法會驗證資料,並傳回適當的 ValidationResult

若要將驗證規則與繫結控制項產生關聯,請使用下列標記。

<Window 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="SDKSample.MarginsDialogBox"
    xmlns:local="clr-namespace:SDKSample"
    Title="Margins"
    Height="190"
    Width="300"
    MinHeight="10"
    MinWidth="300"
    ResizeMode="CanResizeWithGrip"
    ShowInTaskbar="False"
    WindowStartupLocation="CenterOwner" 
    FocusManager.FocusedElement="{Binding ElementName=leftMarginTextBox}">

  <Grid>
    


...


<Label Grid.Column="0" Grid.Row="0">Left Margin:</Label>
<TextBox Name="leftMarginTextBox" Grid.Column="1" Grid.Row="0">
  <TextBox.Text>
    <Binding Path="Left" UpdateSourceTrigger="PropertyChanged">
      <Binding.ValidationRules>
        <local:MarginValidationRule MinMargin="0" MaxMargin="10" />
      </Binding.ValidationRules>
    </Binding>
  </TextBox.Text>
</TextBox>


...


</Window>

一旦關聯驗證規則,WPF 會在將資料輸入繫結控制項時自動套用該驗證規則。 控制項包含無效資料時,WPF 會在無效控制項周圍顯示紅色框線,如下圖所示。

無效的左邊界

在使用者輸入有效資料之前,WPF 都不會限制使用者不可以使用無效控制項。 對對話方塊而言,這十分恰當;不論資料是否有效,使用者都應該可以自由巡覽對話方塊中的控制項。 然而,這表示使用者可以輸入無效的資料,並按 [確定] 按鈕。 因此,您的程式碼也需要處理 Click 事件,來驗證按 [確定] 按鈕時對話方塊中的所有控制項。

Imports System.Windows ' Window, RoutedEventArgs, IInputElement, DependencyObject
Imports System.Windows.Controls ' Validation
Imports System.Windows.Input ' Keyboard

Namespace SDKSample


Public Class MarginsDialogBox
    Inherits Window


...


Private Sub okButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    ' Don't accept the dialog box if there is invalid data
    If Not Me.IsValid(Me) Then Return


...


    End Sub

    ' Validate all dependency objects in a window
    Private Function IsValid(ByVal node As DependencyObject) As Boolean

        ' Check if dependency object was passed and if dependency object is valid.
        ' NOTE: Validation.GetHasError works for controls that have validation rules attached 
        If ((Not node Is Nothing) AndAlso Validation.GetHasError(node)) Then
            ' If the dependency object is invalid, and it can receive the focus,
            ' set the focus
            If TypeOf node Is IInputElement Then
                Keyboard.Focus(DirectCast(node, IInputElement))
            End If
            Return False
        End If

        ' If this dependency object is valid, check all child dependency objects
        Dim subnode As Object
        For Each subnode In LogicalTreeHelper.GetChildren(node)
            If (TypeOf subnode Is DependencyObject AndAlso Not Me.IsValid(DirectCast(subnode, DependencyObject))) Then
                ' If a child dependency object is invalid, return false immediately,
                ' otherwise keep checking
                Return False
            End If
        Next

        ' All dependency objects are valid
        Return True

    End Function

End Class

End Namespace
using System.Windows; // Window, RoutedEventArgs, IInputElement, DependencyObject
using System.Windows.Controls; // Validation
using System.Windows.Input; // Keyboard

namespace SDKSample
{
    public partial class MarginsDialogBox : Window
    {


...


void okButton_Click(object sender, RoutedEventArgs e)
{
    // Don't accept the dialog box if there is invalid data
    if (!IsValid(this)) return;


...


        }

        // Validate all dependency objects in a window
        bool IsValid(DependencyObject node)
        {
            // Check if dependency object was passed
            if (node != null)
            {
                // Check if dependency object is valid.
                // NOTE: Validation.GetHasError works for controls that have validation rules attached 
                bool isValid = !Validation.GetHasError(node);
                if (!isValid)
                {
                    // If the dependency object is invalid, and it can receive the focus,
                    // set the focus
                    if (node is IInputElement) Keyboard.Focus((IInputElement)node);
                    return false;
                }
            }

            // If this dependency object is valid, check all child dependency objects
            foreach (object subnode in LogicalTreeHelper.GetChildren(node))
            {
                if (subnode is DependencyObject)
                {   
                    // If a child dependency object is invalid, return false immediately,
                    // otherwise keep checking
                    if (IsValid((DependencyObject)subnode) == false) return false;
                }
            }

            // All dependency objects are valid
            return true;
        }
    }
}

這個程式碼會列舉視窗上的所有相依性物件,如果有任一個物件無效 (透過 GetHasError 傳回),則無效控制項會取得焦點,而 IsValid 方法會傳回 false,並將視窗視為無效。

一旦對話方塊有效之後,就可以安全關閉對話方塊並返回。 在返回處理序期間,需要將結果傳回給呼叫函式。

設定強制回應對話方塊結果

使用 ShowDialog 開啟對話方塊,基本上與呼叫方法類似:使用 ShowDialog 開啟對話方塊的程式碼,會等到傳回 ShowDialog。 傳回 ShowDialog 時,呼叫它的程式碼需判斷是要繼續處理還是要停止處理,這取決於使用者按 [確定] 按鈕還是 [取消] 按鈕。 為了協助進行判斷,對話方塊需要傳回使用者的選擇,做為 ShowDialog 方法傳回的 Boolean 值。

按一下 [確定] 按鈕時,ShowDialog 應該要傳回 true。 在按一下 [確定] 按鈕時,設定對話方塊的 DialogResult 屬性即可達成。

請注意,設定 DialogResult 屬性同時會自動關閉視窗,如此就不需要明確呼叫 Close

按一下 [取消] 按鈕時,ShowDialog 應該要傳回 false,這也需要設定 DialogResult 屬性。

Imports System.Windows ' Window, RoutedEventArgs, IInputElement, DependencyObject
Imports System.Windows.Controls ' Validation
Imports System.Windows.Input ' Keyboard

Namespace SDKSample


Public Class MarginsDialogBox
    Inherits Window


...


Private Sub cancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    ' Dialog box canceled
    Me.DialogResult = False
End Sub


...


End Class

End Namespace
using System.Windows; // Window, RoutedEventArgs, IInputElement, DependencyObject
using System.Windows.Controls; // Validation
using System.Windows.Input; // Keyboard

namespace SDKSample
{
    public partial class MarginsDialogBox : Window
    {


...


void cancelButton_Click(object sender, RoutedEventArgs e)
{
    // Dialog box canceled
    this.DialogResult = false;
}


...


    }
}

如果按鈕的 IsCancel 屬性設為 true,而且使用者按 [取消] 按鈕或 ESC 鍵,則會將 DialogResult 自動設為 false。 下列標記與前一個程式碼的效果相同,但不需要處理 Click 事件。

<Button Name="cancelButton" IsCancel="True">Cancel</Button>

如果使用者按標題列中的 [關閉] 按鈕,或選擇 [系統] 功能表中的 [關閉] 功能表項目,對話方塊會自動傳回 false。

處理從強制回應對話方塊傳回的資料

如果對話方塊設定 DialogResult,則在傳回 ShowDialog 時檢查 DialogResult 屬性,開啟該對話方塊的函式就可以取得對話方塊結果。


Imports System '  EventArgs
Imports System.ComponentModel '  CancelEventArgs
Imports System.Windows '  Window, MessageBoxXxx, RoutedEventArgs
Imports System.Windows.Controls '  TextChangedEventArgs
Imports Microsoft.Win32 '  OpenFileDialog

Namespace SDKSample

Public Class MainWindow
    Inherits Window


...


Private Sub formatMarginsMenuItem_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)


...


    ' Process data entered by user if dialog box is accepted
    If (dlg.DialogResult.GetValueOrDefault = True) Then
        Me.documentTextBox.Margin = dlg.DocumentMargin
    End If
End Sub


...


End Class

End Namespace
using System; // EventArgs
using System.ComponentModel; // CancelEventArgs
using System.Windows; // Window, MessageBoxXxx, RoutedEventArgs
using System.Windows.Controls; // TextChangedEventArgs
using Microsoft.Win32; // OpenFileDialog

namespace SDKSample
{
    public partial class MainWindow : Window
    {


...


void formatMarginsMenuItem_Click(object sender, RoutedEventArgs e)
{


...


    // Process data entered by user if dialog box is accepted
    if (dlg.DialogResult == true)
    {
        // Update fonts
        this.documentTextBox.Margin = dlg.DocumentMargin;
    }
}


...


    }
}

如果對話方塊結果是 true,則函式會將它當成擷取及處理使用者所提供資料的提示。

注意事項注意事項

傳回 ShowDialog 之後,就無法再重新開啟對話方塊。而是需要建立新的執行個體。

如果對話方塊結果是 false,則函式應該會適當地結束處理。

建立非強制回應自訂對話方塊

非強制回應對話方塊 (如下圖所示的 [尋找] 對話方塊) 與強制回應對話方塊的基本外觀相同。

尋找對話方塊

然而,行為會略有不同 (如下列各節所述)。

開啟非強制回應對話方塊

非強制回應對話方塊是透過呼叫 Show 方法進行開啟。

<!--Main Window-->

Imports System '  EventArgs
Imports System.ComponentModel '  CancelEventArgs
Imports System.Windows '  Window, MessageBoxXxx, RoutedEventArgs
Imports System.Windows.Controls '  TextChangedEventArgs
Imports Microsoft.Win32 '  OpenFileDialog

Namespace SDKSample

Public Class MainWindow
    Inherits Window


...


Private Sub editFindMenuItem_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
    Dim dlg As New FindDialogBox(Me.documentTextBox)
    dlg.Owner = Me
    AddHandler dlg.TextFound, New TextFoundEventHandler(AddressOf Me.dlg_TextFound)
    dlg.Show()
End Sub


...


End Class

End Namespace
using System; // EventArgs
using System.ComponentModel; // CancelEventArgs
using System.Windows; // Window, MessageBoxXxx, RoutedEventArgs
using System.Windows.Controls; // TextChangedEventArgs
using Microsoft.Win32; // OpenFileDialog

namespace SDKSample
{
    public partial class MainWindow : Window
    {


...


void editFindMenuItem_Click(object sender, RoutedEventArgs e)
{
    // Instantiate the dialog box
    FindDialogBox dlg = new FindDialogBox(this.documentTextBox);

    // Configure the dialog box
    dlg.Owner = this;
    dlg.TextFound += new TextFoundEventHandler(dlg_TextFound);

    // Open the dialog box modally
    dlg.Show();
}


...


    }
}

ShowDialog 不同的是,Show 會立即傳回。 因此,呼叫視窗無法分辨關閉非強制回應對話方塊的時機,因而無法得知何時要檢查對話方塊結果,或從對話方塊取得資料以進行進一步處理。 而是對話方塊需要建立替代方式,將資料傳回給呼叫視窗以進行處理。

處理從非強制回應對話方塊傳回的資料

在這個範例中,FindDialogBox 可能會將一個或多個尋找結果傳回給主視窗,這取決於正在搜尋且沒有任何特定頻率的文字。 非強制回應對話方塊與強制回應對話方塊相同的地方是,都可以使用屬性傳回結果。 然而,擁有對話方塊的視窗需要知道何時要檢查那些屬性。 其中一種方式是讓對話方塊實作會在找到文字時引發的事件。 FindDialogBox 即針對此用途實作 TextFoundEvent,但需要先進行委派。

Namespace SDKSample
Public Delegate Sub TextFoundEventHandler(ByVal sender As Object, ByVal e As EventArgs)
End Namespace
using System;
namespace SDKSample
{
    public delegate void TextFoundEventHandler(object sender, EventArgs e);
}

FindDialogBox 透過 TextFoundEventHandler 委派,實作 TextFoundEvent。

Imports System ' EventArgs
Imports System.Windows ' Window, MessageBoxXxx, RoutedEventArgs
Imports System.Windows.Controls ' TextBox, TextChangedEventArgs
Imports System.Text.RegularExpressions ' Regex

Namespace SDKSample

Public Class FindDialogBox
    Inherits Window
    Public Event TextFound As TextFoundEventHandler
    Protected Overridable Sub OnTextFound()
        RaiseEvent TextFound(Me, EventArgs.Empty)
    End Sub


...


End Class

End Namespace
using System; // EventArgs
using System.Windows; // Window, MessageBoxXxx, RoutedEventArgs
using System.Windows.Controls; // TextBox, TextChangedEventArgs
using System.Text.RegularExpressions; // Regex

namespace SDKSample
{
    public partial class FindDialogBox : Window
    {
        public event TextFoundEventHandler TextFound;
        protected virtual void OnTextFound()
        {
            TextFoundEventHandler textFound = this.TextFound;
            if (textFound != null) textFound(this, EventArgs.Empty);
        }


...


    }
}

因此,在找到搜尋結果時,Find 就可以引發事件。

Imports System ' EventArgs
Imports System.Windows ' Window, MessageBoxXxx, RoutedEventArgs
Imports System.Windows.Controls ' TextBox, TextChangedEventArgs
Imports System.Text.RegularExpressions ' Regex

Namespace SDKSample

Public Class FindDialogBox
    Inherits Window


...


Private Sub findNextButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)


...


Me.Index = match.Index
Me.Length = match.Length
RaiseEvent TextFound(Me, EventArgs.Empty)


...


End Sub


...


End Class

End Namespace
using System; // EventArgs
using System.Windows; // Window, MessageBoxXxx, RoutedEventArgs
using System.Windows.Controls; // TextBox, TextChangedEventArgs
using System.Text.RegularExpressions; // Regex

namespace SDKSample
{
    public partial class FindDialogBox : Window
    {


...


void findNextButton_Click(object sender, RoutedEventArgs e)
{


...


// Text found
this.index = match.Index;
this.length = match.Length;
OnTextFound();


...


}


...


    }
}

接下來,主控視窗 (Owner Window) 需要登錄及處理這個事件。


Imports System '  EventArgs
Imports System.ComponentModel '  CancelEventArgs
Imports System.Windows '  Window, MessageBoxXxx, RoutedEventArgs
Imports System.Windows.Controls '  TextChangedEventArgs
Imports Microsoft.Win32 '  OpenFileDialog

Namespace SDKSample

Public Class MainWindow
    Inherits Window


...


    Private Sub dlg_TextFound(ByVal sender As Object, ByVal e As EventArgs)
        Dim dlg As FindDialogBox = DirectCast(sender, FindDialogBox)
        Me.documentTextBox.Select(dlg.Index, dlg.Length)
        Me.documentTextBox.Focus()
    End Sub

End Class

End Namespace
using System; // EventArgs
using System.ComponentModel; // CancelEventArgs
using System.Windows; // Window, MessageBoxXxx, RoutedEventArgs
using System.Windows.Controls; // TextChangedEventArgs
using Microsoft.Win32; // OpenFileDialog

namespace SDKSample
{
    public partial class MainWindow : Window
    {


...


        void dlg_TextFound(object sender, EventArgs e)
        {
            // Get the find dialog box that raised the event
            FindDialogBox dlg = (FindDialogBox)sender;

            // Get find results and select found text
            this.documentTextBox.Select(dlg.Index, dlg.Length);
            this.documentTextBox.Focus();
        }
    }
}

關閉非強制回應對話方塊

因為不需要設定 DialogResult,所以可以使用系統提供的機制來關閉非強制回應對話方塊,包括下列各項:

  • 按一下標題列中的 [關閉] 按鈕。

  • 按 ALT+F4。

  • 從 [系統] 功能表中選擇 [關閉]。

或者,程式碼也可以在按一下 [關閉] 按鈕時呼叫 Close

Imports System ' EventArgs
Imports System.Windows ' Window, MessageBoxXxx, RoutedEventArgs
Imports System.Windows.Controls ' TextBox, TextChangedEventArgs
Imports System.Text.RegularExpressions ' Regex

Namespace SDKSample

Public Class FindDialogBox
    Inherits Window


...


    Private Sub closeButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        MyBase.Close()
    End Sub
End Class

End Namespace
using System; // EventArgs
using System.Windows; // Window, MessageBoxXxx, RoutedEventArgs
using System.Windows.Controls; // TextBox, TextChangedEventArgs
using System.Text.RegularExpressions; // Regex

namespace SDKSample
{
    public partial class FindDialogBox : Window
    {


...


        void closeButton_Click(object sender, RoutedEventArgs e)
        {
            // Close dialog box
            this.Close();
        }
    }
}

請參閱

概念

快顯功能表概觀

其他資源

對話方塊範例

ColorPicker 自訂控制項範例