WPF – Cơ bản về Layout

Cách bố trí (Layout) là một vấn đề quan trọng và không thể thiếu trong việc thiết kế giao diện. WPF hỗ trợ nhiều kiểu layout tương ứng với mỗi control được gọi là layout container/layout control. Các layout container này được kế thừa từ lớp System.Windows.Controls.Panel và có cách sắp xếp và bố trí các control bên trong nó khác nhau.

Trong bài viết này tôi sẽ giới thiệu sơ lược về các layout control thông dụng nhất. Bao gồm:

  • Canvas
  • StackPanel
  • WrapPanel
  • DockPanel
  • Grid

Canvas

Canvas cho phép bố trí các control bằng cách xác định vị trí cố định của chúng. Sử dụng cách này khiến cho giao diện trở nên thiếu linh hoạt và gây ra nhiều hạn chế, như khi độ phân giải hoặc kích thước cửa sổ thay đổi. Vì thế bạn nên hạn chế dùng control này nếu có thể.

Trong Canvas, bạn gán tọa độ cho các control con thông qua các thuộc tính (attached property) Canvas.Left, Canvas.Right, Canvas.Top, Canvas.Bottom.

Cần lưu ý là Left và Top có độ ưu tiên cao hơn Right và Bottom. Nghĩa là nếu bạn gán giá trị cho cả Canvas.Left và Canvas.Right, khi đó tọa độ theo chiều ngang của control sẽ lấy từ Canvas.Left, tương tự với Canvas.Top và  Canvas.Bottom.

Ví dụ sau cho bạn thấy rõ điều này:

<Window
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Canvas Layout">
  <Canvas Background="White">
      <Button Width="100" Height="100"
              Canvas.Left="20" Canvas.Top="50">Button 1</Button>
      <Button Width="100" Height="100"
              Canvas.Left="100" Canvas.Right="100">Button 2</Button>
      <Button Width="100" Height="100"
              Canvas.Right="150" Canvas.Bottom="50">Button 3</Button>
  </Canvas>
</Window>

Kết quả:

StackPanel

Control này sẽ sắp xếp các control con của nó theo dòng hoặc cột tùy theo giá trị của thuộc tính Orientation là Vertical hay Horizontal. Các control con sẽ được sắp xếp với vị trí liên tiếp và không chồng lên nhau.

Giá trị mặc định của thuộc tính Orientation là Vertical, các control sẽ được sắp xếp theo chiều dọc từ trên xuống dưới. Ngược lại là Horizontal, chúng sẽ được sắp xếp từ trái sang phải.

Ví dụ dưới đây cho thấy hai StackPanel lồng nhau. StackPanel bên ngoài sẽ co giãn khít với kích thước của Window và xếp các control theo chiều dọc và StackPanel bên trong xếp theo chiều ngang:

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="StackPanelLayout" Height="320" Width="300">

    <StackPanel Orientation="Vertical" Background="Azure">
        <Label Background="LightBlue">Orientation="Vertical"</Label>
        <Button>Default</Button>
        <Button HorizontalAlignment="Left">Left</Button>
        <Button HorizontalAlignment="Center">Center</Button>
        <Button HorizontalAlignment="Right">Right</Button>
        <Button HorizontalAlignment="Stretch">Stretch</Button>

        <Label Background="LightBlue">Orientation="Horizontal"</Label>

        <StackPanel Orientation="Horizontal" Background="LightYellow" Height="100">
            <Button>Default</Button>
            <Button VerticalAlignment="Bottom">Bottom</Button>
            <Button VerticalAlignment="Center">Center</Button>
            <Button VerticalAlignment="Top">Top</Button>
            <Button VerticalAlignment="Stretch">Stretch</Button>
        </StackPanel>
    </StackPanel>
</Window>

Kết quả:

WrapPanel

Cũng sắp xếp các control lần lượt theo hàng hoặc cột, nhưng đặc điểm chính của WrapPanel là sẽ tự động cho các control sang hàng/cột mới nếu như kích thước của hàng/cột còn lại không đủ chứa control.

Để thay đổi chiều sắp xếp các control con, bạn sử dụng thuộc tính Orientation với hai giá trị là Horizontal và Vertical; giá trị mặc định là Horizontal.

<WrapPanel Orientation="Horizontal">
      <Button VerticalAlignment="Bottom" Width="100">Bottom</Button>
      <Button VerticalAlignment="Center" Width="50">Center</Button>
      <Button VerticalAlignment="Stretch" Width="90" Height="100">Stretch</Button>
      <Button VerticalAlignment="Top" Width="100" >Top</Button>
      <Button Height="100">Default</Button>
</WrapPanel>

Kết quả:


DockPanel

Khái niệm Dock không lạ lẫm gì nếu bạn đã lập trình GUI trong .NET hoặc Java. Khi sử dụng container này, các control con sẽ được gắn thêm một attached property là DockPanel.Dock cho phép bạn gán các giá trị: Left, Right, Top, Bottom. Các giá trị tương ứng với mỗi phần không gian trong DockPanel, phần còn lại ở giữa sẽ được dùng để chứa các control khác.

Thuộc tính LastChilFill có giá trị mặc định true nhằm xác định các control thêm vào sau sẽ lấp đầy khoảng trống còn lại. Nếu bạn muốn giữ lại khoảng trống này, hãy gán giá trị của nó trở thành false.

<DockPanel LastChildFill="True">
  <Button DockPanel.Dock="Top">Top 1</Button>
  <Button DockPanel.Dock="Right">Right 1</Button>
  <Button DockPanel.Dock="Top">Top 2</Button>
  <Button DockPanel.Dock="Bottom">Bottom 1</Button>
  <Button DockPanel.Dock="Left" VerticalAlignment="Top" Height="50">Left, VAlign="Top"</Button>
  <Button DockPanel.Dock="Bottom" HorizontalAlignment="Left" Height="50"> Bottom 2, HAlign="Left"</Button>
  <Button>Add more controls here</Button>
</DockPanel>

Kết quả:

Grid

Control này sắp xếp các control con trong một lưới giống như table trong html. Grid sẽ chia không gian thành các phần với kích thước tùy ý được ngăn cách bởi cách đường lưới. Mặc định các đường lưới này sẽ không hiển thị, để hiển thị chúng bạn chỉ cần thay đổi giá trị của thuộc tính Grid.ShowGridLines thành true.

Để sử dụng control này, trước tiên bạn cần định nghĩa cấu trúc của Grid thông qua hai collection là Grid.RowDefinitions và Grid.ColumnDefinitions. Với mỗi dòng, cột bạn cần thêm một đối tượng RowDefinition/ColumnDefinition vào collection tương ứng.

Với mỗi dòng/cột, bạn có thể thiết lập kích thước cho chúng bằng cách dùng giá trị cố định, tự động (Auto) hoặc tỷ lệ. Với kiểu Auto, kích thước của ô lưới chỉ dùng vừa đủ để chứa các control bên trong nó.

Mặc dịnh các ô lưới sẽ được chia theo tỷ lệ bằng nhau dựa theo số lượng dòng/cột. Cách chia này này sử dụng dấu sao “*” làm đơn vị tính.

Ví dụ sau chia Grid làm 4 dòng, trong đó dòng 1 và dòng 2 bằng nhau, dòng 3 có kích thước gấp đôi dòng 1. Vì dòng đầu tiên có chiều cao là 100, nên chiều cao của dòng 1 sẽ bằng chiều cao của Grid chia cho 4.

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="50" />
        <RowDefinition Height="*" />
        <RowDefinition Height="*" />
        <RowDefinition Height="2*" />
    </Grid.RowDefinitions>
...
</Grid>

Sau khi đã định nghĩa cấu trúc của Grid, bạn thêm các control vào một ô xác đinh trong lưới thông qua hai attached property là Grid.Row và Grid.Colum, như ví dụ sau:

<Grid ShowGridLines="True">
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="100"/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="*"/>
        <RowDefinition Height="2*"/>
    </Grid.RowDefinitions>
    <Button Grid.Column="1" Grid.Row="0" Height="50">Button 1</Button>
    <Button Grid.Column="0" Grid.Row="1">Button 2</Button>
    <Button Grid.Column="1" Grid.Row="2">Button 3</Button>
    <Button Grid.Column="0" Grid.Row="3">Button 4</Button>
</Grid>

Kết quả:

Việc sử dụng Grid không chỉ đơn thuần như ví dụ trên, vì vậy bạn có thể tìm hiểu chi tiết hơn về layout control này tại: (Đang cập nhật)

https://yinyangit.wordpress.com

 

Advertisements

7 thoughts on “WPF – Cơ bản về Layout

  1. Thanks! Trước đây bạn có nói về vấn đề thiết kế WPF bằng chuột, tuy nhiên đa số dùng mã (XAML, code-behind) hơn là dùng chuột kéo thả, resize. Việc dùng chuột chỉ khi thiết kế những giao diện đơn giản và cố định (thiếu linh hoạt). Việc dùng code sẽ giúp xử lý linh hoạt và tăng kinh nghiệm thiết kế hơn.

    Bạn nghĩ sao về vấn đề này?

    Trả lời
  2. Xin lỗi YinYang trước nhé. Dạo này mình hơi bận nên chưa thử WPF.
    Đúng là cách thiết kế tự code tay sẽ dễ kiểm soát và dễ hơn nhiều khi 1 trang web có nhiều chi tiết (Mình lấy ví dụ bên website).
    Mình vẫn thấy rất lạ lẫm về vấn đề thiết kế bên WPF, có lẽ lối tư duy khác với bên CSS. Cách thiết kế bên WPF hơi khó. Bạn có dự định hướng dẫn mọi người vẽ 1 giao diện hoàn thiện bằng WPF không? (giao diện có thể lấy mẫu của chương trình ExpressionTree bạn đã làm).

    Trả lời
  3. Thực ra CSS chỉ là cách tạo style cho trang web, còn XAML thì tương tự như HTML hay XML. XAML cũng hỗ trợ style, template thay vì tách riêng ra một ngôn ngữ mới (CSS và HTML). Cách thiết kế với XAML đòi hỏi kiến thức nhiều vì nó linh hoạt và nhiều chức năng hơn HTML. Một điểm khó nhất là vận dụng các lớp trong .NET (quá nhiều lớp), kết hợp các thành phần với nhau để tạo giao diện mới,… Vì vậy muốn rành WPF phải thực hành nhiều.

    Trong các dự án sau này mình sẽ thay thế các ứng dụng WinForms bằng WPF, mặc dù không phải tất cả.

    Trả lời

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Log Out / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Log Out / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Log Out / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Log Out / Thay đổi )

Connecting to %s