ASP.NET Core: Bố cục đáp ứng với XAML
Hệ thống sắp xếp cục bộ XAML cung cấp khả năng xác định kích thước tự động của các thành phần, bảng bố cục và trạng thái trực quan để giúp bạn tạo giao diện người dùng đáp ứng. Với bố cục đáp ứng, bạn có thể làm cho ứng dụng của mình trông đẹp mắt trên màn hình với các kích thước, độ phân giải, mật độ pixel và hướng cửa sổ ứng dụng khác nhau. Bạn có thể sử dụng XAML để định vị lại, thay đổi kích thước, chỉnh sửa lại dòng, hiển thị/ẩn, thay thế hoặc kiến trúc lại giao diện người dùng của ứng dụng, như đã thảo luận trong Kỹ thuật thiết kế đáp ứng. Ở đây, chúng ta thảo luận về cách phát triển bố cục đáp ứng với XAML.
Bố cục linh hoạt với các property và bảng điều khiển
Nền tảng của bố cục đáp ứng là việc sử dụng phù hợp với các property và bảng điều khiển bố cục XAML để định vị lại, thay đổi kích thước và chỉnh sửa lại nội dung theo cách linh hoạt.
Hệ thống bố cục XAML hỗ trợ cả bố cục tĩnh và linh hoạt. Trong bố cục tĩnh, bạn cung cấp cho các điều khiển kích thước và vị trí pixel rõ ràng. Khi người dùng thay đổi độ phân giải hoặc hướng của thiết bị, giao diện người dùng không thay đổi. Bố cục tĩnh có thể được cắt bớt trên các yếu tố hình thức và kích thước hiển thị khác nhau. Mặt khác, bố cục linh hoạt thu nhỏ, phát triển và chỉnh lại dòng để đáp ứng với không gian trực quan có sẵn trên thiết bị.
Trong thực tế, bạn sử dụng kết hợp các yếu tố tĩnh và linh hoạt để tạo giao diện người dùng của mình. Bạn vẫn sử dụng các phần tử và giá trị tĩnh ở một số nơi, nhưng hãy đảm bảo rằng giao diện người dùng tổng thể đáp ứng các độ phân giải, kích thước màn hình và chế độ xem khác nhau.
Ở đây, chúng ta thảo luận về cách sử dụng các property XAML và bảng bố cục để tạo bố cục linh hoạt.
Các property bố cục
Các property bố cục kiểm soát kích thước và vị trí của một phần tử. Để tạo bố cục linh hoạt, hãy sử dụng định cỡ tự động hoặc theo tỷ lệ cho các thành phần và cho phép các bảng bố cục định vị các phần tử con của chúng khi cần.
Dưới đây là một số property bố cục phổ biến và cách sử dụng chúng để tạo bố cục linh hoạt.
Height và Width
Các property Height và Width chỉ định kích thước của một phần tử. Bạn có thể sử dụng các giá trị cố định được đo bằng pixel hiệu dụng hoặc bạn có thể sử dụng định cỡ tự động hoặc định cỡ theo tỷ lệ.
Tự động thay đổi kích thước các phần tử giao diện người dùng để phù hợp với nội dung hoặc vùng chứa chính của chúng. Bạn cũng có thể sử dụng định cỡ tự động với các hàng và cột của lưới. Để sử dụng kích thước tự động, hãy đặt Height và/hoặc Width của các phần tử giao diện người dùng thành Auto.
Ghi chú
Việc một phần tử thay đổi kích thước theo nội dung hoặc vùng chứa của nó phụ thuộc vào cách vùng chứa chính xử lý kích thước của các phần tử con của nó. Để biết thêm thông tin, hãy xem bảng Bố cục ở phần sau của bài viết này.
Định cỡ theo tỷ lệ, còn được gọi là định cỡ theo sao (star sizing), nó phân phối không gian có sẵn giữa các hàng và cột của lưới theo tỷ lệ trọng số. Trong XAML, các giá trị sao được biểu thị bằng * (hoặc n* đối với định cỡ sao có trọng số). Ví dụ: để chỉ định rằng một cột rộng hơn 5 lần so với cột thứ hai trong bố cục 2 cột, hãy sử dụng "5*" và "*" cho thuộc tính Width trong các phần tử ColumnDefinition.
Ví dụ này kết hợp định cỡ cố định, tự động và theo tỷ lệ trong một Lưới (Grid) có 4 cột.
Cột | Định cỡ | Mô tả |
---|---|---|
Cột_1 | Auto | Cột sẽ có kích thước phù hợp với nội dung của nó. |
Cột_2 | * | Sau khi cột Auto được tính toán, cột sẽ nhận được một phần chiều rộng còn lại. Cột_2 sẽ rộng bằng một nửa so với Cột_4. |
Cột_3 | 44 | Cột sẽ rộng 44 pixel. |
Cột_4 | 2* | Sau khi cột Auto được tính toán, cột sẽ nhận được một phần chiều rộng còn lại. Cột_4 sẽ rộng gấp đôi Cột_2. |
Chiều rộng cột mặc định là "*", vì vậy bạn không cần đặt rõ ràng giá trị này cho cột thứ hai.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
<ColumnDefinition Width="44"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Column 1 sizes to its content." FontSize="24"/>
</Grid>
Trong trình thiết kế Visual Studio XAML, kết quả sẽ như thế này.
Để lấy kích thước của một phần tử trong thời gian chạy, hãy sử dụng các property ActualHeight và ActualWidth chỉ đọc thay vì Height và Width.
Ràng buộc về kích thước
Khi bạn sử dụng kích thước tự động trong giao diện người dùng của mình, bạn vẫn có thể cần đặt các ràng buộc về kích thước của một phần tử. Bạn có thể đặt các property MinWidth/MaxWidth và MinHeight/MaxHeight để chỉ định các giá trị ràng buộc kích thước của một phần tử trong khi cho phép thay đổi kích thước linh hoạt.
Trong Lưới, MinWidth/MaxWidth cũng có thể được sử dụng với định nghĩa cột và MinHeight/MaxHeight có thể được sử dụng với định nghĩa hàng.
Căn chỉnh
Sử dụng các thuộc tính HorizontalAlignment và VerticalAlignment để chỉ định cách định vị một phần tử bên trong vùng chứa chính của nó.
- Các giá trị cho HorizontalAlignment là Left, Center, Right và Stretch.
- Các giá trị cho VerticalAlignment là Top, Center, Bottom và Stretch.
Với căn chỉnh Stretch, các phần tử sẽ lấp đầy tất cả khoảng trống mà chúng được cung cấp trong vùng chứa chính. Stretch là mặc định cho cả hai thuộc tính căn chỉnh. Tuy nhiên, có một số điều khiển chẳng hạn như Button, sẽ ghi đè giá trị này theo kiểu mặc định của chúng. Bất kỳ phần tử nào có thể có các phần tử con đều có thể xử lý giá trị Stretch cho các thuộc tính HorizontalAlignment và VerticalAlignment một cách duy nhất. Ví dụ: một phần tử sử dụng các giá trị Stretch mặc định được đặt trong Lưới kéo dài để lấp đầy ô chứa nó. Phần tử tương tự được đặt trong Canvas có kích thước phù hợp với nội dung của nó. Để biết thêm thông tin về cách mỗi bảng xử lý giá trị Stretch, hãy xem bài viết Bố cục bảng.
Để biết thêm thông tin, hãy xem bài viết Căn chỉnh, margin và padding cũng như các trang tham khảo HorizontalAlignment và VerticalAlignment.
Visibility
Bạn có thể hiển thị hoặc ẩn một phần tử bằng cách đặt property Visibility của nó thành một trong các giá trị liệt kê Visibility: Visible hoặc Collapsed. Khi một phần tử được Collapsed, nó sẽ không chiếm bất kỳ khoảng trống nào trong bố cục giao diện người dùng.
Bạn có thể thay đổi property Visibility của phần tử trong code hoặc ở trạng thái trực quan. Khi Visibility của một phần tử bị thay đổi, tất cả các phần tử con của nó cũng bị thay đổi. Bạn có thể thay thế các phần của giao diện người dùng bằng cách hiển thị một bảng trong khi thu gọn một bảng khác.
Mẹo
Khi bạn có các thành phần trong giao diện người dùng được Collapsed theo mặc định, các đối tượng vẫn được tạo khi khởi động, mặc dù chúng không hiển thị. Bạn có thể trì hoãn việc tải các phần tử này cho đến khi chúng được hiển thị bằng cách sử dụng attribute x:Load để trì hoãn việc tạo các đối tượng. Điều này có thể cải thiện hiệu suất khởi động. Để biết thêm thông tin, hãy xem attribute x:Load.
Tài nguyên style
Bạn không phải đặt riêng từng giá trị property trên một điều khiển. Thông thường, việc nhóm các giá trị thuộc tính vào tài nguyên Style và áp dụng Style cho một điều khiển sẽ hiệu quả hơn. Điều này đặc biệt đúng khi bạn cần áp dụng các giá trị thuộc tính giống nhau cho nhiều điều khiển. Để biết thêm thông tin về cách sử dụng style, hãy xem Style cho các control.
Bảng bố cục
Để định vị các đối tượng trực quan, bạn phải đặt chúng trong bảng điều khiển hoặc đối tượng chứa khác. Framework XAML cung cấp nhiều lớp bảng điều khiển khác nhau, chẳng hạn như Canvas, Grid, RelativePanel và StackPanel, đóng vai trò là bộ chứa và cho phép bạn định vị cũng như sắp xếp các phần tử giao diện người dùng bên trong chúng.
Điều chính cần xem xét khi chọn bảng bố cục là cách bảng định vị và kích thước các thành phần con của nó. Bạn cũng có thể cần xem xét cách các phần tử con chồng chéo được xếp chồng lên nhau.
Dưới đây là so sánh các tính năng chính của bảng điều khiển được cung cấp trong khung XAML.
Bảng điều khiển | Mô tả |
---|---|
Canvas | Canvas không hỗ trợ giao diện người dùng linh hoạt; bạn kiểm soát tất cả các khía cạnh của việc định vị và định cỡ các phần tử con. Bạn thường sử dụng nó cho các trường hợp đặc biệt như tạo đồ họa hoặc để xác định các vùng tĩnh nhỏ của giao diện người dùng thích ứng lớn hơn. Bạn có thể sử dụng mã hoặc trạng thái trực quan để định vị lại các phần tử trong thời gian chạy.
|
Grid | Grid hỗ trợ thay đổi kích thước linh hoạt của các phần tử con. Bạn có thể sử dụng code hoặc trạng thái trực quan để định vị lại và chỉnh lại các phần tử.
|
RelativePanel |
|
StackPanel |
|
VariableSizedWrapGrid |
|
Để biết thông tin chi tiết và ví dụ về các bảng này, hãy xem Bảng bố cục.
Bảng bố cục cho phép bạn sắp xếp giao diện người dùng của mình thành các nhóm điều khiển hợp lý. Khi bạn sử dụng chúng với cài đặt thuộc tính phù hợp, bạn sẽ nhận được một số hỗ trợ để tự động thay đổi kích thước, định vị lại và chỉnh lại dòng của các thành phần giao diện người dùng. Tuy nhiên, hầu hết các bố cục giao diện người dùng cần sửa đổi thêm khi có những thay đổi đáng kể đối với kích thước cửa sổ. Đối với điều này, bạn có thể sử dụng các trạng thái trực quan.
Bố cục thích ứng với trạng thái trực quan và trình kích hoạt trạng thái
Sử dụng các trạng thái trực quan để thực hiện các thay đổi quan trọng đối với giao diện người dùng của bạn dựa trên kích thước cửa sổ hoặc các thay đổi khác.
Khi cửa sổ ứng dụng của bạn mở rộng hoặc thu nhỏ vượt quá một lượng nhất định, bạn có thể muốn thay đổi thuộc tính bố cục để định vị lại, thay đổi kích thước, chỉnh lại dòng, hiển thị hoặc thay thế các phần trong giao diện người dùng của mình. Bạn có thể xác định các trạng thái trực quan khác nhau cho giao diện người dùng của mình và áp dụng chúng khi chiều rộng cửa sổ hoặc chiều cao cửa sổ vượt qua ngưỡng đã chỉ định.
VisualState xác định các giá trị thuộc tính được áp dụng cho một phần tử khi nó ở một trạng thái cụ thể. Bạn nhóm các trạng thái trực quan trong VisualStateManager áp dụng VisualState thích hợp khi các điều kiện đã chỉ định được đáp ứng. AdaptiveTrigger cung cấp một cách dễ dàng để đặt ngưỡng (còn được gọi là 'điểm dừng') nơi một trạng thái được áp dụng trong XAML. Hoặc, bạn có thể gọi phương thức VisualStateManager.GoToState trong mã của mình để áp dụng trạng thái trực quan. Ví dụ về cả hai cách được hiển thị trong các phần tiếp theo.
Đặt trạng thái trực quan trong code
Để áp dụng trạng thái trực quan từ mã, bạn gọi phương thức VisualStateManager.GoToState. Ví dụ: để áp dụng trạng thái khi cửa sổ ứng dụng có kích thước cụ thể, hãy xử lý sự kiện SizeChanged và gọi GoToState để áp dụng trạng thái phù hợp.
Ở đây, một VisualStateGroup chứa hai định nghĩa VisualState. Cái đầu tiên, DefaultState
là trống. Khi được áp dụng, các giá trị được xác định trong trang XAML sẽ được áp dụng. Thứ hai, WideState
, thay đổi property DisplayMode của SplitView thành Inline và mở ngăn. Trạng thái này được áp dụng trong trình xử lý sự kiện SizeChanged nếu chiều rộng cửa sổ lớn hơn 640 pixel hiệu quả.
Ghi chú
Windows không cung cấp cách để ứng dụng của bạn phát hiện thiết bị cụ thể mà ứng dụng của bạn đang chạy trên đó. Nó có thể cho bạn biết dòng thiết bị (máy tính để bàn, v.v.) mà ứng dụng đang chạy, độ phân giải hiệu dụng và dung lượng màn hình có sẵn cho ứng dụng (kích thước cửa sổ của ứng dụng). Chúng tôi khuyên bạn nên xác định trạng thái trực quan cho kích thước màn hình và điểm ngắt.
<Page ...
SizeChanged="CurrentWindow_SizeChanged">
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState x:Name="DefaultState">
<Storyboard>
</Storyboard>
</VisualState>
<VisualState x:Name="WideState">
<Storyboard>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="SplitView.DisplayMode"
Storyboard.TargetName="mySplitView">
<DiscreteObjectKeyFrame KeyTime="0">
<DiscreteObjectKeyFrame.Value>
<SplitViewDisplayMode>Inline</SplitViewDisplayMode>
</DiscreteObjectKeyFrame.Value>
</DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="SplitView.IsPaneOpen"
Storyboard.TargetName="mySplitView">
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<SplitView x:Name="mySplitView" DisplayMode="CompactInline"
IsPaneOpen="False" CompactPaneLength="20">
<!-- SplitView content -->
<SplitView.Pane>
<!-- Pane content -->
</SplitView.Pane>
</SplitView>
</Grid>
</Page>
private void CurrentWindow_SizeChanged(object sender, Windows.UI.Core.WindowSizeChangedEventArgs e)
{
if (e.Size.Width > 640)
VisualStateManager.GoToState(this, "WideState", false);
else
VisualStateManager.GoToState(this, "DefaultState", false);
}
Đặt trạng thái trực quan trong đánh dấu XAML
Trước Windows 10, các định nghĩa VisualState yêu cầu các đối tượng Bảng phân cảnh để thay đổi thuộc tính và bạn phải gọi mã GoToState để áp dụng trạng thái. Điều này được thể hiện trong ví dụ trước. Bạn vẫn sẽ thấy nhiều ví dụ sử dụng cú pháp này hoặc bạn có thể có mã hiện có sử dụng nó.
Bắt đầu từ Windows 10, bạn có thể sử dụng cú pháp Setter đơn giản hóa được hiển thị ở đây và bạn có thể sử dụng StateTrigger trong đánh dấu XAML của mình để áp dụng trạng thái. Bạn sử dụng trình kích hoạt trạng thái để tạo các quy tắc đơn giản tự động kích hoạt các thay đổi trạng thái trực quan để phản hồi một sự kiện trong ứng dụng.
Ví dụ này thực hiện giống như ví dụ trước, nhưng sử dụng cú pháp Setter được đơn giản hóa thay vì Bảng phân cảnh để xác định các thay đổi thuộc tính. Và thay vì gọi GoToState, nó sử dụng trình kích hoạt trạng thái AdaptiveTrigger tích hợp sẵn để áp dụng trạng thái. Khi bạn sử dụng trình kích hoạt trạng thái, bạn không cần xác định tệp DefaultState
. Cài đặt mặc định được tự động áp dụng lại khi các điều kiện của trình kích hoạt trạng thái không còn được đáp ứng.
<Page ...>
<Grid>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup>
<VisualState>
<VisualState.StateTriggers>
<!-- VisualState to be triggered when the
window width is >=640 effective pixels. -->
<AdaptiveTrigger MinWindowWidth="640" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="mySplitView.DisplayMode" Value="Inline"/>
<Setter Target="mySplitView.IsPaneOpen" Value="True"/>
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<SplitView x:Name="mySplitView" DisplayMode="CompactInline"
IsPaneOpen="False" CompactPaneLength="20">
<!-- SplitView content -->
<SplitView.Pane>
<!-- Pane content -->
</SplitView.Pane>
</SplitView>
</Grid>
</Page>
Quan trọng
Trong ví dụ trước, thuộc tính đính kèm VisualStateManager.VisualStateGroups được đặt trên phần tử Grid. Khi bạn sử dụng StateTriggers, hãy luôn đảm bảo rằng VisualStateGroups được gắn vào phần tử con đầu tiên của thư mục gốc để các trình kích hoạt tự động có hiệu lực. (Ở đây, Lưới là phần tử con đầu tiên của phần tử Trang gốc.)
Cú pháp thuộc tính đính kèm
Trong VisualState, bạn thường đặt giá trị cho thuộc tính điều khiển hoặc cho một trong các thuộc tính đính kèm của bảng điều khiển có chứa điều khiển. Khi bạn đặt thuộc tính được đính kèm, hãy sử dụng dấu ngoặc đơn xung quanh tên thuộc tính được đính kèm.
Ví dụ này cho thấy cách đặt thuộc tính đính kèm RelativePanel.AlignHorizontalCenterWithPanel trên Hộp văn bản có tên myTextBox
. XAML đầu tiên sử dụng cú pháp ObjectAnimationUsingKeyFrames và XAML thứ hai sử dụng cú pháp Setter.
<!-- Set an attached property using ObjectAnimationUsingKeyFrames. -->
<ObjectAnimationUsingKeyFrames
Storyboard.TargetProperty="(RelativePanel.AlignHorizontalCenterWithPanel)"
Storyboard.TargetName="myTextBox">
<DiscreteObjectKeyFrame KeyTime="0" Value="True"/>
</ObjectAnimationUsingKeyFrames>
<!-- Set an attached property using Setter. -->
<Setter Target="myTextBox.(RelativePanel.AlignHorizontalCenterWithPanel)" Value="True"/>
Trình kích hoạt trạng thái tùy chỉnh
Bạn có thể mở rộng lớp StateTrigger để tạo các trình kích hoạt tùy chỉnh cho nhiều tình huống khác nhau. Ví dụ: bạn có thể tạo StateTrigger để kích hoạt các trạng thái khác nhau dựa trên loại đầu vào, sau đó tăng lề xung quanh điều khiển khi loại đầu vào là cảm ứng. Hoặc tạo một StateTrigger để áp dụng các trạng thái khác nhau dựa trên dòng thiết bị mà ứng dụng được chạy trên đó. Để biết ví dụ về cách tạo trình kích hoạt tùy chỉnh và sử dụng chúng để tạo trải nghiệm giao diện người dùng được tối ưu hóa từ trong một chế độ xem XAML duy nhất, hãy xem mẫu Trình kích hoạt trạng thái.
Trạng thái và style trực quan
Bạn có thể sử dụng tài nguyên Style ở trạng thái trực quan để áp dụng một tập hợp các thay đổi thuộc tính cho nhiều điều khiển. Để biết thêm thông tin về cách sử dụng kiểu, hãy xem Styling cho các control.
Trong XAML được đơn giản hóa từ mẫu trình kích hoạt trạng thái, tài nguyên Style được áp dụng cho Button để điều chỉnh kích thước và lề cho đầu vào chuột hoặc ứng dụng cảm ứng. Để biết mã hoàn chỉnh và định nghĩa của tùy chỉnh kích hoạt trạng thái kích hoạt, vui lòng xem mẫu Kích hoạt trạng thái.
<Page ... >
<Page.Resources>
<!-- Styles to be used for mouse vs. touch/pen hit targets -->
<Style x:Key="MouseStyle" TargetType="Rectangle">
<Setter Property="Margin" Value="5" />
<Setter Property="Height" Value="20" />
<Setter Property="Width" Value="20" />
</Style>
<Style x:Key="TouchPenStyle" TargetType="Rectangle">
<Setter Property="Margin" Value="15" />
<Setter Property="Height" Value="40" />
<Setter Property="Width" Value="40" />
</Style>
</Page.Resources>
<RelativePanel>
<!-- ... -->
<Button Content="Color Palette Button" x:Name="MenuButton">
<Button.Flyout>
<Flyout Placement="Bottom">
<RelativePanel>
<Rectangle Name="BlueRect" Fill="Blue"/>
<Rectangle Name="GreenRect" Fill="Green" RelativePanel.RightOf="BlueRect" />
<!-- ... -->
</RelativePanel>
</Flyout>
</Button.Flyout>
</Button>
<!-- ... -->
</RelativePanel>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="InputTypeStates">
<!-- Second set of VisualStates for building responsive UI optimized for input type.
Take a look at InputTypeTrigger.cs class in CustomTriggers folder to see how this is implemented. -->
<VisualState>
<VisualState.StateTriggers>
<!-- This trigger indicates that this VisualState is to be applied when MenuButton is invoked using a mouse. -->
<triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Mouse" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="BlueRect.Style" Value="{StaticResource MouseStyle}" />
<Setter Target="GreenRect.Style" Value="{StaticResource MouseStyle}" />
<!-- ... -->
</VisualState.Setters>
</VisualState>
<VisualState>
<VisualState.StateTriggers>
<!-- Multiple trigger statements can be declared in the following way to imply OR usage.
For example, the following statements indicate that this VisualState is to be applied when MenuButton is invoked using Touch OR Pen.-->
<triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Touch" />
<triggers:InputTypeTrigger TargetElement="{x:Bind MenuButton}" PointerType="Pen" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="BlueRect.Style" Value="{StaticResource TouchPenStyle}" />
<Setter Target="GreenRect.Style" Value="{StaticResource TouchPenStyle}" />
<!-- ... -->
</VisualState.Setters>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Page>