Cách chuyển đổi giữa các window trong wpf

Trong WPF bạn thiết kế bố cục của ứng dụng bằng các panel container khác nhau.Mỗi panel lại có đặc tính bố trí logic hoàn toàn khác nhau, như StackPanel sắp xếp các control trong nó theo dạng ngăn xếp, Grid sắp xếp theo dạng lưới v.v

1.Tìm hiểu về Layout trong WPF

- Một cửa sổ chỉ có thể chứa duy nhất một điều khiển, để tạo nhiều điều khiển trên giao diện người dùng, chúng ta cần 1 panel container trong cửa sổ và thêm các điều khiển vào panel này.

Hạn chế này nguyên nhân là lớp Window có nguồn gốc từ ContentControl

Trong WPF, bố cục được xác định bởi cách bạn sử dụng các panel container,khi bạn đã nắm được 1 số nguyên tắc cơ bản sử dụng các panel container sau :

- Các điều khiển không nên xác định kích thước rõ ràng,thay vào đó chỉ nên phát triển phù hợp tùy theo nội dung, ví dụ 1 button tự động mở rộng cho phù hợp khi bạn thêm text bằng cách thiết lập kích thước tối đa và tối thiểu. 

- Các điều khiển không được xác định vị trí trên màn hình tọa độ.Thay vào đó, chúng được sắp xếp bằng các panel dựa vào kích thước , thứ tự, các thông tin lựa chọn khác.Nếu cần thêm khoảng trắng giữa các control dùng thuộc tính Margin.

- Panel container chia sẻ không gian trong nó cho các điều khiển.Chúng cung cấp kích thước cho điều khiển dựa trên nội dung của điều khiển , nếu không gian bên trong nó không đủ, chúng sẽ phân phối thêm không gian bên trong chúng cho một hoặc nhiều điều khiển tùy theo giới hạn cho phép chúng.

- Panel container có thể lồng nhiều cấp.Thông thường là dùng gird, một panel container hiệu quả nhất để sắp xếp các nhóm nhỏ các control

Mặc dù có những trường hợp ngoại lệ cho những nguyên tắc này, nhưng tuân thủ được sẽ tạo ra được giao diện bố cục thích hợp cho WPF

2.Tiến trình sắp xếp bố cục

- Bố cục được hình thành bởi hai giai đoạn : đo lường và sắp xếp.Trong giai đoạn đo lường : các vòng lặp qua các phần tử điều khiển để lấy các thông tin kích thước thích hợp.Trong giai đoạn sắp xếp : đặt các điều khiển ở vị trí thích hợp trong panel container, vẫn có trường hợp không đủ chỗ trống cần thiết dẫn đến một số điều khiển hiển thị không đầy đủ.Để tránh tình trạng này, chúng ta sẽ thiết lập kích thước tối thiểu phù hợp.

3.Bố cục của các Panel Container

- Tất cả các panel container đều kế thừa từ lớp trừu tượng System.Windows.Controls.Panel


Các thuộc tính public của class Panel

Tên thuộc tínhMiêu tả
 Background   - Brush element sẽ được dùng để vẽ nền của Panel , chúng ta sẽ phải thiết lập giá trị của thuộc tính này là non-null nếu bạn muốn nó nhận sự kiện của chuột , [Trường hợp bạn muốn nó nhận một sự kiện của chuột nhưng không muốn hiển thị 1 solid background bạn thiết lập background đến Transparent]
 Children - Tập hợp các Item được lưu trữ trong Panel , đây là cấp đầu tiên của các item , và chính các item này sẽ chứa trong nó các item khác.
 IsItemsHost - Có giá trị kiểu Boolean , giá trị là true nếu panel được dùng để hiển thị các Item kết hợp với 1 ItemsControl [Chẳng hạn như Node trong một TreeView hoặc là một list mục trong một ListBox] 

Quan trọng : 

- Bạn có thể override MeasureOveride[] và ArrangOveride[] kế thừa từ lớp FrameworkElement để thay đổi cách xử lý Panel trong các giai đoạn đo lường và sắp xếp đã nói ở trên lúc sắp xếp các element của nó
- WPF cung cấp một số Panel cơ bản nhất để sắp xếp bố cục và hầu hết các Element Object đều nằm trong không gian tên System.Windows.Controls

Danh sách các Panel chính của WPFTên PanelMiêu tả
StackPanel - Đặt các element trong một ngăn xếp nằm ngang [horizontal] hoặc thẳng đứng [vertical], container loại này được dùng để xây dựng một phần trên của sổ của ứng dụng.
WrapPanel - Cho phép sắp xếp các phần tử từ trái sang phải. Khi một dòng phần tử đã điền đầy khoảng không gian cho phép theo chiều ngang, WrapPanel sẽ cuốn phần tử bị tràn khởi giới hạn kích quy định của nó  xuống đầu dòng tiếp theo [tương tự như việc cuốn text].
DockPanel - DockPanel cho phép các phần tử bám lên các cạnh của panel DockPanel bao chứa chúng, tương tự như khái niệm Docking trong Windows Forms. Nếu như có nhiều phần tử cùng bám về một cạnh, chúng sẽ tuân theo thứ tự mà chúng được khai báo trong file XAML
Grid - Panel dạng Grid là dạng panel hết sức linh hoạt, và có thể sử dụng để đạt được gần như tất cả khả năng mà các dạng panel khác có thể làm được, mặc dù mức độ khó dễ không giống nhau. Grid cho phép ta phân định các dòng và cột theo dạng một lưới kẻ ô, và sau đó sẽ sắp đặt các phần tử UI vào các ô tùy ý. Grid sẽ tự động chia đều các dòng và cột [dựa trên kích thước của phần nội dung]. Tuy nhiên, ta có thể sử dụng dấu sao [*] để phân định kích thước theo tỉ lệ hoặc phân định giá trị tuyệt đối về chiều cao hoặc chiều rộng cho hàng và cột. Ta có thể nhận biết sự khác biệt của 2 dạng phân định kích thước nêu trên bằng cách thay đổi kích thước của form chứa panel Grid. Thêm vào đó, thuộc tính ShowGridLines = "True" cho phép hiển thị các đường kẻ ô.
UniformGrid  - Đặt các yếu tố trong một bảng vô hình nhưng buộc tất cả các tế bào có cùng kích thước. Container bố trí này được sử dụng thường xuyên.
Canvas - Panel dạng Canvas sử dụng phương thức sắp xếp các phần tử UI theo vị trí tuyệt đối bằng cách đặt thuộc tính Top [đỉnh] và Left [bên trái] của chúng. Thêm vào đó, thay vì đặt thuộc tính Top, Left, ta có thể đặt thuộc tính Bottom [đáy], Right [bên phải]. Nếu ta đặt đồng thời thuộc tính Left và Right, thuộc tính Right sẽ bị bỏ qua. Phần tử UI sẽ không thay đổi kích thước để thỏa mãn 2 thuộc tính trên cùng một lúc. Tương tự thuộc tính Top sẽ được ưu tiên hơn thuộc tính Bottom. Các phần tử được khai báo sớm hơn trong file XAML sẽ có thể bị che khuất phía dưới các phần tử được khai báo muộn hơn nếu vị trí của chúng xếp chồng lên nhau.

- Ngoài các container chính chúng ta còn có 1 số container phụ chẳng hạn như TabPanel
[ các tab trong một TabControl ] , ToolbarPanel [ các nút trong một thanh công cụ ] , và ToolbarOverflowPanel [ các lệnh trong menu sổ xuống của một thanh công cụ ]

4.Tạo một bố cục đơn giản với Stack Panel
- StackPanel bố trí các phần tử con nằm trong nó bằng cách sắp xếp chúng theo thứ tự trước sau. Các phần
tử sẽ xuất hiện theo thứ tự mà chúng được khai báo trong file XAML theo chiều dọc [ngầm định] hoặc theo
chiều ngang
4.1 Sắp xếp theo chiều dọc

Code XAML

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

    

    

        Control 1

    

    

        Control 2

    

     

        

        Control 3

    

4.1 Sắp xếp theo chiều ngang
Sau đây là đoạn mã XAML minh họa việc sử dụng StackPanel để sắp xếp các phần tử UI cùng ví dụ ở trên theo chiều ngang. Điểm khác biệt duy nhất ở đây là thiết lập thêm thuộc tính Orientation="Horizontal" của đối tượng StackPanel được sử dụng.
Code XAML

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

    

        

        

            

            Control 1

        

        

        Control 2

        

        

            

            Control 3

        

    

Trong trường hợp sắp xếp theo chiều ngang, nếu tổng chiều rộng của các phần tử con lớn hơn chiều rộng của form chứa, thì các phần tử nằm ngoài form sẽ không được nhìn thấy.

5.Các thuộc tính của Layout :
- Mặc dù bố cục ứng dụng được xác định bởi các container, các yếu tố con vẫn có thể có được tiếng nói của mình. Trong thực tế, các panel layout kết hợp với các child element của nó rất nhịp nhàng bằng cách tôn trọng một tập nhỏ các thuộc tính bố trí, được liệt kê dưới đây

Tên thuộc tính Miêu tả chi tiết
HorizontalAlignment - Xác định cách một child element đặt bên trong một container bố cục
lúc thêm không gian dạng hàng ngang. Bạn có thể chọn
Center, Left, Right, hoặc Stretch.
VerticalAlignment  - Xác định cách một child element đặt bên trong một container bố cục 
lúc thêm không gian theo chiều dọc có sẵn. Bạn có thể chọn 
Center, Top, Bottom, hoặc Stretch.
Margin - Xác định khoảng cách xung quanh một phần tử so với parent element chứa nó. Margin thuộc tính là một instance của System.Windows.Thickness cấu trúc, với các thành phần riêng biệt cho Top, Bottom, Left, và Right.
MinWidth & MinHeight - Thiết lập kích thước tối thiểu của một phần tử. Nếu một phần tử quá lớn container bố cục của nó, nó sẽ được cắt cho phù hợp.
MaxWidth & MaxHeight - Thiết lập kích thước tối đa của một phần tử. Nếu container
đã nhiều không gian trong nó có sẵn, các phần tử sẽ không được mở rộng vượt ra ngoài những giới hạn, ngay cả khi HorizontalAlignment và Thuộc tính VerticalAlignment được đặt là Stretch.
Width & Height - Một cách rõ ràng thiết lập kích thước của một phần tử. Thiết lập này sẽ ghi đè một giá trị Stretch dùng cho các HorizontalAlignment hoặc VerticalAlignment
thuộc tính. Tuy nhiên, kích thước này không được áp dụng nếu nó vượt ra bên ngoài giới hạn theo quy định của MinWidth, Minheight, MaxWidth, và
MaxHeight.

Tất cả những thuộc tính được thừa kế từ lớp FrameworkElement cơ sở và do đó được hỗ trợ bởi tất cả các widget đồ hoạ, bạn có thể sử dụng trong một cửa sổ WPF.

6.Canh lề cho bố cục :

- Một StackPanel với thuộc tính định hướng VerticalAlignment dọc không có tác dụng vì mỗi phần tử được cho là nhiều chiều cao như nó cần và không cầ nhiều hơn. Tuy nhiên, HorizontalAlignment lại 
quan trọng và có ảnh hưởng. Nó quyết định đó mỗi thành phần child element được đặt trong hàng của mình.

- Thông thường, các HorizontalAlignment có giá trị mặc định là Left cho một nhãn Label và Stretch cho một Button. Đó là lý do tại sao mỗi nút có độ rộng đổ đầy chiều rộng của Panel cha chứa nó. Tuy nhiên, bạn có thể thay đổi điều đó :

?

1

2

3

4

5

6

7

8

        Align Default Left

        A Button Stack

        Button 1

        Button 2

        Button 3

        Button 4

    

Lưu ý : StackPanel cũng có đặc tính HorizontalAlignment và VerticalAlignment riêng của mình. Theo mặc định, cả hai này được thiết lập để Stretch, và do đó, StackPanel đổ đầy container chứa nó hoàn toàn. Trong ví dụ này, có nghĩa là StackPanel lấp đầy cửa sổ chứa nó. Nếu bạn sử dụng các cài đặt khác nhau, StackPanel sẽ được hiện thực đủ lớn để phù hợp với giới hạn rộng nhất.

7.Lề Margin trong Layout :

- Có một vấn đề rõ ràng với ví dụ StackPanel trong hình thức hiện tại của nó. Một cửa sổ được thiết kế tốt không chỉ chứa các thành phần child element-nó cũng bao gồm một chút khoảng trống thêm trong giữa các yếu tố cho dễ nhìn để giới thiệu thêm không gian này và làm cho ví dụ StackPanel dễ nhìn, bạn có thể thiết lập lề Margin

- Khi thiết lập lề Margin, bạn có thể thiết lập độ rộng cho tất cả các bên của child element, như thế này:

?

1

Button 3

Ngoài ra, bạn có thể thiết lập lề khác nhau cho 4 phía của một điều khiển theo thứ tự Left, Top, Right, Bottom

?

1

Button 3

- Trong code - behind giá trị của thuộc tính Margin được thiết lập bởi 1 khởi tạo của lớp Thickness

?

1

cmd.Margin = new Thickness[5];

- Canh lề đúng cho control là một một nghệ thuật bởi vì bạn cần phải xem xét như thế nào lề sẽ
thiết lập các điều khiển lân cận ảnh hưởng đến nhau. Ví dụ, nếu bạn có hai nút chồng lên nhau, và các nút trên cùng có một lề dưới 5 và nút dưới cùng có một đầu biên độ 5, bạn có tổng cộng 10 đơn vị của không gian giữa hai nút. 
Lý tưởng nhất, bạn sẽ có thể để giữ các thiết lập lề khác nhau như phù hợp nhất có thể và tránh thiết lập giá trị riêng biệt cho hai bên lề khác nhau. Ví dụ, trong ví dụ StackPanel nó làm cho cảm giác 
sử dụng đúng khi canh trên của một nút và trên bảng điều khiển chính nó, như ví dụ sau 

?

1

2

3

4

5

6

7

8

A Button Stack

Button 1

Button 2

Button 3

Button 4

8. Kích thước tối thiểu , tối đa & kích thước tường minh

- Cuối cùng, tất cả các yếu tố bao gồm thuộc tính Chiều cao và rộng cho phép bạn cung cấp cho các element một kích thước rõ ràng.

- Tuy nhiên, đó không phải là một ý tưởng tốt để thực hiện. Thay vào đó, sử dụng thuộc tính kích thước tối đa Maximum Size và tối thiểu Minimum Size để khóa các element của mình vào phạm vi cho phép, nếu cần.

- Suy nghĩ trước khi thiết lập một kích thước tường minh trong WPF. Trong một bố cục thiết kế tốt, đó là điều không nên làm vì nếu bạn thêm thông tin kích thước tường minh, bạn có nguy cơ tạo ra một bố trí giòn dễ vỡ mà không thể thích ứng với những thay đổi [chẳng hạn như sự khác nhau ngôn ngữ và kích thước cửa sổ] và sẽ cắt cụt nội dung của bạn khi phát sinh những vấn đề kích thước vượt phạm vi cho phép.

- Ví dụ, bạn sẽ quyết định rằng các nút trong StackPanel bạn nên Stretch để phù hợp với
StackPanel nhưng được thực hiện không lớn hơn 200 đơn vị rộng và không nhỏ hơn 100 đơn vị rộng. [Theo mặc định,nút bắt đầu có chiều rộng tối thiểu là 75 đơn vị] Dưới đây là đánh dấu bạn cần:

?

1

2

3

4

5

6

7

8

A Button Stack

Button 1

Button 2

Button 3

Button 4

- Tại thời điểm này, bạn có thể tự hỏi " có cách dễ dàng hơn để đặt thuộc tính một cách

chuẩn hóa trên một số thành phần ? " , chẳng hạn như biên nút trong ví dụ này. Câu trả lời là đặc tính Style-một tính năng cho phép bạn sử dụng lại các thiết lập thuộc tính và thậm chí áp dụng một cách tự động luôn sẽ được giới thiệu trong bài viết khác.

- Khi StackPanel xác định kích thước một nút, nó sẽ xem xét một số thông tin:

Kích thước tối thiểu. Mỗi nút phải có kích thước lớn hơn kích thước tối thiểu.

Kích thước tối đa . Mỗi nút sẽ có kích thước luôn luôn nhỏ hơn so với tối đa kích thước [ trừ khi bạn sai khi thiết lập kích thước tối đa là nhỏ hơn kích thước tối thiểu ] .

Nội dung. Nếu content bên trong các nút Button đòi hỏi có chiều rộng lớn hơn,StackPanel sẽ cố gắng để mở rộng nút . [ Bạn có thể tìm ra kích thước của Button bằng cách kiểm tra thuộc tính DesiredSize ,nó sẽ trả về giá trị chiều rộng tối thiểu hoặc chiều rộng nội dung, giá trị lớn hơn ]

Kích thước của container. Nếu chiều rộng tối thiểu của nút lớn hơn chiều rộng của StackPanel  , một phần của nút sẽ được cắt bỏ và các nút sẽ không được phép phát triển rộng hơn StackPanel .

Sự liên kết ngang HorizontalAlignment​ . Vì nút sử dụng một HorizontalAlignment có giá trị Stretch mặc định , StackPanel sẽ cố gắng để mở rộng nút để điền vào toàn bộ chiều rộng của StackPanel .


Đó là hướng để hiểu quá trình này và hiểu kích thước tối thiểu và tối đa thiết lập giới hạn tuyệt đối . Trong giới hạn, StackPanel cố gắng tôn trọng kích thước mong muốn của nút [ để phù hợp với nội dung của nút] và các thiết lập liên kết của nó .

?

1

2

3

4

5

6

7

8

        

            A Button Stack

        Button 1

        Button 2

        Button 3

        Button 4

    

- Trong ví dụ này chúng ta chú ý đến thuộc tính MaxWidth="90" thay đổi kích thước của Button 3

Lưu ý : Trong một số trường hợp, bạn có thể muốn sử dụng mã để kiểm tra kích thước của element control trong cửa sổ. Các thuộc tính chiều cao và rộng sẽ không giúp đỡ vì chúng chỉ hiện thực các thiết lập kích thước mong muốn của bạn, mà có thể không tương ứng với kích thước thực tế các control được hiển thị. Và mong muốn của bạn làm cho kích thước của control phù hợp với nội dung của chúng, và 
các thuộc tính Chiều cao và rộng sẽ không được thiết lập ở tất cả. Bạn có thể lấy kích thước thực tế đang được hiển thị bằng cách đọc các thuộc tính ActualHeight và ActualWidth. Nhưng hãy nhớ, những giá trị này có thể thay đổi mỗi khi cửa sổ thay đổi kích cỡ hoặc các nội dung bên trong nó thay đổi.

Thực tế nếu bạn muốn cửa sổ có kích thước tự động thì bạn nên xây dựng một cửa sổ đơn giản với nội dung động. Để cho phép cửa sổ có kích thước tự động, loại bỏ các thuộc tính Chiều cao và rộng 
 và thiết lập thuộc tính Window.SizeToContent = "WidthAndHeight". Cửa sổ sẽ làm cho chính nó chỉ 
đủ lớn để chứa tất cả các nội dung của nó.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

    

        

            A Button Stack

        Button 1

        Button 2

        Button 3

        Button 4

    

9.Border

- Border không phải là một panel bố trí, nhưng đó là một phần tử hữu ích mà bạn sẽ thường xuyên sử dụng.Vì lý do đó, nó có ý nghĩa để giới thiệu ngay bây giờ, trước khi bạn đi thêm nữa.
- Lớp Border là sự đơn giản thuần túy cho biết thêm một nền hoặc biên xung quanh c. Để làm chủ Bord, bạn cần biết các thuộc tính được liệt kê sau đây.

Tên thuộc tính Diễn tả chi tiết
- Background  - Thiết lập một nền xuất hiện đằng sau tất cả các nội dung nằm trong
biên bằng cách sử dụng một đối tượng Brush. Bạn có thể sử dụng một solid color.
- BorderBrush và BorderThickness - Thiết lập màu sắc của biên  xuất hiện ở cạnh của Đối tượng Border, sử dụng một đối tượng Brush, và thiết lập chiều rộng của biên, tương ứng. Để hiển thị một biên, bạn phải thiết lập cả hai thuộc tính này.
- CornerRadius - Cho phép bạn nhẹ nhàng làm tròn các góc của biên của bạn.Giá trị càng lớn  của CornerRadius hiệu quả làm tròn sẽ cao hơn.
- Padding - Thêm khoảng cách giữa các biên và nội dung bên trong. [Ngược lại, Margin thêm khoảng cách bên ngoài biên.]

?

1

2

3

4

5

6

7

8

9

One

Two

Three

- Về mặt kỹ thuật, Border là một trang trí, một kiểu phần tử thường được sử dụng để tô điểm đồ họa xung quanh một đối tượng. Tất cả các trang trí xuất phát từ lớp System.Windows.Controls.Decorator
. Hầu hết các trang trí được thiết kế để sử dụng với các điều khiển cụ thể. Ví dụ, các nút sử dụng một ButtonChrome trang trí để có được nhãn hiệu của nó, làm tròn góc và nền bóng mờ, trong khi ListBox sử dụng ListBoxChrome trang trí. Ngoài ra còn có hơn hai trang trí chung rất hữu ích khi soạn giao diện người dùng: Border thảo luận ở đây và ViewBox.

10.Wrapper Panel

- WrapPanel cho phép sắp xếp các phần tử từ trái sang phải. Khi một dòng phần tử đã điền đầy khoảng không gian cho phép theo chiều ngang, WrapPanel sẽ cuốn phần tử tiếp theo xuống đầu dòng tiếp theo

?

1

2

3

4

5

6

7

8

9

10

11

12

13

        

    

         Chú cào cào nhỏ, ngồi trong đám cỏ

        Control 2

        

            Control 3

        

    

Chủ Đề