Có một số loại chiến lược hệ thống để thiết kế và thực hiện GUI?


18

Tôi đang sử dụng Visual Studio để tạo một ứng dụng GUI trong C #. Hộp công cụ đóng vai trò là bảng thành phần tiện lợi cho phép tôi dễ dàng kéo và thả các nút và các yếu tố khác (để rõ ràng tôi sẽ nói nút bất cứ khi nào tôi có nghĩa là "điều khiển") trên biểu mẫu của mình, điều này làm cho các dạng tĩnh khá dễ thực hiện. Tuy nhiên, tôi gặp phải hai vấn đề:

  • Tạo các nút ở nơi đầu tiên là rất nhiều công việc. Khi tôi có một biểu mẫu không tĩnh (ví dụ: các nút hoặc các điều khiển khác được tạo trong thời gian chạy theo những gì người dùng làm) Tôi hoàn toàn không thể sử dụng bảng màu. Thay vào đó, tôi phải tạo từng nút theo cách thủ công, bằng cách gọi hàm tạo trong bất kỳ phương thức nào tôi đang sử dụng, sau đó khởi tạo thủ công bằng cách chỉ định chiều cao, chiều rộng, vị trí, nhãn, trình xử lý sự kiện, v.v. Điều này là vô cùng tẻ nhạt vì tôi phải đoán tất cả các thông số mỹ phẩm này mà không thể xem biểu mẫu sẽ trông như thế nào và nó cũng tạo ra nhiều dòng mã lặp đi lặp lại cho mỗi nút.
  • Làm các nút làm một cái gì đó cũng là rất nhiều công việc. Đối phó với các sự kiện trong một ứng dụng đầy đủ tính năng là một nỗi đau rất lớn. Cách duy nhất tôi biết cách thực hiện là chọn một nút, chuyển đến tab sự kiện trong thuộc tính của nó, nhấp vào OnClicksự kiện để nó tạo sự kiện trong Formmã của mã, sau đó điền vào phần thân của sự kiện. Vì tôi muốn tách biệt logic và trình bày, tất cả các trình xử lý sự kiện của tôi kết thúc bằng các cuộc gọi một đường đến chức năng logic nghiệp vụ thích hợp. Nhưng việc sử dụng điều này cho nhiều nút (ví dụ, hãy tưởng tượng số lượng nút có trong một ứng dụng như MS Word) làm ô nhiễm mã của tôi Formvới hàng tá phương thức xử lý sự kiện nồi hơi và rất khó để duy trì điều này.

Do đó, bất kỳ chương trình GUI nào phức tạp hơn Hello World đều rất không thực tế đối với tôi. Để rõ ràng, tôi không gặp vấn đề gì khi xử lý sự phức tạp trong các chương trình mà tôi viết có giao diện người dùng tối thiểu - tôi cảm thấy mình có thể sử dụng OOP với mức độ hợp lý để cấu trúc gọn gàng mã logic kinh doanh của mình. Nhưng khi phát triển GUI, tôi bị kẹt. Có vẻ tẻ nhạt đến nỗi tôi cảm thấy như mình đang phát minh lại cái bánh xe, và có một cuốn sách ở đâu đó ngoài kia giải thích cách làm GUI đúng cách mà tôi chưa đọc.

Tui bỏ lỡ điều gì vậy? Hay tất cả các nhà phát triển C # chỉ chấp nhận danh sách vô tận các trình xử lý sự kiện lặp lại và mã tạo nút?


Như một gợi ý (hy vọng hữu ích), tôi hy vọng rằng một câu trả lời hay sẽ nói về:

  • Sử dụng các kỹ thuật OOP (chẳng hạn như mẫu nhà máy) để đơn giản hóa việc tạo nút lặp lại
  • Kết hợp nhiều trình xử lý sự kiện thành một phương thức duy nhất để kiểm tra Senderxem nút nào được gọi là nút đó và hành xử theo đó
  • XAML và sử dụng WPF thay vì Windows Forms

Bạn không cần phải đề cập đến bất kỳ trong số này, tất nhiên. Đó chỉ là phỏng đoán tốt nhất của tôi về loại câu trả lời mà tôi đang tìm kiếm.


Như tôi thấy có, Factory sẽ là một mẫu tốt nhưng tôi thấy nhiều hơn một mẫu Model-View-Controller với các Lệnh được nối với các điều khiển thay vì các sự kiện trực tiếp. WPF thường là MVVM nhưng MVC là có thể. Chúng tôi hiện có một phần mềm khổng lồ trong WPF sử dụng MVC với 90%. Tất cả các lệnh được chuyển tiếp đến bộ điều khiển và anh ta xử lý mọi việc
Franck

Bạn không thể tạo biểu mẫu động bằng tất cả các nút có thể bằng trình chỉnh sửa GUI và chỉ làm cho những thứ bạn không cần phải vô hình động? Âm thanh như công việc ít hơn nhiều.
Hans-Peter Störr

@hstoerr Nhưng thật khó để bố trí hoạt động. Rất nhiều nút sẽ chồng lên nhau.
Tuyệt vời nhất vào

Câu trả lời:


22

Có một số phương pháp đã phát triển qua nhiều năm để giải quyết những vấn đề bạn đã đề cập, đó là, tôi đồng ý, hai vấn đề chính mà khung UI đã phải giải quyết trong những năm gần đây. Đến từ nền tảng WPF, chúng được tiếp cận như sau:

Thiết kế khai báo, thay vì bắt buộc

Khi bạn mô tả mã viết cẩn thận để khởi tạo các điều khiển và đặt thuộc tính của chúng, bạn đang mô tả mô hình bắt buộc của thiết kế UI. Ngay cả với nhà thiết kế WinForms, bạn chỉ đang sử dụng trình bao bọc cho mô hình đó - mở tệp Form1.Designer.cs và bạn thấy tất cả mã đó đang nằm ở đó.

Với WPF và XAML - và các mô hình tương tự trong các khung khác, tất nhiên, từ HTML trở đi - bạn sẽ mô tả bố cục của mình và để cho khung công tác thực hiện công việc này. Bạn sử dụng các điều khiển thông minh hơn như Bảng điều khiển (Lưới hoặc WrapPanel, v.v.) để mô tả mối quan hệ giữa các thành phần UI, nhưng kỳ vọng là bạn không định vị chúng theo cách thủ công. Các khái niệm linh hoạt như điều khiển lặp lại - như Repeater của ASP.NET hoặc ItemControl của WPF - giúp bạn tạo giao diện người dùng linh hoạt mà không cần viết mã lặp lại, cho phép các thực thể dữ liệu tăng trưởng được thể hiện linh hoạt dưới dạng điều khiển.

Các mẫu dữ liệu của WPF cho phép bạn xác định - một lần nữa, khai báo - một chút cố gắng về tính tốt của giao diện người dùng và khớp chúng với dữ liệu của bạn; chẳng hạn, một danh sách các đối tượng dữ liệu của Khách hàng có thể được liên kết với ItemControl và một mẫu dữ liệu khác được gọi tùy thuộc vào việc anh ta có phải là nhân viên bình thường hay không (sử dụng mẫu hàng lưới tiêu chuẩn có tên và địa chỉ) hoặc Prime Prime, trong khi một mẫu khác , với một hình ảnh và các nút dễ truy cập được hiển thị. Một lần nữa, không cần viết mã trong cửa sổ cụ thể , chỉ cần các điều khiển thông minh hơn nhận thức được bối cảnh dữ liệu của họ và do đó cho phép bạn chỉ cần liên kết chúng với dữ liệu của bạn và để chúng thực hiện các hoạt động liên quan.

Ràng buộc dữ liệu và tách lệnh

Chạm vào liên kết dữ liệu, cơ sở dữ liệu của WPF (và, một lần nữa, trong các khung khác, như AngularJS) cho phép bạn nêu ý định của mình bằng cách liên kết một điều khiển (giả sử, hộp văn bản) với một thực thể dữ liệu (giả sử là Tên của khách hàng) và cho phép khung xử lý hệ thống ống nước. Logic được xử lý tương tự. Thay vì kết nối thủ công các trình xử lý sự kiện phía sau với logic nghiệp vụ dựa trên Bộ điều khiển, bạn sử dụng cơ chế Ràng buộc dữ liệu để liên kết hành vi của bộ điều khiển (giả sử, thuộc tính Lệnh của nút) với đối tượng Lệnh đại diện cho nugget của hoạt động.

Nó cho phép Lệnh này được chia sẻ giữa các cửa sổ mà không cần viết lại trình xử lý sự kiện mỗi lần.

Mức độ trừu tượng cao hơn

Cả hai giải pháp này cho hai vấn đề của bạn thể hiện sự chuyển sang mức độ trừu tượng cao hơn so với mô hình hướng sự kiện của Windows Forms mà bạn thấy mệt mỏi.

Ý tưởng là bạn không muốn xác định, trong mã, mọi thuộc tính duy nhất mà điều khiển có và mọi hành vi bắt đầu từ nút bấm và trở đi. Bạn muốn các điều khiển và khung cơ bản của bạn làm được nhiều việc hơn cho bạn và cho phép bạn suy nghĩ về các khái niệm trừu tượng hơn về Ràng buộc dữ liệu (tồn tại trong WinForms, nhưng không hữu dụng như trong WPF) và mẫu Lệnh để xác định các liên kết giữa UI và hành vi không yêu cầu xuống kim loại.

Mẫu MVVM là cách tiếp cận của Microsoft đối với mô hình này. Tôi đề nghị đọc lên trên đó.

Đây là một ví dụ sơ bộ về mô hình này sẽ trông như thế nào và nó sẽ giúp bạn tiết kiệm thời gian và dòng mã. Điều này sẽ không được biên dịch, nhưng nó là giả WPF. :)

Ở đâu đó trong tài nguyên ứng dụng của bạn, bạn xác định Mẫu dữ liệu:

<DataTemplate x:DataType="Customer">
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

<DataTemplate x:DataType="PrimeCustomer">
   <Image Source="GoldStar.png"/>
   <TextBox Text="{Binding Name}"/> 
</DataTemplate>

Bây giờ, bạn liên kết màn hình chính của bạn với ViewModel, một lớp hiển thị dữ liệu của bạn. Giả sử, một tập hợp các Khách hàng (đơn giản là a List<Customer>) và một đối tượng Lệnh (một lần nữa, một thuộc tính công cộng đơn giản thuộc loại ICommand). Liên kết này cho phép ràng buộc:

public class CustomersnViewModel
{
     public List<Customer> Customers {get;}
     public ICommand RefreshCustomerListCommand {get;}  
}

và giao diện người dùng:

<ListBox ItemsSource="{Binding Customers}"/>
<Button Command="{Binding RefreshCustomerListCommand}">Refresh</Button>

Và đó là nó. Cú pháp của ListBox sẽ lấy danh sách Khách hàng khỏi ViewModel và cố gắng hiển thị chúng cho UI. Do hai Mẫu dữ liệu mà chúng tôi đã xác định trước đó, nó sẽ nhập mẫu có liên quan (dựa trên Kiểu dữ liệu, giả sử PrimeCustomer kế thừa từ Khách hàng) và đặt nó làm nội dung của ListBox. Không lặp, không tạo thế hệ điều khiển động thông qua mã.

Nút, tương tự, có cú pháp từ trước để liên kết hành vi của nó với việc triển khai ICommand, có lẽ biết để cập nhật thuộc Customerstính - tự động nhắc khung liên kết dữ liệu để cập nhật lại giao diện người dùng.

Tôi đã thực hiện một số phím tắt ở đây, tất nhiên, nhưng đây là ý chính của nó.


5

Đầu tiên, tôi đề xuất loạt bài viết này: http://codebetter.com/jeremymiller/2007/07/26/the-build-your-own-cab-series-table-of-contents/ - nó không giải quyết các vấn đề chi tiết với mã nút của bạn, nhưng nó cung cấp cho bạn một chiến lược có hệ thống để thiết kế và triển khai khung ứng dụng của riêng bạn, đặc biệt là cho các ứng dụng GUI, cho bạn thấy cách áp dụng MVC, MVP, MVVM cho ứng dụng Biểu mẫu điển hình của bạn.

Giải quyết câu hỏi của bạn về "xử lý sự kiện": nếu bạn có nhiều biểu mẫu với các nút hoạt động theo cách tương tự, có thể bạn sẽ phải trả giá để tự mình thực hiện phân công xử lý sự kiện trong "khung ứng dụng". Thay vì chỉ định các sự kiện nhấp bằng trình thiết kế GUI, hãy tạo quy ước đặt tên như thế nào một trình xử lý "nhấp chuột" phải được đặt tên cho một nút của một tên cụ thể. Ví dụ: cho nút MyNiftyButton_ClickHandlercho nút MyNiftyButton. Sau đó, thực sự không khó để viết một thói quen có thể sử dụng lại (sử dụng sự phản chiếu) lặp đi lặp lại trên tất cả các nút của biểu mẫu, kiểm tra xem biểu mẫu có chứa trình xử lý nhấp chuột liên quan và tự động gán trình xử lý cho sự kiện không. Xem ở đây cho một ví dụ.

Và nếu bạn chỉ muốn sử dụng một trình xử lý sự kiện cho một loạt các nút, điều đó cũng có thể. Vì mỗi trình xử lý sự kiện được người gửi thông qua và người gửi luôn là nút được nhấn, bạn có thể gửi đến mã doanh nghiệp bằng cách sử dụng thuộc tính "Tên" của mỗi nút.

Đối với việc tạo động các nút (hoặc các điều khiển khác): bạn có thể tạo các nút hoặc các điều khiển khác với nhà thiết kế trên một hình thức không sử dụng chỉ với mục đích nuôi ong một mẫu . Sau đó, bạn sử dụng sự phản chiếu (xem ví dụ ở đây ) để sao chép điều khiển mẫu và chỉ thay đổi các thuộc tính như vị trí hoặc kích thước. Điều đó có lẽ sẽ đơn giản hơn nhiều so với việc khởi tạo hai hoặc ba chục thuộc tính theo cách thủ công trong mã.

Tôi đã viết điều này ở trên bằng cách ghi nhớ Winforms (giống như bạn, tôi đoán vậy), nhưng các nguyên tắc chung nên áp dụng cho các môi trường GUI khác như WPF với khả năng tương tự.


Quả thực có thể làm trong WPF và nó thậm chí còn dễ dàng hơn nhiều. Bạn chỉ cần điền vào danh sách các lệnh (chức năng cụ thể được mã hóa trước) và đặt nguồn trên cửa sổ của bạn. Bạn tạo một mẫu trực quan đơn giản và tất cả những gì bạn cần quan tâm là điền vào một bộ sưu tập với các lệnh bạn muốn và ràng buộc WPF tuyệt vời sẽ chăm sóc phần còn lại cho bạn một cách trực quan.
Franck
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.