Lỗi thường gặp trong lập trình winform – Series Giải pháp winforms (1)

3

Lập trình winform được nhiều bạn lựa chọn học để làm thực tập/đồ án vì sự đơn giản, dễ học, dễ thực hiện. Tuy nhiên, có lẽ cũng vì sự đơn giản này khiến nhiều bạn lầm tưởng rằng không cần học nhiều cũng lập trình winform được ngon lành.

Sự thật không phải như vậy. Mặc dù winform dễ học, nhưng để lập trình ứng dụng thực sự với winform đòi hỏi đầu tư nhiều thời gian công sức để học và làm.

Khi học lập trình winform một cách lõm bõm, bạn chắc chắn sẽ mắc phải một loạt lỗi. Bạn hẳn đã từng viết hàng đống code đủ thể loại vào file code behind chứ? Còn nhiều lỗi nữa mà phần tiếp theo của bài này sẽ chỉ ra giúp bạn.

Để giúp bạn khắc phục các lỗi đó, chúng tôi xây dựng loạt bài bao gồm sáu phần trình bày các giải pháp đơn giản nhưng hữu ích dành cho lập trình winform. Những giải pháp này hướng tới giúp các bạn sinh viên khi làm project môn học, khóa luận, thực tập, hoặc đồ án có sử dụng đến công nghệ winform.

Cũng xin lưu ý ngay từ đầu rằng đây không phải là loạt bài dạy lập trình winform cơ bản. Để đọc, hiểu và làm theo loạt bài này, bạn cần biết sẵn một số kỹ thuật lập trình winform như sử dụng các điều khiển cơ bản (bằng code hoặc trên visual designer), cài đặt thư viện, tương tác với điều khiển (đọc/ghi dữ liệu).

Loạt bài “Các giải pháp dành cho lập trình winform”:
Phần 1 – Lỗi thường gặp trong lập trình winforms
Phần 2 – Thiết kế giao diện với Data Sources và BindingSource
Phần 3 – Phân chia code thành module sử dụng Interface
Phần 4 – Sử dụng thư viện DevExpress cho winforms
Phần 5 – Sử dụng Data Binding
Phần 6 – Sử dụng Entity Framework

Như đã nói trong loạt bài về các công nghệ Microsoft sinh viên nên biết, winforms là một công nghệ lớn tuổi. Tuy nhiên, winforms vẫn còn được sử dụng rất rộng rãi. Winforms đặc biệt phù hợp trong quá trình đào tạo để làm project môn học/đồ án/thực tập, cũng như để làm prototype nhanh. Vì sự hữu dụng của mình, người ta vẫn sẽ còn dùng đến winforms. Vì vậy, winforms vẫn là thứ nên học.

Với lịch sử lâu đời của mình, có vô số giải pháp ứng dụng đã được đưa ra cho winforms. Ở đây chúng ta không liệt kê/trình bày lại những giải pháp đó (và cũng không thể). Chúng ta sẽ đưa ra một số giải pháp riêng, đơn giản nhưng hiệu quả giúp giải quyết một số “lỗi” sinh viên thường mắc phải khi code.

Trước hết, hãy xem các lỗi người mới học lập trình winform thường mắc phải là gì?

Một số lỗi thường gặp trong lập trình winform

Viết mọi thứ vào code behind

Các bạn rất thường xuyên viết tất cả các loại code trong file code behind. Nó bao gồm đủ các code xử lý logic, tính toán, truy xuất cơ sở dữ liệu, tương tác với các điều khiển.

Kết quả là file code behind thường xuyên là một mớ hổ lốn với số lượng code rất lớn. Cá biệt, đôi khi gặp những project mà toàn bộ code của cả chương trình dồn vào trong vài form.

Mỗi lớp form thường được tách làm hai file: file design và file code behind. Code của file design được chương trình designer sinh tự động. Phần code behind là nơi lập trình viên viết code của mình để xử lý sự kiện hoặc thực hiện các tính toán.

Giờ đặt ra mấy vấn đề:

  • Bạn sẽ làm thế nào nếu muốn thay đổi thiết kế giao diện? Hầu như sẽ phải code lại hết từ đầu rồi.
  • Nếu bạn làm việc theo nhóm 2-3 người, làm sao phân chia công việc? Cái này chịu rồi.
  • Làm sao để test được logic/tính toán của ứng dụng? Cái này cũng chịu rồi.
  • Làm sao để bảo trì code về sau, đặc biệt khi có yêu cầu điều chỉnh từ người hướng dẫn?
  • Người khác (giáo viên chẳng hạn) đọc code của bạn chắc chắn sẽ không chịu nổi đâu.

Tại sao lại có việc dồn code hết vào một cục như vậy?

Đây là do bạn không biết và vận dụng các nguyên lý của thiết kế hướng đối tượng (như bộ nguyên lý SOLID) và các nguyên lý chung trong lập trình ứng dụng (vd, nguyên lý Separation of Concerns). Các nguyên lý này áp dụng không chỉ cho winform, mà còn cho bất kỳ công nghệ phát triển ứng dụng nào.

Bạn cũng không quan tâm đến các yêu cầu chung đặt ra cho việc code phần mềm (ví dụ, khả năng bảo trì, nâng cấp, test). Mục đích của các bạn chỉ là làm sao có được một chương trình chạy.

Tuy nhiên, nếu bạn quen với lối lập trình như vậy, dù là theo công nghệ nào đi nữa, code của các bạn cũng vẫn chỉ là các mớ hổ lốn. Sau sẽ rất khó sửa.

Không phát huy được các tính năng của windows forms

Ví dụ, khi làm việc với các control như TextBox, các bạn thường truy xuất thông tin trực tiếp qua code và thuộc tính Text. Khi làm việc với DataGridView/ListView, các bạn thường đổ dữ liệu vào một cách thủ công.

Khi làm như vậy, tất cả đều phải thông qua code. Mà code này lại nằm ở chính file code behind đã nói ở trên. Nó càng làm phình file này ra.

Với kiểu làm “thủ công” trên, bạn đã vứt bỏ đi khả năng liên kết dữ liệu (data binding) và hỗ trợ thiết kế rất mạnh của winforms. Lối làm thủ công mất rất nhiều công sức, dễ bị lỗi, ứng dụng làm ra hay bị lỗi vặt và thiếu ổn định.

Hãy tưởng tượng khi một giao diện phức tạp với rất nhiều điều khiển trên đó trao đổi và đồng bộ dữ liệu với nhau. Việc lẫn lộn giữa chúng là rất thường xuyên. Kiểu làm thủ công này cũng rất rắc rối khi cần đồng bộ dữ liệu giữa các điều khiển.

Không phân biệt giữa giao diện – logic – dữ liệu

Ở trên có nói tới việc bạn thường xuyên nhồi nhét đủ mọi loại code vào file code-behind của form. Nếu bạn phân biệt được rạch ròi các thành phần chính của ứng dụng thì có thể mọi thứ sẽ khác.

Nhìn một cách chung nhất, mỗi ứng dụng thường phân biệt rõ 3 thành phần cơ bản: giao diện người dùng, logic, dữ liệu. Mỗi thành phần này có mục tiêu khác nhau, kỹ thuật xử lý khác nhau. Do đó, không thể trộn lẫn lộn với nhau được.

Giao diện người dùng được tạo ra bởi Form và các điều khiển bạn đặt trên nó. Nhiệm vụ của nó là giúp người dùng nhập dữ liệu, và hiển thị dữ liệu (đã xử lý) trở lại cho người dùng.

Logic là các quy tắc chi phối việc xử lý dữ liệu. Anh này thường âm thầm đứng sau chứ ít khi lộ diện và do đó ít khi được để ý tới. Ví dụ, khi người dùng cần một danh sách email sắp xếp theo thứ tự abc. Anh logic phải làm việc này. Còn anh giao diện chỉ làm nhiệm vụ hiển thị nó ra.

Dữ liệu là thứ trung tâm, cả anh giao diện và logic đều phải làm việc với anh dữ liệu. Tuy nhiên, anh dữ liệu cũng đòi hỏi có nhà riêng cho nó ở. Đó là file dữ liệu, hoặc một cơ sở dữ liệu. Khi cần, dữ liệu sẽ rời nhà đến làm việc với logic và giao diện. Xong việc, dữ liệu sẽ quay về nhà ở.

Như vậy, nếu phân biệt rõ ba anh này, bạn chắc chắn sẽ thấy chúng nó không thể ở chung với nhau được. Mỗi anh cần không gian riêng cho mình.

Một số giải pháp đơn giản cho các vấn đề của lập trình winform

Do nắm được các vấn đề trên, chúng tôi đưa ra một số giải pháp khắc phục đơn giản nhưng hiệu quả. Bất kỳ bạn nào cũng có có thể nắm được trong thời gian ngắn. Các giải pháp này hướng tới mấy vấn đề.

Phát huy các chức năng hỗ trợ thiết kế giao diện của winform

Việc đầu tiên là chúng ta phải phát huy được khả năng binding và hỗ trợ thiết kế giao diện khá mạnh của windows form. Ở đây chúng ta sẽ tận dụng BindingSource làm nguồn dữ liệu cho các điều khiển, thay vì đổ/nhận dữ liệu thủ công. BindingSource cũng rất quan trọng khi sử dụng giao diện thiết kế form. Nội dung này được trình bày ở phần 2 – thiết kế giao diện với Data Sources và BindingSource

Cũng trong phần 2, chúng ta sẽ đưa ra một mô hình kiến trúc UI đơn giản cho winform. Mô hình này sẽ giúp định hướng cho việc thiết kế giao diện. Theo mô hình này, giao diện không thực hiện bất kỳ việc tính toán nào, chỉ hiển thị dữ liệu và nhận input từ user. Từ đó hạn chế tối đa code behind của form (giao diện) để có thể dễ dàng đổi sang giao diện mới nếu cần thiết.

Khi tách rời các thành phần, yêu cầu đặt ra là phải đồng bộ hóa dữ liệu giữa phần logic và giao diện. Databinding là kỹ thuật chính được sử dụng cho mục đích này. Do vậy, ở phần 5 – sử dụng các kỹ thuật Databinding cho giao diện chúng ta sẽ xem xét kỹ hơn nữa vấn đề này. Khi sử dụng hợp lý Databinding, việc đọc/ghi/đồng bộ hóa dữ liệu giữa các điều khiển sẽ đơn giản hơn rất nhiều. Điều này rất có lợi khi viết phần logic xử lý dữ liệu.

Phân chia project ra các thành phần độc lập

Chúng ta phải tách project windows forms ra các thành phần tương đối độc lập, bao gồm phần xử lý logic/tính toán, phần giao diện, và phần dữ liệu. Khi tách ra làm các thành phần, bạn có thể sử dụng kỹ thuật riêng cho từng anh. Vấn đề phân chia project ra các thành phần độc lập được trình bày chi tiết ở phần 3 – phân chia code thành module sử dụng interface và nguyên lý loose-coupling.

Toàn bộ các tính toán của mỗi form được đưa vào một class riêng cho form đó. Các sự kiện trên một form sẽ được delegate sang thực hiện trong một class tương ứng.

Khi thực hiện tốt việc phân chia code, mặc dù nhìn project sẽ phức tạp hơn nhưng có lợi ích lớn nếu bạn cần thay thế/ thay đổi/ nâng cấp một thành phần mà không ảnh hưởng đến những phần còn lại. Sẽ rất có ích khi bạn đem báo cáo bài cho giáo viên đấy.

Khai thác thư viện control của bên thứ 3

Thực tế, các điều khiển nguyên bản của winform rất xấu, hiệu suất thấp, và thiếu nhiều tính năng hữu ích. Do vậy, nhiều bên thứ 3 đã phát triển các bộ thư viện điều khiển riêng cho winform. Các bộ điều khiển này thường tốt hơn nhiều so với bộ gốc.

Do đó ở phần 4 chúng ta sẽ xem cách sử dụng bộ thư viện DevExpress cho winform.

Các thư viện điều khiển này giúp ích rất lớn cho việc thiết kế giao diện, giúp đơn giản hóa nhiều công việc vốn rắc rối nếu dùng các điều khiển gốc. Ngoài ra, giao diện thiết kế với các điều khiển mới nhìn chuyên nghiệp hơn nhiều.

Sử dụng cơ sở dữ liệu

Một chương trình thường không thể thiếu được một cơ sở dữ liệu đứng sau lưng. Với ứng dụng trên .NET framework, SQL Server gần như là cơ sở dữ liệu phổ biến nhất. Do đó chúng ta sẽ xem xét cách lưu trữ và truy xuất dữ liệu từ cơ sở dữ liệu quan hệ Sql Server.

Tuy nhiên, để đơn giản hóa tối đa việc truy xuất dữ liệu, chúng ta sẽ trực tiếp xem xét cách sử dụng ORM Entity Framework từ winform. Thư viện này sẽ giải phóng chúng ta khỏi ngôn ngữ SQL và các thao tác nhàm chán khi làm việc với cơ sở dữ liệu quan hệ.

Các giải pháp trên nghe có vẻ phức tạp nhưng đến giai đoạn thực hiện các bạn sẽ thấy nó khá đơn giản. Để thể hiện rõ các giải pháp đã nêu trên “thực địa”, chúng ta sẽ áp dụng nó trong một project đơn giản xuyên suốt 5 phần còn lại của chuỗi bài này.

Hãy đọc tiếp về bài toán minh họa này trong phần dưới đây.

Bài toán minh họa sử dụng trong Series Giải pháp lập trình winform

Chúng ta tiếp cận theo kiểu “làm để học” thông qua thực hiện một ví dụ minh họa. Chúng ta sẽ cùng thực hiện một project xây dựng một ứng dụng quản lý danh bạ đơn giản. Project này sẽ được sử dụng và nâng cấp dần trong 5 phần còn lại của series này.

Trong bài này, chúng ta sẽ phân tích bài toán, xây dựng các class và cấu trúc cơ bản trước.

Yêu cầu

Xây dựng một ứng dụng đơn giản giúp quản lý danh bạ.

Mỗi bản ghi trong danh bạ chứa các thông tin:

  • Tên liên hệ (contact name),
  • Biệt danh (alias),
  • Họ + đệm (first name),
  • Tên (last name),
  • Ngày sinh (birth day),
  • Danh sách email, mỗi email trong danh sách bao gồm loại email (cá nhân, công việc, tổ chức) và địa chỉ email tương ứng.
  • Danh sách số điện thoại, mỗi thông tin về điện thoại bao gồm loại (cá nhân, công việc), và số điện thoại tương ứng.

Ứng dụng khi chạy sẽ liệt kê danh sách contact trong danh bạ dưới dạng bảng. Có thể trực tiếp sửa thông tin ngay trong bảng.

Ứng với mỗi contact sẽ thực hiện đủ 4 thao tác CRUD (Create-Retrieve-Update-Delete). Khi chọn một contact bất kỳ sẽ hiển thị thêm thông tin chi tiết trên một form riêng.

Ở các phần đầu, để giữ ứng dụng đơn giản cho mục đích của bài, chúng ta không sử dụng cơ sở dữ liệu quan hệ mà sẽ lưu dữ liệu vào file và đọc từ file. Đến phần cuối của loạt bài chúng ta mới sử dụng cơ sở dữ liệu.

Xây dựng solution

Trong phần này chúng ta sẽ tạo ra một solution với các project chính sẽ sử dụng. Bạn có thể sử dụng visual studio community 2017 hoặc 2019 (mới nhất).

Bước 1. Tạo một solution trống ContactManager (New Project => Other Project Types => Visual Studio Solutions => Blank Solution

Tạo solution trống ContactManager

Bước 2. Thêm vào solution trên một project thuộc loại Class Library (.NET Framework) đặt tên là Models

Thêm thư viện Models

Bước 3. Thêm tiếp một project thuộc lại Windows Forms App (.NET Framework) vào solution và đặt tên là App.

Thêm winform project

Solution giờ có hình dạng như sau:

Solution ContactManager

Bước 4. Cho App tham chiếu sang Models

App tham chiếu sang Models
App tham chiếu sang Models

Xây dựng thư viện Models

Trong project Models tạo ba class mới, đặt tên lần lượt là Contact (file Contact.cs), Email (file Email.cs) và Phone (file Phone.cs). Viết code cho các class này như sau:

using System;
using System.Collections.Generic;

namespace Models
{
    [Serializable]
    public class Contact
    {
        public int Id { get; set; }
        public string ContactName { get; set; }
        public string Alias { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public DateTime DateOfBirth { get; set; } = DateTime.Now;
        public List<Email> Emails { get; set; } = new List<Email>();
        public List<Phone> Phones { get; set; } = new List<Phone>();
    }

}
using System;

namespace Models
{
    [Serializable]
    public class Email
    {
        public int Id { get; set; }
        public string Type { get; set; }
        public string EmailAddress { get; set; }
    }

}
using System;

namespace Models
{
    [Serializable]
    public class Phone
    {
        public int Id { get; set; }
        public string Type { get; set; }
        public string Number { get; set; }
    }

}

Để ý cả ba class này đều đánh dấu [Serializable] để sau có thể sử dụng BinaryFormatter để đoc/ghi dữ liệu với file.

Các class của thư viện Models

Chúng ta tạm dừng ở đây.

Kết phần

Trong bài viết này chúng ta đã điểm qua một số lỗi thường gặp khi bạn mới sử dụng winform để làm đề tài. Từ các lỗi đó, chúng ta đã vạch ra một số hướng giải quyết. Từ các bài sau, chúng ta sẽ lần lượt đi sâu vào từng vấn đề cụ thể.

Để minh họa, chúng ta đã đưa ra một bài toán và xây dựng một số cấu trúc đầu tiên cho project. Bài toán này sẽ được sử dụng xuyên suốt trong 5 phần còn lại của series bài này.

Nếu có thắc mắc hoặc cần trao đổi thêm, mời bạn viết trong phần Thảo luận cuối trang.
Nếu cần trao đổi riêng với chúng tôi, hãy gửi email hoặc nhắn tin qua form liên hệ.
Nếu bài viết hữu ích với bạn, hãy giúp chúng tôi chia sẻ tới mọi người.
Đăng ký theo dõi trang facebook để nhận thông tin về bài viết mới.
Tắt Adblocker hoặc whitelist trang để hỗ trợ chúng tôi.
Cảm ơn bạn!

Loạt bài “Các giải pháp dành cho lập trình winform”:
Phần 1 – Lỗi thường gặp trong lập trình winforms
Phần 2 – Thiết kế giao diện với Data Sources và BindingSource
Phần 3 – Phân chia code thành module sử dụng Interface
Phần 4 – Sử dụng thư viện DevExpress cho winforms
Phần 5 – Sử dụng Data Binding
Phần 6 – Sử dụng Entity Framework

* Bản quyền bài viết thuộc về Tự học ICT. Đề nghị tôn trọng bản quyền. DMCA.com Protection Status
Subscribe
Notify of
guest
3 Thảo luận
Oldest
Newest
Inline Feedbacks
View all comments
Trung Minh

sao phân dob, email, phone mình khai báo trong Contacts bao loi!

Mai Chi

Nó báo lỗi gì ở đó vậy bạn? Đây là file code rất cơ bản. Bạn kiểm tra thử xem có hai lệnh using ở đầu file không. Thứ hai là kiểm tra xem namespace của class có phải là Models không.

Last edited 3 months ago by Mai Chi
Trung Minh

Minh cảm ơn bạn, đã hỗ trợ!