Trong bài này chúng ta sẽ tìm hiểu:
- Tạo ứng dụng WPF
- Sử dụng các điều khiển cơ bản (controls) như label, textbox, button, checkbox
- Thay đổi các thuộc tính cho forms và controls
- Xử lý sự kiện
Tạo ứng dụng WPF
Chúng ta đã làm quen với cách tạo một ứng dụng WPF đơn giản từ bài đầu tiên và trong bài này, chúng ta sẽ khám phá chi tiết hơn về cách tạo một ứng dụng WPF phức tạp. Bài thực hành sau sẽ giúp chúng ta hiểu về cách tạo một ứng dụng WPF, làm quen với bố cục (layout) và tương tác với các điều khiển.
Tạo ứng dụng WPF Application
- Vào thực đơn File chọn New > Project. Trong hộp thoại New Project chọn Visual C#, WPF Application, đặt tên dự án là BellRingers trong ô Name, và chọn vị trí lưu ứng dụng trong ô Location.
- Khi nhấn OK, VS sẽ phát sinh hai tập tin MainWindow.xaml và MainWindow.xaml.cs:
Khi chọn tab MainWindow.xaml chúng ta sẽ có giao diện trong như sau:
Đây là cửa sổ dùng cho việc thiết kế giao diện của ứng dụng. Chúng ta có thể thiết kế kéo-thả bằng cách dùng thanh Toolbox hay có thể viết mã trong khung XAML. Khi chọn tab MainWindow.xaml.cs:
Đây là cửa sổ được dùng để viết các chức năng cho ứng dụng bằng C#.
- Xem xét đoạn mã XAML của ứng dụng:
<Window x:Class="BellRingers.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> </Grid> </Window>
Giao diện chúng ta được tạo bởi phần tử Window (gồm một thẻ mở <Window> và một thẻ đóng </Window>). Phần tử Window chứa các thuộc tính (properties) sau:
- Class: xác định lớp thực thi form ứng dụng. Trong trường hợp này là lớp MainWindow được chứa trong namespace BellRingers.
- xmlns: xác định namespace XML để định nghĩa các lược đồ được dùng bởi WPF. Tất cả các thành phần (ví dụ các điều khiển) liên quan đến ứng dụng được định nghĩa trong namespace XML này.
- Title: xác định đoạn văn bản xuất hiện trên thanh tiêu đề của form.
- Height và Width: xác định chiều cao và chiều rộng mặc định cho form.
Phần tử Window chứa phần tử con là Grid (cũng gồm thẻ mở <Grid> và thẻ đóng </Grid>). Grid xác định bố cục (layout) kiểu lưới trong chế độ Design và chứa các controls như textbox, button, v.v. khi thêm đến form. Bố cục kiểu Grid gồm các cột và hàng giúp ta dễ dàng trong việc thiết kế giao diện. Ngoài Grid còn có các kiểu WrapPanel hay StackPanel.
Thay đổi tiêu đề của form theo hai cách:
Cách 1: sử dụng cửa sổ Properties (chọn form vào vào menu VIEW > Properties Window hay nhấn F4)
Tìm đến thuộc tính Title và nhập nội dung mới là Middleshire Bell Ringers Association Members (thay cho MainWindow):
Cách 2 (viết mã XAML): tìm đến thuộc tính Title của phần tử Window và thay đổi nội dung từ MainWindow sang Middleshire Bell Ringers Association Members
<Window x:Class="BellRingers.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Middleshire Bell Ringers Association Members" Height="350" Width="525"> <Grid> </Grid> </Window>
Chọn một button và thêm vào form tại một vị trí bất kỳ. Chọn button sẽ trông như sau:
Chúng ta thấy khoảng cách từ button đến cạnh trái của form là 378, đến cạnh trên là 74. Những điểm từ button đến các cạnh phải và dưới của form được gọi là các điểm neo (anchor points); các đoạn thẳng (kèm theo giá trị khoảng cách ) gọi là các kết nối (connectors)
Button lúc này được thêm vào Grid. Đoạn mã XAML lúc này như sau:
<Window x:Class="BellRingers.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Middleshire Bell Ringers Association Members" Height="350" Width="525"> <Grid> <Button Content="Button" HorizontalAlignment="Left" Margin="378,74,0,0" VerticalAlignment="Top" Width="75" /> </Grid </Window>
Với việc neo button như trên, khi chạy ứng dụng nếu chúng ta thay đổi kích cỡ form thì khoảng cách từ button đến cạnh trên và cạnh trái luôn không đổi như sau:
Trong trường hợp này, chúng ta kết nối đến các cạnh trái và trên của form. Có thể thay đổi cạnh kết nối bằng các thuộc tính HorizontalAlignment (cạnh trái hay phải)và VerticalAlignment (cạnh trên hay dưới). Thuộc tính Margin xác định khoảng cách từ button đến các cạnh theo trình tự trái (left) – trên (top) – phải (right) – dưới (bottom). Trong trường hợp trên, khoảng cách đến cạnh trái là 378 units, cạnh trên là 74 units, các giá trị còn lại bằng 0 do chúng ta không kết nối đến các cạnh phải và dưới (vì HorizontalAlignment=”Left” và VerticalAlignment=”Top”). Mặc định, giá trị của HorizontalAlignment và VerticalAlignment là Stretch – tức là neo đến tất cả các cạnh đối diện. Hãy thử chạy ứng dụng khi thiết lập giá trị Stretch cho hai thuộc tính này.
Chỉnh sửa lại đoạn mã XAML cho phần tử button như sau:
<Button Content="Button" Margin="0,0,0,0" Name="button2" Width="75" Height="23"/>
Chạy ứng dụng
Lúc này button luôn nằm giữa form và khi chúng ta thay đổi kích cỡ form button sẽ tự điều chỉnh vị trí của mình một cách tương đối với kích cỡ form sao cho vị trí của button luôn là giữa form:
Thêm ảnh nền đến form bằng cách thêm Image trên thanh Toolbox tại mục Common WPF Controls vào form. Điều chỉnh mã XAML cho Image như sau:
<Image Margin="0,0,0,0" Name="image1"/>
Lúc này Image sẽ mở rộng toàn bộ layout của form.
- Mục đích của Image là hiển thị hình ảnh. Trong cửa sổ Solution Explorer, kích chuột phải vào dự án BellRingers > Add > Existing Item và chọn ảnh bất kỳ từ ổ cứng, ví dụ ảnh noel.jpg. Với thao tác vừa thực hiện, chúng ta đã biến tập tin ảnh noel.jpg thành tài nguyên của ứng dụng.
- Thêm thuộc tính Image.Source đến Image bằng cách điều chỉnh mã XAML của Image như sau:
<Image Margin="0,0,0,0" Name="image1"> <Image.Source> <BitmapImage UriSource="noel.jpg" /> </Image.Source> </Image>
Kết quả:
Hình ảnh đã xuất hiện nhưng nó đã che mất button của chúng ta.
- Hình ảnh từ Image che khuất button vì Image xuất hiện trước button. Chúng ta có thể thay đổi thứ tự xuất hiện của các điều khiển bằng thuộc tính Panel.ZIndex. Điều khiển nào có giá trị ZIndex cao hơn sẽ xuất hiện trước điều khiển có giá trị ZIndex thấp hơn. Chúng ta sẽ gán Panel.ZIndex cho button = 1 và Panel.ZIndex cho Image là 0:
Button Panel.ZIndex="1" Content="Button" Margin="0,0,0,0" Name="button2" Width="75" Height="23"/> <Image Panel.ZIndex="0" Margin="0,0,0,0" Name="image1"> <Image.Source> <BitmapImage UriSource="noel.jpg" /> </Image.Source> </Image>
Kết quả:
Bài thực hành trên cung cấp cho chúng ta cách thức tạo ra một ứng dụng WPF và đồng thời hướng dẫn cách chúng ta làm việc với các điều khiển thông qua mã XAML. Với mã XAML, chúng ta có thể thay đổi cách các điểu khiển xuất hiện trên form sao cho ấn tượng nhất (theo nghĩa đơn giản, đẹp mắt, dễ sử dụng) đến người dùng. Trong bài thực hành kế tiếp, chúng ta sẽ tiếp tục tìm hiểu chi tiết hơn về cách thức WPF hỗ trợ chúng ta quản lý các thuộc tính của các điều khiển sao cho hiệu quả nhất.
Định nghĩa tập thuộc tính cho các điều khiển
Thêm một button khác đến form của chúng ta, nhớ điều chỉnh giá trị của thuộc tính Panel.Zindex đến 1. Button xuất hiện trên form cùng với mã XAML có thể như sau:
<Button Panel.ZIndex="1" Content="Button" Margin="0,0,0,0" Name="button2" Width="75" Height="23"/> <Button Panel.ZIndex="1" Content="Button" HorizontalAlignment="Left" Margin="354,69,0,0" VerticalAlignment="Top" Width="75"/> <Image Panel.ZIndex="0" Margin="0,0,0,0" Name="image1"> <Image.Source> <BitmapImage UriSource="noel.jpg" /> </Image.Source> </Image>
Giả sử chúng ta muốn thêm một số thuộc tính đến button thứ nhất như màu nền (Background), màu chữ (Foreground), kiểu chữ (FontFamily). Có thể dễ dàng làm điều này với cửa sổ Properties (tìm đến Background và Foreground trong Brush và FontFamily trong Text). Tuy nhiên, chúng ta cũng có thể làm điều này dễ dàng với mã XAML bằng cách dùng thuộc tính Button.Resources. Đoạn mã XAML sau thêm các thuộc tính Background, Foreground, FontFamily đến button thứ nhất bằng cách dùng Button.Resources cùng các thuộc tính con của nó như sau và lưu ý chúng ta đã thay đổi phần tử Button bằng cách xoá dấu / và thêm </Button> :
<Button Panel.ZIndex="1" Content="Button" Margin="0,0,0,0" Name="button2" Width="75" Height="23"> <Button.Resources> <Style x:Key="buttonStyle"> <Setter Property="Button.Background" Value="Gray"/> <Setter Property="Button.Foreground" Value="White"/> <Setter Property="Button.FontFamily" Value="Comic Sans MS"/> </Style> </Button.Resources> </Button>
Chúng ta sử dụng phần tử Style với thuộc tính là x:Key là tên đại diện cho tập thuộc tính. Chúng ta có thể thay đổi tuỳ ý giá trị của x:Key nhưng cần một số từ đại diện cho các control như buttonStyle hay textboxStyle. Mỗi thuộc tính sẽ được khởi tạo với phần tử Setter cùng với cặp thuộc tính Property (chỉ tên thuộc tính) và Value (giá trị của thuộc tính). Cú pháp tổng quát:
<Button.Resources> <Style x:Key=Tên_tập_thuộc_tính> <Setter Property= thuộc_tính_1 Value=Giá_trị_1/> <Setter Property= thuộc_tính_2 Value=Giá_trị_2/> ... </Style> </Button.Resources>
Tập thuộc tính của chúng ta gồm 3 thuộc tính là Background, Foreground và FontFamily nên chúng ta cần 3 phần tử Setter tương ứng với 3 cặp Property/Value:
<Style x:Key="buttonStyle"> <Setter Property="Button.Background" Value="Gray"/> <Setter Property="Button.Foreground" Value="White"/> <Setter Property="Button.FontFamily" Value="Comic Sans MS"/> </Style>
Lúc này các thuộc tính vẫn chưa ảnh hưởng đến button, chúng ta cần thêm thuộc tính Style với giá trị là {DynamicResource Tên_tập_thuộc_tính} đến button thứ nhất như sau:
<Button Style="{DynamicResource buttonStyle}" Panel.ZIndex="1" Content="Button" Margin="0,0,0,0" Name="button2" Width="75" Height="23"> <Button.Resources> ... </Button.Resources> </Button>
Kết quả:
Bên cạnh DynamicResource, chúng ta có thêm giá trị StaticResource, nhưng tốt nhất là nên sử dụng DynamicResource vì nó cho phép chúng ta thay đổi các thuộc tính khi thực thi ứng dụng (chúng ta có thể viết mã C# để thay đổi thuộc tính của điều khiển) trong khi StaticResource không cho phép điều này.
Bây giờ, nếu chúng ta muốn button thứ hai cũng có những thay đổi tương tự button thứ nhất, cách dễ hiểu là chúng ta sao chép lại đoạn mã của thuộc tính Button.Resources và thêm thuộc tính Style đến button thứ hai như sau:
<Button Style="{DynamicResource buttonStyle}" Panel.ZIndex="1" Content="Button" Margin="0,0,0,0" Name="button2" Width="75" Height="23"> <Button.Resources> <Style x:Key="buttonStyle"> <Setter Property="Button.Background" Value="Gray"/> <Setter Property="Button.Foreground" Value="White"/> <Setter Property="Button.FontFamily" Value="Comic Sans MS"/> </Style> </Button.Resources> </Button> <Button Style="{DynamicResource buttonStyle}" Panel.ZIndex="1" Content="Button" HorizontalAlignment="Left" Margin="354,69,0,0" VerticalAlignment="Top" Width="75"> <Button.Resources> <Style x:Key="buttonStyle"> <Setter Property="Button.Background" Value="Gray"/> <Setter Property="Button.Foreground" Value="White"/> <Setter Property="Button.FontFamily" Value="Comic Sans MS"/> </Style> </Button.Resources> </Button> <Image Panel.ZIndex="0" Margin="0,0,0,0" Name="image1"> <Image.Source> <BitmapImage UriSource="noel.jpg" /> </Image.Source> </Image>
Kết quả:
- Việc sao chép đoạn mã cho những điều khiển có chung một tập thuộc tính (ví dụ hai button của chúng ta ở trên) sẽ dẫn đến sự lặp lại và làm mã XAML của chúng ta trở nên cồng kềnh. Giải pháp cho vấn đề này là thêm phần tử Window.Resources phía trên phần tử Grid – điều này sẽ tác động đến toàn bộ các điều khiển có sử dụng chung một tập các thuộc tính. Như vậy, tập thuộc tính buttonStyle sẽ được định nghĩa trong Window.Resources như sau:
<Window.Resources> <Style x:Key="buttonStyle"> <Setter Property="Button.Background" Value="Gray"/> <Setter Property="Button.Foreground" Value="White"/> <Setter Property="Button.FontFamily" Value="Comic Sans MS"/> </Style> </Window.Resources> <Grid> ....... </Grid>
Các button được chỉnh sửa lại như sau:
<Button Style="{DynamicResource buttonStyle}" Panel.ZIndex="1" Content="Button" Margin="0,0,0,0" Name="button2" Width="75" Height="23"/> <Button Style="{DynamicResource buttonStyle}" Panel.ZIndex="1" Content="Button" HorizontalAlignment="Left" Margin="354,69,0,0" VerticalAlignment="Top" Width="75"/>
Với việc dùng Window.Resources, chúng ta có thể áp dụng tập thuộc tính đến tất cả các điều khiển sử dụng tập thuộc tính đó (ví dụ buttonStyle) trong form thông qua các thuộc tính Style cho mỗi điều khiển. Điều này giúp ta tránh việc lặp lại các đoạn mã nhưng khi khai báo giá trị cho thuộc tính Property của Setter, chúng ta vẫn lặp lại từ Button như Button.Background, Button.Foreground, Button.FontFamily. Điều này có nghĩa rằng, các thuộc tính này là áp dụng cho các button. Chúng ta có thể sử dụng thuộc tính TargetType cho phần tử Style để làm đoạn mã trở nên gọn gàng hơn như sau:
<Style x:Key="buttonStyle" TargetType="Button"> <Setter Property="Background" Value="Gray"/> <Setter Property="Foreground" Value="White"/> <Setter Property="FontFamily" Value="Comic Sans MS"/> </Style>
Lúc này, chúng ta có thể áp dụng tập thuộc tính buttonStyle cho mọi button trên form.
- Với việc sử dụng thuộc tính TargetType như trên, chúng ta có thể áp dụng tập thuộc tính buttonStyle cho tất cả button nhưng còn các điều khiển khác như Textbox, label, v.v. thì sao? Để biết rõ điều này, chúng ta thêm một textbox đến form và mã XAML lúc này như sau:
<TextBox HorizontalAlignment="Left" Height="23" Margin="176,89,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
Thêm thuộc tính Style để áp dụng tập thuộc tính buttonStyle :
<TextBox Style="{DynamicResource buttonStyle}" HorizontalAlignment="Left" Height="23" Margin="176,89,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="120"/>
Lúc này sẽ xuất hiện lỗi trong Window.Resources như sau:
Nguyên nhân là tập thuộc tính buttonStyle chỉ áp dụng cho các button và không thể áp dụng cho các điều khiển khác như textbox. Để có thể tạo ra một tập thuộc tính áp dụng cho tất cả các điều khiển (miễn là thừa kế từ lớp System.Control), chúng ta thay đổi thuộc tính TargetResource đến giá trị là Control và để tương ứng chúng ta cũng thay đổi giá trị của Key thành bellRingersStyle (vì chúng ta dùng cho mọi điều khiển trong ứng dụng BellRingers):
<Window.Resources> <Style x:Key="bellRingersStyle" TargetType="Control"> <Setter Property="Background" Value="Gray"/> <Setter Property="Foreground" Value="White"/> <Setter Property="FontFamily" Value="Comic Sans MS"/> </Style> </Window.Resources>
Các button và textbox cũng thay đổi giá trị của thuộc tính Style như sau:
<Button Style="{DynamicResource bellRingersStyle}".../> <Button Style="{DynamicResource bellRingersStyle}".../> <TextBox Style="{DynamicResource bellRingersStyle}".../>
Kết quả:
Để kết thúc phần thực hành này chúng ta sẽ tìm hiểu thêm phần tử <Style.Triggers>. Theo trên, textbox của chúng ta có màu xám (Gray) và chúng ta muốn khi đưa con trỏ chuột vào textbox, nó sẽ chuyển sang màu xanh da trời (blue) như sau:
Bằng cách sử dụng <Style.Triggers>, chúng ta sẽ kích hoạt sự kiện (event) MouseOver của textbox thông qua phần tử Trigger và cặp thuộc tính Property có giá trị là IsMouseOver và Value có giá trị là True; thêm thuộc tính Background (giá trị của Property) với giá trị là Blue (giá trị của Value) khi sự kiện xảy ra thông qua phần tử Setter. Đoạn mã XAML của phần tử <Style.Triggers> được thêm vào phần tử Window.Resources như sau:
<Window.Resources> <Style x:Key="bellRingersStyle" TargetType="Control"> <Setter Property="Background" Value="Gray"/> <Setter Property="Foreground" Value="White"/> <Setter Property="FontFamily" Value="Comic Sans MS"/> <Style.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="Background" Value="Blue" /> </Trigger> </Style.Triggers> </Style>
Thêm các điều khiển đến form
Cho đến lúc này, chúng ta đã biết cách tạo form, thay đổi thuộc tính của form và một vài điều khiển bằng cả hai cách là dùng cửa sổ Properties và viết mã XAML. Trong phần thực hành kế tiếp chúng ta sẽ làm quen với các điều khiển như checkbox, listbox, combobox, v.v.
Sử dụng các điều khiển
Xoá tất cả các điều khiển gồm hai button, một textbox và một Image chúng ta đã thêm vào form từ các bài thực hành trước.
Thay đổi kích cỡ của form qua các thuộc tính Height và Width của phần tử Window:
<Window x:Class="BellRingers.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Middleshire Bell Ringers Association Members" Height="470" Width="600"> .... </Window>
Kéo một Label từ Toolbox và đặt gần góc trái trên của form. Thay đổi thuộc tính Content của label thành First Name.
Thêm một textbox phía bên phải label và thay đổi thuộc tính Name của textbox thành firstName.
Thêm một label thứ hai vào vị trí bên phải của textbox (cùng hàng) và thay đổi thuộc tính Content thành Last Name.
Thêm một textbox phía bên phải label Last Name (cùng hàng) và thay đổi thuộc tính Name thành lastName. Kết quả form lúc này trông như sau:
Thêm một label thứ 3 ngay dưới label First Name và thay đổi Content cho label thứ 3 này thành Tower.
Thêm một Combobox đặt ngay dưới textbox firstName và bên phải label Tower. Thay đổi thuộc tính Name của combobox thành towerNames
Thêm một checkbox đặt ngay dưới textbox lastName và bên phải combobox towerNames. Thay đổi thuộc tính Name của checkbox thành isCaptain, thuộc tính Content thành Captain
Thêm một label thứ tư đặt ngay dưới label Tower và thay đổi thuộc tính Content thành Member Since
Trong thanh Toolbox, tại mục All WPF Controls, tìm đến DatePicker và thêm nó đến form. DatePicker được đặt ngay dưới combobox towerNames, bên phải label Member Since và thuộc tính Name được thay đổi thành memberSince.
Thêm một GroupBox, chọn từ Toolbox tại All WPF Controls, đến form và đặt ngay dưới label Member Since. Thay đổi thuộc tính Name cùa GroupBox thành yearsExperience, thuộc tính Header của GroupBox thành Experience và thuộc tính Height thành 200
Thêm một StackPanel đến form. Trong khung mã XAML, điều chỉnh thuộc tính Margin của StackPanel thành (0,0,0,0). Xoá tất cả các thuộc tính của StackPanel chỉ giữ lại thuộc tính Margin và Name như sau:
<StackPanel Margin="0,0,0,0" Name="stackPanel1" />
Trong khung XAML, thay đổi Groupbox và đặt phần tử StackPanel vào trong GroupBox như sau:
<GroupBox Name ="yearsExperience" Header="Experience" Height="200"...> <StackPanel Margin="0,0,0,0" Name="stackPanel1" /> </GroupBox>
Thêm 4 RadioButton đến form và đặt trong StackPanel. Để chắc chắn rằng chúng ta đã đặt các RadioButton này trong StackPanel, chúng ta có thể xem và điều chỉnh mã XAML như sau:
<GroupBox Name ="yearsExperience" Header="Experience" Height="200"...> <StackPanel Margin="0,0,0,0" Name="stackPanel1" > <RadioButton Content="RadioButton"/> <RadioButton Content="RadioButton"/> <RadioButton Content="RadioButton"/> <RadioButton Content="RadioButton"/> </StackPanel> </GroupBox>
Thêm các thuộc tính Name và thay đổi thuộc tính Content cho từng RadioButton như sau:
<GroupBox Name ="yearsExperience" Header="Experience" Height="200"...> <StackPanel Margin="0,0,0,0" Name="stackPanel1" > <RadioButton Content="Up to 1 year" Name="novice"/> <RadioButton Content="1 to 4 years" Name="intermediate"/> <RadioButton Content="5 to 9 years" Name="experienced"/> <RadioButton Content="10 or more years" Name="accomplished"/> </StackPanel> </GroupBox>
Thêm một ListBox đến form và đặt bên phải GroupBox. Thay đổi thuộc tính Name của ListBox thành methods
Thêm một button vào form, đặt gần góc trái dưới của form và phía dưới GroupBox. Thay đổi thuộc tính Name của button thành add và thuộc tính Content thành Add.
Thêm một button khác và đặt bên phải button Add. Thay đổi thuộc tính Name của button này thành clear và thuộc tính Content thành Clear.
Lúc này, form của chúng ta sẽ trông như sau:
Tạo và ứng dụng tập thuộc tính đến các điều khiển bằng cách bổ sung thêm tập thuộc tính sau:
<Window.Resources> <Style x:Key="bellRingersFontStyle" TargetType="Control"> <Setter Property="FontFamily" Value="Comic Sans MS"/> </Style> <Style x:Key="bellRingersStyle" TargetType="Control"> ... </Style> </Window.Resources>
Lúc này chúng ta có hai tập thuộc tính là bellRingersFontStyle và bellRingersStyle. Việc áp dụng thuộc tính nào chúng ta có thể dùng thuộc tính Style cho điều khiển tương ứng hoặc dùng cửa sổ Properties.
Trong phần thực hành này chúng ta sẽ áp dụng tập thuộc tính bellRingersFontStyle cho label First Name bằng cách chọn label và mở cửa sổ Properties chọn thuộc tính Style trong mục Miscellaneous (Arrange by:Category hay gõ Style trong ô Search Properties). Tại Style, chọn biểu tượng ô vuông bên phải và chọn Local Resource chọn bellRingersFontStyle
Với các điều khiển như label chúng ta muốn các thuộc tính là không đổi trong quá trình thực thi nên có thể áp dụng StaticResource. Trong mã XAML, chọn thuộc tính Style và thay đổi từ DynamicResource thành StaticResource.
<Label Content="First Name" Style="{StaticResource bellRingersFontStyle}".../>
Tương tự label First Name, áp dụng tập thuộc tính bellRingersFontStyle và StaticResource đến các label Last Name, Tower, Member Since, checkbox Captain, combobox towerNames, groupbox yearExperience, listbox methods.
Áp dụng tập thuộc tính bellRingersStyle đến các textbox firstName, lastName, các button add, clear (dùng DynamicResource). Form của chúng ta lúc này như sau:
Viết code C# cho các điều khiển
Chúng ta đã thêm các điều khiển đến form và cũng đã tạo và áp dụng tập thuộc tính để định dạng cho các điểu khiển này. Trong phần thực hành kế tiếp chúng ta sẽ làm quen với cách viết mã C# cho các điều khiển và cũng sẽ làm quen với xử lý sự kiện.
Khởi tạo giá trị cho các điều khiển
Để có thể viết mã C# cho các điều khiển chúng ta nhấp chuột phải vào form và chọn View Code. Thêm phương thức Reset() đến lớp MainWindow như sau:
namespace BellRingers { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } public void Reset() { firstName.Text = String.Empty; lastName.Text = String.Empty; } } }
Phương thức Reset sẽ khởi tạo chuỗi rỗng cho các textbox firstName và lastName.
Chúng ta cũng khởi tạo giá trị cho combobox towerNames và listbox methods. Để khởi tạo giá trị cho combobox, chúng ta cần tạo một mảng towers kiểu string chứa các giá trị muốn thêm đến combobox. Thêm đoạn mã khai báo mảng towers đến lớp MainWindow và phía trên phương thức khởi tạo MainWindow():
private string[] towers = { "Great Shevington", "Little Mudford", "Upper Gumtree", "Downley Hatch" };
Dùng vòng lặp foreach để duyệt qua từng phần tử trong mảng towers và gán từng phần tử trong mảng đến combobox thông qua phương thức Add. Thêm đoạn mã sau đến phương thức Reset:
towerNames.Items.Clear(); foreach (string towerName in towers) { towerNames.Items.Add(towerName); } towerNames.Text = towerNames.Items[0] as string;
Tương tự, để gán giá trị đến listbox, chúng ta tạo một mảng ringingMethods kiểu string đến lớp MainWindow như sau:
private string[] ringingMethods = { "Plain Bob", "Reverse Canterbury", "Grandsire", "Stedman", "Kent Treble Bob", "Old Oxford Delight", "Winchendon Place", "Norwich Surprise", "Crayford Little Court" };
Dùng vòng lặp foreach và phương thức Add để thêm giá trị đến listbox. Thêm đoạn mã sau đến phương thức Reset và lưu ý rằng, mỗi giá trị của mảng sẽ được gán đến nội dung của thuộc tính Content cho một đối tượng checkbox:
methods.Items.Clear(); CheckBox method = null; foreach (string methodName in ringingMethods) { method = new CheckBox(); method.Margin = new Thickness(0, 0, 0, 10); method.Content = methodName; methods.Items.Add(method); }
Checkbox isCaptain mặc định là chưa được chọn hay giá trị của thuộc tính isChecked của nó có giá trị false. Thêm đoạn mã sau đến phương thức Reset():
isCaptain.IsChecked = false;
Các radiobutton trong Groupbox mặc định là chưa được chọn. Chúng ta có thể gán một radiobutton, ví dụ novice, được chọn bằng cách gán giá trị true đến thuộc tính isChecked. Thêm đoạn mã sau đến Reset():
novice.IsChecked = true;
Chúng ta cũng muốn điều khiển DatePicker memberSince nhận giá trị mặc định là ngày hiện tại bằng cách thêm đoạn mã sau đến phương thức Reset():
Lúc này, những điều khiển trong form của chúng ta đều đã được khởi tạo trong phương thức Reset(). Để những giá trị này có thể được áp dụng đến các điều khiển tương ứng, chúng ta phải gọi phương thức Reset() trong phương thức khởi tạo MainWindow():
public MainWindow() { InitializeComponent(); this.Reset(); }
Dùng từ khoá this để chỉ lớp form hiện tại (lớp MainWindow).
Chạy ứng dụng và from chúng ta lúc này như sau:
Xử lý sự kiện trong form WPF
Chúng ta đã viết một ít mã khởi tạo cho các điều khiển trong phương thức Reset() và gọi phương thức này trong phương thức khởi tạo MainWindow(). Tuy nhiên, hai button Add và Clear vẫn chưa có chức năng gì.
Trong bài trước, chúng ta đã làm quen khái niệm sự kiện (event) và xử lý sự kiện (event handling). Ứng dụng WPF cung cấp các sự kiện được định nghĩa sẵn như Click, MouseOver, v.v. cho các form và điều khiển. Ví dụ với button Add, chúng ta có thể xem danh sách các sự kiện bằng cách vào cửa sổ Properties và chọn biểu tượng tia chớp:
Chúng ta thấy button add có rất nhiều sự kiện như Click, DragOver, v.v.
Xử lý sự kiện tức là viết phương thức sẽ thực thi khi sự kiện xảy ra. Ví dụ chúng ta có phương thức Display hiển thị thông điệp như sau:
private void Display(object sender, RoutedEventArgs e) { MessageBox.Show("Hello"); }
Chúng ta muốn gọi phương thức Display khi sự kiện Click của button Add xảy ra, có hai cách:
– Cách thứ nhất: gõ tên phương thức Display tại sự kiện Click trong cửa sổ Properties:
– Cách thứ hai: Dùng thuộc tính Click trong XAML tại phần tử button add:
<Button Name="add" Click="Display" .../>
Thực hành xử lý sự kiện
Chúng ta xử lý sự kiện Click của button add như sau (gọi phương thức Display chỉ là ví dụ):
Tại khung XAML tìm đến phần tử button add, tại thuộc tính Click xoá Display và gõ lại dấu nháy kép sẽ xuất hiện <New Event Handler> . Chọn <New Event Handler> sẽ xuất hiện phương thức add_Click_1. Phương thức add_Click_1 tự động được thêm vào cửa sổ Code View như sau:
private void add_Click_1(object sender, RoutedEventArgs e) { }
Có thể điều chỉnh lại tên phương thức, ví dụ add_Click, và cập nhật lại thuộc tính Click trong khung XAML.
Thêm đoạn mã sau vào phương thức add_Click:
private void add_Click(object sender, RoutedEventArgs e) { string nameAndTower = String.Format( "Member name: {0} {1} from the tower at {2} rings the following methods:", firstName.Text, lastName.Text, towerNames.Text); StringBuilder details = new StringBuilder(); details.AppendLine(nameAndTower); foreach (CheckBox cb in methods.Items) { if (cb.IsChecked.Value) { details.AppendLine(cb.Content.ToString()); } } MessageBox.Show(details.ToString(), "Member Information"); }
Đoạn mã trên thêm một thành viên mới và hiển thị thông tin qua MessageBox.
Tương tự, thêm phương thức xử lý sự kiện clear_Click đến button Clear. Phương thức này đơn giản chỉ gọi phương thức Reset() để gán các giá trị mặc định cho các điều khiển:
private void clear_Click(object sender, RoutedEventArgs e) { this.Reset(); }
Khi đóng form chúng ta muốn hiển thị một thông điệp cảnh báo người dùng bằng cách thêm phương thức xử lý sự kiện Closing cho form. Trong XAML, tìm đến phần tử Window và thêm thuộc tính Closing:
<Window x:Class="BellRingers.MainWindow"...Closing="Window_Closing">
Thêm đoạn mã sau đến phương thức Window_Closing:
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) { MessageBoxResult key = MessageBox.Show ("Are you sure you want to quit", "Confirm", MessageBoxButton.YesNo, MessageBoxImage.Question, MessageBoxResult.No); e.Cancel = (key == MessageBoxResult.No); }
Lưu tất cả. Thực thi ứng dụng và thêm thành viên mới:
Nhấn nút Add:
Nhấn của hộp thoại Member Information. Nhấn nút Clear để trả về giá trị mặc định cho các điều khiển. Nhấn nút đóng form:
Nhấn Yes để đóng form.