教學課程:使用 .NET 建立新的 WPF 應用程式
在本簡短的教學課程中,您將了解如何使用 Visual Studio 建立新的 Windows Presentation Foundation (WPF) 應用程式。 產生初始應用程式之後,您將了解如何新增控制項,以及如何處理事件。 在本教學課程結束時,您將有一個簡單的應用程式,可將名稱新增至清單方塊。
在本教學課程中,您會了解如何:
- 建立新的 WPF 應用程式
- 將控制項新增至表單
- 處理控制項事件以提供應用程式功能
- 執行應用程式
以下是您遵循本教學課程時所建置應用程式的預覽:
必要條件
提示
使用 Visual Studio 2022 17.4 版或更新版本,並同時安裝 .NET 7 和 .NET 6 個別元件。 Visual Studio 2022 17.4 版已新增 .NET 7 的支援。
建立 WPF 應用程式
建立新應用程式的第一個步驟是開啟 Visual Studio,並從範本產生應用程式。
開啟 Visual Studio。
選取 [建立新專案]。
在 [搜尋範本] 方塊中,輸入 wpf,然後按 Enter 鍵。
在 [程式碼語言] 下拉式清單中,選擇 [C#] 或 [Visual Basic]。
在範本清單中,選取 [WPF 應用程式],然後選取 [下一步]。
重要
請勿選取 [WPF 應用程式 (.NET Framework)] 範本。
下圖顯示 C# 和 Visual Basic .NET 專案範本。 如果您已套用程式碼語言篩選,則會看到對應的範本。
在 [設定新專案] 視窗中,執行下列動作:
- 在 [專案名稱] 方塊中,輸入 Names。
- 選取 [將解決方案與專案放置在同一個目錄] 核取方塊。
- 或者,選擇不同的 [位置] 來儲存您的程式碼。
- 選取下一步按鈕。
在 [其他資訊] 視窗中,針對 [目標 Framework] 選取 [.NET 6.0 (長期支援)]。 選取建立按鈕。
開啟 Visual Studio。
選取 [建立新專案]。
在 [搜尋範本] 方塊中,輸入 wpf,然後按 Enter 鍵。
在 [程式碼語言] 下拉式清單中,選擇 [C#] 或 [Visual Basic]。
在範本清單中,選取 [WPF 應用程式],然後選取 [下一步]。
重要
請勿選取 [WPF 應用程式 (.NET Framework)] 範本。
下圖顯示 C# 和 Visual Basic .NET 專案範本。 如果您已套用程式碼語言篩選,則會看到對應的範本。
在 [設定新專案] 視窗中,執行下列動作:
- 在 [專案名稱] 方塊中,輸入 Names。
- 選取 [將解決方案與專案放置在同一個目錄] 核取方塊。
- 或者,選擇不同的 [位置] 來儲存您的程式碼。
- 選取下一步按鈕。
在 [其他資訊] 視窗中,針對 [目標 Framework] 選取 [.NET 7.0 (標準期間支援)]。 選取建立按鈕。
應用程式產生後,Visual Studio 應該會開啟預設視窗 MainWindow 的 XAML 設計工具窗格。 如果看不到設計工具,請按兩下 [方案總管] 窗格中的 [MainWindow.xaml] 檔案,以開啟設計工具。
Visual Studio 的重要部分
Visual Studio 中的 WPF 支援有五個重要元件,您會在建立應用程式時與其互動:
方案總管
您的所有專案檔案、程式碼、視窗、資源都會出現在此窗格中。
屬性
此窗格會顯示您可以根據選取項目設定的屬性設定。 例如,如果您從 [方案總管] 選取項目,則會看到與檔案相關的屬性設定。 如果您在設計工具中選取物件,則會看到該項目的設定。
工具箱
工具箱包含您可以新增至表單的所有控制項。 若要將控制項新增至目前表單,請按兩下控制項或拖放控制項。
XAML 設計工具
這是 XAML 文件的設計工具。 它是互動式的,而您可以從 [工具箱] 拖放物件。 藉由在設計工具中選取和移動項目,您能夠以視覺化方式撰寫應用程式的使用者介面 (UI)。
當設計工具與編輯器都可見時,對於其中一項所做的變更會在另一項中反映出來。 當您在設計工具中選取項目時,[屬性] 窗格會顯示該物件的相關內容和屬性。
XAML 程式碼編輯器
這是 XAML 文件的 XAML 程式碼編輯器。 XAML 程式碼編輯器是不使用設計工具來手動製作 UI 的方法。 當設計工具中新增了控制項時,設計工具可能會推斷控制項上的屬性值。 XAML 程式碼編輯器可讓您獲得更多控制力。
當設計工具與編輯器都可見時,對於其中一項所做的變更會在另一項中反映出來。 當您在程式碼編輯器中瀏覽文字插入點時,[屬性] 窗格會顯示該物件的相關內容和屬性。
檢查 XAML
專案建立好之後,便可看見 XAML 程式碼編輯器,其中會有要顯示視窗所需的最少量 XAML 程式碼。 如果編輯器未開啟,請按兩下 [方案總管] 中的 [MainWindow.xaml] 項目。 您應該會看到類似下列範例的 XAML:
<Window x:Class="Names.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Names"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
</Grid>
</Window>
讓我們拆解此 XAML 程式碼以利進一步了解。 XAML 就是可由 WPF 所使用的編譯器處理的 XML。 其會描述 WPF UI,並且會與 .NET 程式碼互動。 若要了解 XAML,請至少要熟悉 XML 的基本概念。
文件的根 <Window>
代表 XAML 檔案所描述的物件類型。 有八個已宣告的屬性,這些屬性通常屬於三個類別:
命名空間
XML 命名空間會向 XML 提供結構,以判斷可於檔案中宣告的 XML 內容。
主要的
xmlns
屬性會匯入整個檔案的 XML 命名空間,在本案例中,其會對應至 WPF 所宣告的類型。 其他 XML 命名空間會宣告前置詞,並匯入 XAML 檔案的其他類型和物件。 例如,xmlns:local
命名空間會宣告local
前置詞,並對應至您的專案所宣告的物件,也就是在Names
程式碼命名空間中所宣告的物件。x:Class
屬性這個屬性會將
<Window>
對應至您的程式碼所定義的類型:MainWindow.xaml.cs 或 MainWindow.xaml.vb 檔案,此為Names.MainWindow
類別。Title
屬性XAML 物件上宣告的任何一般屬性會設定該物件的屬性。 在本案例中,
Title
屬性會設定Window.Title
屬性。
變更視窗
首先,請執行專案並查看預設輸出。 您會看到沒有任何控制項的彈出視窗,以及 MainWindow 的標題:
在我們的範例應用程式中,此視窗太大,且標題列的描述不夠具體。 請藉由將 XAML 中的適當屬性變更為下列值,來變更視窗的標題和大小:
<Window x:Class="Names.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Names"
mc:Ignorable="d"
Title="Names" Height="180" Width="260">
<Grid>
</Grid>
</Window>
準備版面配置
WPF 提供功能強大的版面配置系統,其中包含許多不同的版面配置控制項。 版面配置控制項有助於子控制項的放置和大小調整,且甚至可以自動進行。 在此 XAML 中提供給您的預設版面配置控制項是 <Grid>
控制項。
Grid
控制項可讓您定義列和欄 (就像資料表一樣),並可讓您將控制項放在特定列和欄組合的界限內。 您可以將任意數量的子控制項或其他版面配置控制項新增至 Grid
。 例如,您可以將另一個 Grid
控制項放置到特定列和欄組合中,然後這個新的 Grid
控制項可以定義更多列和欄,並且有自己的子控制項。
<Grid>
控制項會定義控制項所在的列和欄。 方格一律會宣告單一的列和欄,也就是說,方格依預設會是單一儲存格。 這種方式無法讓您極為彈性地放置控制項。
在新增列和欄之前,請將以下新屬性新增至 <Grid>
元素:Margin="10"
。 這會從視窗中插入方格,使其看起來更為美觀。
接下來,定義兩個列和兩個欄,將方格分成四個儲存格:
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
</Grid>
在 XAML 程式碼編輯器或 XAML 設計工具中選取方格,您會看到 XAML 設計工具顯示每個列和欄:
新增第一個控制項
方格現已建立好,接下來我們可以開始將控制項新增至其中。 首先,從標籤控制項開始。 在 <Grid>
元素內的列和欄定義後面建立新的 <Label>
元素,並為其提供 Names
的字串值:
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label>Names</Label>
</Grid>
<Label>Names</Label>
會定義內容 Names
。 有些控制項了解如何處理內容,有些則不了解。 控制項的內容會對應至 Content
屬性。 透過 XAML 屬性語法來設定內容,您會使用此格式:<Label Content="Names" />
。 這兩種方式可以完成相同的作業,設定標籤的內容以顯示文字 Names
。
但有一個問題,該標籤是由系統自動指派給方格的第一個列和欄,因此會佔用一半視窗。 第一個列不需要那麼大,因為這個列只用於標籤。 將第一個 <RowDefinition>
的 Height
屬性從 *
變更為 Auto
。 Auto
值會自動將方格列的大小調整為其內容 (在本案例中,即標籤控制項) 的大小。
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
請注意,設計工具現在所顯示的標籤只佔用少量可用高度。 現在有更多空間可供下一個列佔用。 大部分控制項都會定義其為了要有最佳樣貌所應佔用的某種高度和寬度值。 例如,標籤控制項便會有高度值以確保您可以看清楚標籤。
控制項的放置
讓我們來談談控制項的放置。 在上一節中建立的標籤會自動放在方格的第 0 列和第 0 欄中。 列和欄的編號從 0 開始,並針對每個新的列或欄遞增 1。 控制項不知道方格的任何資訊,而且控制項未定義任何屬性來控制其在方格內的放置位置。 控制項甚至可能放置到一些其他版面配置的控制項 (其有自己的規則集以定義放置方式) 內。
當控制項不了解方格時,要如何指示控制項使用不同的列或欄? 附加屬性! 方格會利用 WPF 所提供的強大屬性系統。 方格會定義子控制項可以宣告及使用的新屬性。 這些屬性實際上不存在於控制項本身之上,而是會在控制項新增至方格時由方格附加上去。
方格會定義兩個屬性來判斷子控制項的列和欄放置位置:Grid.Row
和 Grid.Column
。 如果控制項中省略這些屬性,則表示其預設值為 0,因此控制項會放在方格的第 0
列和第 0
欄中。 請嘗試將 Grid.Column
屬性設定為 1
,以變更 <Label>
控制項的放置位置:
<Label Grid.Column="1">Names</Label>
請注意您的標籤現在如何移至第二欄。 您可以使用 Grid.Row
和 Grid.Column
附加屬性來放置我們將建立的下一個控制項。 不過,現在請將標籤還原至第 0 欄。
建立名稱清單方塊
方格現已正確調整大小且標籤也已建立,接下來請在標籤下方的列上新增清單方塊控制項。 清單方塊會位於第 1
列和第 0
欄中。 我們也會將此控制項命名為 lstNames
。 為控制項命名後,就可以在程式碼後置中參考該控制項。 名稱會使用 x:Name
屬性指派給控制項。
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label>Names</Label>
<ListBox Grid.Row="1" x:Name="lstNames" />
</Grid>
新增其餘控制項
我們將新增的最後兩個控制項是文字輸入框和按鈕,以供使用者輸入要新增至清單方塊的名稱。 不過,我們不會嘗試為方格建立更多列和欄,而是會將這些控制項放入 <StackPanel>
版面配置控制項中。
堆疊面板與方格的不同之處在於控制項的放置方式。 當您使用 Grid.Row
和 Grid.Column
附加屬性向方格指出您希望控制項所在的位置時,堆棧面板便會自動運作,其做法是先放置第一個控制項,再於其後放置下一個控制項,如此循環直到所有控制項放置完成為止。 其會讓每個控制項「堆疊」在彼此下方。
在清單方塊後面建立 <StackPanel>
控制項,並將其放在方格的第 1
列、第 1
欄中。 新增另一個名為 Margin
且值為 5,0,0,0
的屬性:
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label>Names</Label>
<ListBox Grid.Row="1" x:Name="lstNames" />
<StackPanel Grid.Row="1" Grid.Column="1" Margin="5,0,0,0">
</StackPanel>
方格上先前使用過 Margin
屬性,但我們只放入單一值 10
。 現在我們已在堆疊面板上使用 5,0,0,0
的值。 邊界是 Thickness
類型,而且可以解譯這兩個值。 粗細會分別定義矩形框架每一邊 (、左、上、右、下) 的空間。 如果邊界的值是單一值,則四個邊都會使用這個值。
接下來,在 <StackPanel>
中建立 <TextBox>
和 <Button>
控制項。
<StackPanel Grid.Row="1" Grid.Column="1" Margin="5,0,0,0">
<TextBox x:Name="txtName" />
<Button x:Name="btnAdd" Margin="0,5,0,0">Add Name</Button>
</StackPanel>
視窗的版面配置已完成。 不過,我們的應用程式內還沒有任何可實際運作的邏輯。 接下來,我們需要將控制項事件連結至程式碼,並讓應用程式實際執行某些動作。
新增 Click 事件的程式碼
我們建立的 <Button>
有一個會在使用者按下按鈕時引發的 Click
事件。 您可以訂閱此事件,並新增程式碼以將名稱新增至清單方塊。 就像藉由新增 XAML 屬性來對控制項設定屬性一樣,您也可以使用 XAML 屬性來訂閱事件。 將 Click
屬性設定為 ButtonAddName_Click
<StackPanel Grid.Row="1" Grid.Column="1" Margin="5,0,0,0">
<TextBox x:Name="txtName" />
<Button x:Name="btnAdd" Margin="0,5,0,0" Click="ButtonAddName_Click">Add Name</Button>
</StackPanel>
現在您需要產生處理常式程式碼。 以滑鼠右鍵按一下 ButtonAddName_Click
,然後選取 [移至定義]。 此動作會在程式碼後置中為您產生符合所輸入處理常式名稱的方法。
private void ButtonAddName_Click(object sender, RoutedEventArgs e)
{
}
Private Sub ButtonAddName_Click(sender As Object, e As RoutedEventArgs)
End Sub
接下來,新增下列程式碼以執行這三個步驟:
- 確定文字輸入框包含名稱。
- 驗證在文字輸入框中所輸入的名稱不是既存名稱。
- 將該名稱新增至清單方塊。
private void ButtonAddName_Click(object sender, RoutedEventArgs e)
{
if (!string.IsNullOrWhiteSpace(txtName.Text) && !lstNames.Items.Contains(txtName.Text))
{
lstNames.Items.Add(txtName.Text);
txtName.Clear();
}
}
Private Sub ButtonAddName_Click(sender As Object, e As RoutedEventArgs)
If Not String.IsNullOrWhiteSpace(txtName.Text) And Not lstNames.Items.Contains(txtName.Text) Then
lstNames.Items.Add(txtName.Text)
txtName.Clear()
End If
End Sub
執行應用程式
既然事件已經編碼,您可以按 F5 鍵或從功能表中選取 [偵錯]>[開始偵錯] 來執行應用程式。 隨即會顯示視窗,您可以在文字輸入框中輸入名稱,然後按一下按鈕加以新增。