Thiết kế tốt nhất cho các biểu mẫu Windows sẽ chia sẻ chức năng chung


20

Trước đây, tôi đã sử dụng tính kế thừa để cho phép mở rộng các biểu mẫu Windows trong ứng dụng của mình. Nếu tất cả các biểu mẫu của tôi sẽ có các điều khiển, tác phẩm nghệ thuật và chức năng chung, tôi sẽ tạo một biểu mẫu cơ sở thực hiện các điều khiển và chức năng chung và sau đó cho phép các điều khiển khác kế thừa từ biểu mẫu cơ sở đó. Tuy nhiên, tôi đã gặp phải một vài vấn đề với thiết kế đó.

  1. Điều khiển chỉ có thể ở trong một container tại một thời điểm, do đó, bất kỳ điều khiển tĩnh nào bạn có sẽ rất khó khăn. Ví dụ: Giả sử bạn có một biểu mẫu cơ sở có tên BaseForm chứa TreeView mà bạn thực hiện được bảo vệ và tĩnh để tất cả các phiên bản (dẫn xuất) khác của lớp này có thể sửa đổi và hiển thị cùng một TreeView. Điều này sẽ không hoạt động đối với nhiều lớp kế thừa từ BaseForm, bởi vì TreeView chỉ có thể ở trong một container tại một thời điểm. Nó có thể sẽ ở dạng cuối cùng được khởi tạo. Mặc dù mọi phiên bản đều có thể chỉnh sửa điều khiển, nhưng nó sẽ chỉ hiển thị trong một tại một thời điểm nhất định. Tất nhiên, có những công việc xung quanh, nhưng tất cả chúng đều xấu xí. (Đây có vẻ là một thiết kế thực sự tồi tệ đối với tôi. Tại sao nhiều container không lưu trữ con trỏ vào cùng một đối tượng? Dù sao đi nữa, đó là những gì nó là.)

  2. Trạng thái giữa các biểu mẫu, nghĩa là trạng thái nút, văn bản nhãn, v.v., tôi phải sử dụng các biến toàn cục cho và đặt lại trạng thái trên Tải.

  3. Điều này thực sự không được hỗ trợ bởi nhà thiết kế của Visual Studio.

Có một thiết kế tốt hơn, nhưng vẫn dễ dàng bảo trì để sử dụng? Hoặc thừa kế hình thức vẫn là cách tiếp cận tốt nhất?

Cập nhật Tôi đã chuyển từ xem MVC sang MVP sang Mẫu quan sát đến Mẫu sự kiện. Đây là những gì tôi đang nghĩ cho lúc này, xin hãy phê bình:

Lớp BaseForm của tôi sẽ chỉ chứa các điều khiển và các sự kiện được kết nối với các điều khiển đó. Tất cả các sự kiện cần bất kỳ loại logic nào để xử lý chúng sẽ chuyển ngay đến lớp BaseFormPresenter. Lớp này sẽ xử lý dữ liệu từ UI, thực hiện bất kỳ hoạt động logic nào và sau đó cập nhật BaseFormModel. Mô hình sẽ hiển thị các sự kiện, sẽ kích hoạt các thay đổi trạng thái, đến lớp Người trình bày, mà nó sẽ đăng ký (hoặc quan sát). Khi Người thuyết trình nhận được thông báo sự kiện, nó sẽ thực hiện bất kỳ logic nào và sau đó Người thuyết trình sẽ sửa đổi Chế độ xem cho phù hợp.

Sẽ chỉ có một trong mỗi lớp Model trong bộ nhớ, nhưng có khả năng có thể có nhiều phiên bản của BaseForm và do đó là BaseFormPresenter. Điều này sẽ giải quyết vấn đề của tôi về việc đồng bộ hóa từng phiên bản của BaseForm với cùng một mô hình dữ liệu.

Câu hỏi:

Lớp nào nên lưu trữ nội dung như, nút nhấn cuối cùng, để tôi có thể giữ nó được tô sáng cho người dùng (như trong menu CSS) giữa các biểu mẫu?

Hãy chỉ trích thiết kế này. Cảm ơn bạn đã giúp đỡ!


Tôi không thấy lý do tại sao bạn buộc phải sử dụng các biến toàn cục, nhưng nếu vậy thì chắc chắn phải có một cách tiếp cận tốt hơn. Có lẽ các nhà máy để tạo ra các thành phần phổ biến kết hợp với thành phần thay vì kế thừa?
stijn

Toàn bộ thiết kế của bạn là thiếu sót. Nếu bạn đã hiểu những gì bạn muốn làm không được Visual Studio hỗ trợ sẽ cho bạn biết điều gì đó.
Ramhound

1
@Ramhound Nó được hỗ trợ bởi studio hình ảnh, chỉ là không tốt. Đây là cách Microsoft bảo bạn làm điều đó. Tôi chỉ thấy đó là một nỗi đau trong A. msdn.microsoft.com/en-us/l Library / aa983613 (v = vs.71) .aspx Dù sao đi nữa, nếu bạn có một ý tưởng tốt hơn, tôi sẽ là tất cả.
Jonathan Henson

@stijn Tôi cho rằng tôi có thể có một phương thức trong mỗi dạng cơ sở sẽ tải các trạng thái điều khiển vào một thể hiện khác. tức là LoadStatesToNewInstance (ví dụ BaseForm). Tôi có thể gọi nó bất cứ lúc nào tôi muốn hiển thị một hình thức mới của loại cơ sở đó. form_I_am_about_to_ leather.LoadStatesToNewInstance (cái này);
Jonathan Henson

Tôi không phải là nhà phát triển .net, bạn có thể mở rộng ở điểm số 1 không? Tôi có thể có một giải pháp
Imran Omar Bukhsh

Câu trả lời:


6
  1. Tôi không biết tại sao bạn cần điều khiển tĩnh. Có lẽ bạn biết điều gì đó tôi không biết. Tôi đã sử dụng rất nhiều kế thừa trực quan nhưng tôi chưa bao giờ thấy các điều khiển tĩnh là cần thiết. Nếu bạn có một điều khiển treeview chung, hãy để mọi thể hiện của biểu mẫu có đối tượng điều khiển riêng và chia sẻ một thể hiện duy nhất của dữ liệu được liên kết với các lần xem tre.

  2. Chia sẻ trạng thái kiểm soát (trái ngược với dữ liệu) giữa các biểu mẫu cũng là một yêu cầu bất thường. Bạn có chắc chắn FormB thực sự cần biết về trạng thái của các nút trên FormA không? Hãy xem xét các thiết kế MVP hoặc MVC. Hãy nghĩ về mỗi hình thức như một "khung nhìn" ngu ngốc không biết gì về các khung nhìn khác hoặc thậm chí chính ứng dụng. Giám sát mỗi chế độ xem với người trình bày / bộ điều khiển thông minh. Nếu nó có ý nghĩa, một người thuyết trình duy nhất có thể giám sát một số quan điểm. Liên kết một đối tượng nhà nước với mỗi chế độ xem. Nếu bạn có một số trạng thái cần được chia sẻ giữa các chế độ xem, hãy để người trình bày hòa giải điều này (và xem xét cơ sở dữ liệu - xem bên dưới).

  3. Đồng ý, Visual Studio sẽ khiến bạn đau đầu. Khi xem xét kế thừa biểu mẫu hoặc điều khiển người dùng, bạn cần cân nhắc cẩn thận các lợi ích so với chi phí tiềm năng (và có thể xảy ra) của vật lộn với các hạn chế và hạn chế gây khó chịu của người thiết kế biểu mẫu. Tôi đề nghị giữ kế thừa hình thức ở mức tối thiểu - chỉ sử dụng nó khi mức chi trả cao. Hãy nhớ rằng, như một cách thay thế cho phân lớp, bạn có thể tạo biểu mẫu "cơ sở" chung và chỉ cần khởi tạo nó một lần cho mỗi "đứa trẻ" và sau đó tùy chỉnh nó một cách nhanh chóng. Điều này có ý nghĩa khi sự khác biệt giữa mỗi phiên bản của biểu mẫu là nhỏ so với các khía cạnh được chia sẻ. (IOW: dạng cơ sở phức tạp, dạng con chỉ phức tạp hơn một chút)

Đừng sử dụng các điều khiển người dùng khi nó giúp bạn ngăn chặn sự trùng lặp đáng kể của sự phát triển UI. Xem xét kế thừa usercontrol nhưng áp dụng các cân nhắc tương tự như đối với thừa kế mẫu.

Tôi nghĩ rằng lời khuyên quan trọng nhất tôi có thể cung cấp là, nếu bạn hiện không sử dụng một số dạng mẫu xem / bộ điều khiển, tôi khuyến khích bạn nên bắt đầu làm như vậy. Nó buộc bạn phải tìm hiểu và đánh giá cao những lợi ích của việc tách rời và tách lớp.

phản hồi cập nhật của bạn

Lớp nào sẽ lưu trữ những thứ như, nút nhấn cuối cùng, để tôi có thể giữ nó nổi bật cho người dùng ...

Bạn có thể chia sẻ trạng thái giữa các chế độ xem giống như bạn sẽ chia sẻ trạng thái giữa người trình bày và chế độ xem. Tạo một lớp SharedViewState đặc biệt. Để đơn giản, bạn có thể biến nó thành một singleton hoặc bạn có thể khởi tạo nó trong người trình bày chính và chuyển nó đến tất cả các chế độ xem (thông qua người thuyết trình của họ) từ đó. Khi trạng thái được liên kết với các điều khiển, sử dụng ràng buộc dữ liệu nếu có thể. Hầu hết các Controlthuộc tính có thể được ràng buộc dữ liệu. Ví dụ, thuộc tính BackColor của Nút có thể được liên kết với thuộc tính của lớp SharedViewState của bạn. Nếu bạn thực hiện liên kết này trên tất cả các biểu mẫu có các nút giống hệt nhau, bạn có thể tô sáng Nút1 trên tất cả các biểu mẫu chỉ bằng cách cài đặt SharedViewState.Button1BackColor = someColor.

Nếu bạn không quen với cơ sở dữ liệu WinForms, hãy nhấn MSDN và đọc một số thứ. Nó không khó. Tìm hiểu về INotifyPropertyChangedvà bạn đang ở giữa chừng.

Dưới đây là một triển khai điển hình của lớp viewstate với thuộc tính Button1BackColor làm ví dụ:

public class SharedViewState : INotifyPropertyChanged
{
    // boilerplate INotifyPropertyChanged stuff
    public event PropertyChangedEventHandler PropertyChanged;
    protected void NotifyPropertyChanged(string info)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
        }
    }

    // example of a property for data-binding
    private Color button1BackColor;
    public Color Button1BackColor
    {
        get { return button1BackColor; }
        set
        {
            if (value != button1BackColor)
            {
                button1BackColor = value;
                NotifyPropertyChanged("Button1BackColor");
            }
        }
    }
}

Cảm ơn bạn đã trả lời chu đáo. Bạn có biết một tài liệu tham khảo tốt cho mẫu view / bộ điều khiển không?
Jonathan Henson

1
Tôi nhớ mình đã nghĩ rằng đây là một đoạn giới thiệu khá hay: codebetter.com/jeremymiller 2007/05/22 / Giả
Igby

xin vui lòng xem cập nhật của tôi.
Jonathan Henson

@JonathanHenson: câu trả lời được cập nhật.
Igby Largeeman

5

Tôi duy trì một ứng dụng WinForms / WPF mới dựa trên mẫu MVVM và Điều khiển cập nhật . Nó bắt đầu là WinForms, sau đó tôi đã tạo một phiên bản WPF vì tầm quan trọng tiếp thị của một giao diện người dùng có vẻ tốt. Tôi nghĩ rằng đó sẽ là một hạn chế thiết kế thú vị để duy trì một ứng dụng hỗ trợ hai công nghệ UI hoàn toàn khác nhau với cùng một mã phụ trợ (viewmodels và model), và tôi phải nói rằng tôi khá hài lòng với phương pháp này.

Trong ứng dụng của tôi, bất cứ khi nào tôi cần chia sẻ chức năng giữa các phần của giao diện người dùng, tôi sử dụng các điều khiển tùy chỉnh hoặc điều khiển người dùng (các lớp có nguồn gốc từ WPF UserControl hoặc WinForms UserControl). Trong phiên bản WinForms có một UserControl bên trong một UserControl khác bên trong một lớp TabPage dẫn xuất, cuối cùng nằm trong một điều khiển tab trên biểu mẫu chính. Phiên bản WPF thực sự có UserControls lồng sâu hơn một cấp. Sử dụng các điều khiển tùy chỉnh, bạn có thể dễ dàng soạn UI mới từ UserControls mà bạn đã tạo trước đó.

Vì tôi sử dụng mẫu MVVM, tôi đặt càng nhiều logic chương trình càng tốt vào ViewModel (bao gồm Mô hình trình bày / Điều hướng ) hoặc Mô hình (tùy thuộc vào việc mã có liên quan đến giao diện người dùng hay không), nhưng vì cùng một ViewModel được sử dụng bởi cả chế độ xem WinForms và chế độ xem WPF, ViewModel không được phép chứa mã được thiết kế cho WinForms hoặc WPF hoặc tương tác trực tiếp với giao diện người dùng; mã như vậy PHẢI đi vào xem.

Cũng trong mẫu MVVM, các đối tượng UI nên tránh tương tác với nhau! Thay vào đó họ tương tác với các khung nhìn. Chẳng hạn, TextBox sẽ không hỏi ListBox gần đó, mục nào được chọn; thay vào đó, hộp danh sách sẽ lưu tham chiếu đến mục hiện được chọn ở đâu đó trong lớp viewmodel và TextBox sẽ truy vấn lớp viewmodel để tìm hiểu những gì được chọn ngay bây giờ. Điều này giải quyết vấn đề của bạn về việc tìm ra nút nào được đẩy ở một dạng từ bên trong một dạng khác. Bạn chỉ cần chia sẻ một đối tượng Mô hình Điều hướng (là một phần của lớp viewmodel của ứng dụng) giữa hai biểu mẫu và đặt một thuộc tính trong đối tượng đó đại diện cho nút nào được ấn.

Bản thân WinForms không hỗ trợ mẫu MVVM rất tốt, nhưng Update Controls cung cấp MVVM tiếp cận độc đáo của riêng nó như một thư viện nằm trên WinForms.

Theo tôi, cách tiếp cận này để thiết kế chương trình hoạt động rất tốt và tôi dự định sẽ sử dụng nó cho các dự án trong tương lai. Lý do nó hoạt động rất tốt là (1) rằng Update Controls tự động quản lý các phụ thuộc và (2) rằng nó thường rất rõ ràng về cách bạn nên cấu trúc mã của mình: tất cả mã tương tác với các đối tượng UI thuộc về Chế độ xem, tất cả mã đó là Liên quan đến giao diện người dùng nhưng KHÔNG PHẢI tương tác với các đối tượng UI thuộc về ViewModel. Rất thường xuyên, bạn sẽ chia mã của mình thành hai phần, một phần cho View và một phần khác cho ViewModel. Tôi gặp một số khó khăn khi thiết kế menu ngữ cảnh trong hệ thống của mình, nhưng cuối cùng tôi cũng đã nghĩ ra một thiết kế cho điều đó.

Tôi cũng phát cuồng về Kiểm soát cập nhật trên blog của mình . Tuy nhiên, cách tiếp cận này đã làm quen rất nhiều và trong các ứng dụng quy mô lớn (ví dụ: nếu hộp danh sách của bạn chứa hàng ngàn mục), bạn có thể gặp phải các vấn đề về hiệu suất do hạn chế hiện tại của quản lý phụ thuộc tự động.


3

Tôi sẽ trả lời điều này mặc dù bạn đã chấp nhận một câu trả lời.

Như những người khác đã chỉ ra, tôi không hiểu tại sao bạn phải sử dụng bất cứ thứ gì tĩnh; Điều này có vẻ như bạn đã làm điều gì đó rất sai.

Dù sao, tôi cũng gặp vấn đề tương tự như bạn: trong ứng dụng WinForms của tôi, tôi có một số biểu mẫu chia sẻ một số chức năng và một số điều khiển. Ngoài ra, tất cả các biểu mẫu của tôi đã xuất phát từ một biểu mẫu cơ sở (hãy gọi nó là "MyForm") có thêm chức năng cấp khung (không phân biệt ứng dụng.) Trình thiết kế biểu mẫu của Visual Studio được cho là hỗ trợ chỉnh sửa các biểu mẫu kế thừa từ các biểu mẫu khác, nhưng trong thực hành nó chỉ hoạt động miễn là các hình thức của bạn không làm gì khác hơn là "Xin chào, thế giới! - OK - Hủy".

Điều cuối cùng tôi làm là: Tôi giữ lớp cơ sở chung "MyForm", khá phức tạp và tôi tiếp tục lấy được tất cả các mẫu đơn của mình từ nó. Tuy nhiên, tôi hoàn toàn không làm gì trong các biểu mẫu này, vì vậy Trình thiết kế biểu mẫu VS không gặp vấn đề gì khi chỉnh sửa chúng. Các biểu mẫu này chỉ bao gồm mã mà Trình thiết kế biểu mẫu tạo cho chúng. Sau đó, tôi có một hệ thống phân cấp song song riêng biệt của các đối tượng mà tôi gọi là "Surrogates" có chứa tất cả các chức năng dành riêng cho ứng dụng, như khởi tạo các điều khiển, xử lý các sự kiện được tạo bởi biểu mẫu và các điều khiển, v.v. sự tương ứng giữa các lớp thay thế và các hộp thoại trong ứng dụng của tôi: có một lớp thay thế cơ sở tương ứng với "MyForm", sau đó một lớp thay thế khác có nguồn gốc tương ứng với "MyApplicationForm",

Mỗi đại diện chấp nhận như một tham số thời gian xây dựng một loại biểu mẫu cụ thể và nó tự đính kèm với nó bằng cách đăng ký các sự kiện của nó. Nó cũng ủy quyền cho căn cứ, tất cả các cách để "MySurrogate" chấp nhận "MyForm". Người thay thế đó đăng ký với sự kiện "Vứt bỏ" của biểu mẫu, để khi biểu mẫu bị hủy, người thay thế sẽ gọi một bản ghi đè lên chính nó để nó và tất cả con cháu của nó có thể thực hiện việc dọn dẹp. (Deregister từ các sự kiện, vv)

Nó đã hoạt động tốt cho đến nay.

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.