Tùy chỉnh giao diện và chức năng của ứng dụng XAF

    1

    Trong bài học trước chúng ta đã cùng thực hiện xây dựng mô hình dữ liệu sử dụng Entity Framework Core. Từ mô hình dữ liệu, XAF sẽ tự động sinh ra giao diện và các chức năng xử lý dữ liệu, còn EF Core tự động sinh ra cấu trúc cơ sở dữ liệu. Tuy nhiên, trong quá trình xây dựng ứng dụng có thể phát sinh các tình huống như:

    • bạn có thể tiếp tục cập nhật mô hình dữ liệu,
    • giao diện sinh ra chưa phù hợp với yêu cầu của bạn,
    • bạn muốn thêm các tính năng khác với những gì XAF cung cấp sẵn (như thêm các nút bấm để thực hiện các tác vụ, thay đổi luồng xử lý thông thường của ứng dụng, v.v.).

    XAF cung cấp cơ chế thực hiện hết các vấn đề đặt ra như vậy. Trong bài học này, chúng ta sẽ tiếp tục làm quen với khả năng tủy chỉnh giao diện và chức năng của ứng dụng XAF.

    Khả năng tùy chỉnh giao diện của ứng dụng XAF

    Trong XAF, mô hình dữ liệu xác định cấu trúc cơ sở dữ liệu và giao diện người dùng. Ứng với mỗi lớp thực thể, XAF sẽ tự động tạo ra 3 loại giao diện:

    • Giao diện form chi tiết, gọi là DetailView;
    • Giao diện danh sách, gọi là ListView;
    • Giao diện danh sách tìm kiếm, gọi là LookupListView.

    Ví dụ, với lớp thực thể Customer, XAF sẽ tự động tạo ra 3 giao diện có tên là Cusomter_DetailView, Customer_ListView, Customer_LookupListView. Ngoài ra, XAF cũng tạo ra một số giao diện khác cho các thuộc tính kiểu danh sách.

    Khi sinh giao diện tự động cần lưu ý:

    • Vị trí của điều khiển tương đương với vị trí của thuộc tính trong lớp thực thể. Ví dụ, nếu có thuộc tính DateOfBirth vào sau thuộc tính LastName thì điều khiển xuất hiện ở vị trí tương ứng.
    • Kiểu của điều khiển được lựa chọn tự động phù hợp với kiểu của thực thể. Ví dụ, thuộc tính DateOfBirth có kiểu DateTime thì XAF sẽ tự động lựa chọn điều khiển là DateTimePicker.

    Thông tin để tạo ra các giao diện này được gọi là metadata và lưu trong một file XML. Thông tin này sẽ được XAF sử dụng khi bắt đầu chạy ứng dụng. Do đó, chúng ta có nhiều cách khác nhau để can thiệp vào việc tạo giao diện của ứng dụng.

    Có những cách thức sau để tùy chỉnh giao diện:

    Thay đổi tự động

    Những thay đổi trong các lớp thực thể sẽ tự động tạo ra thay đổi tương ứng giao diện người dùng. Ví dụ, nếu thêm một thuộc tính mới vào một lớp thực thể, XAF sẽ tự động điều chỉnh giao diện như sau:

    • Sinh ra một điều khiển mới phù hợp với kiểu dữ liệu của thuộc tính đó trên DetailView;
    • Tự động tạo thêm một cột mới cho thuộc tính đó trên ListView.

    Thay đổi giao diện bằng attribute

    XAF cung cấp một số attribute áp dụng cho property để điều chỉnh cách sinh điều khiển tương ứng với thuộc tính. Trong phần thực hành tiếp theo chúng ta sẽ xem xét một số attribute thường dùng.

    Sử dụng model editor

    Model editor là một giao diện đồ họa để điều chỉnh metadata (tức là thông tin dùng để điều chỉnh cách sinh hoặc cách hoạt động) của một ứng dụng XAF, trong đó có thông tin về cách sinh giao diện. Chúng ta cũng sẽ xem xét cách sử dụng model editor cơ bản trong bài học này.

    Xây dựng controller

    Controller là một thành phần đặc biệt của ứng dụng XAF cho phép điều chỉnh gần như mọi khía cạnh của ứng dụng XAF khi chạy. Đây là thành phần mạnh nhất và cũng phức tạp nhất của XAF. Mọi tính năng nâng cao của ứng dụng XAF đều thực hiện bằng cách xây dựng controller tương ứng.

    Trong các phần sau đây chúng ta tiếp tục thực hành trên dự án đã xây dựng từ bài học trước.

    Thay đổi cấu trúc mô hình dữ liệu

    Lớp thực thể Customer hiện còn thiếu một thông tin quan trọng. Đó là ngày sinh của khách hàng. Hãy cùng bổ sung thêm thông tin này vào lớp Customer như sau:

    public class Customer : BaseObject {
        public virtual string FirstName { get; set; }
        public virtual string LastName { get; set; }
        public virtual DateTime DateOfBirth { get; set; } // bổ sung thuộc tính này
        public virtual string Email { get; set; }
    ...

    Lưu ý rằng, mặc dù XAF có thể tạo ra điều khiển mới tương ứng với thuộc tính mới của lớp thực thể, cấu trúc cơ sở dữ liệu bên dưới chưa thay đổi. Do đó ứng dụng sẽ bị lỗi ngay khi chạy thử nghiệm. Để đảm bảo ứng dụng hoạt động bình thường, hãy chạy lại các lệnh migration để yêu cầu EF Core cập nhật cấu trúc cơ sở dữ liệu:

    PM> add-migration AddDateOfBirth -startupproject SimpleProjectManager.Module -project SimpleProjectManager.Module
    PM> update-database -startupproject SimpleProjectManager.Module -project SimpleProjectManager.Module

    Nếu bây giờ bạn dịch và chạy chương trình, trên giao diện DetailView và ListView của Customer đã xuất hiện thêm trường DateOfBirth.

    Như vậy, XAF tự động sinh lại giao diện khi bạn cập nhật mô hình dữ liệu.

    Điều chỉnh giao diện sử dụng attribute

    Hình dung tình huống khi khách hàng gửi lời nhắn (chứa trong trường Quote của lớp Testimonial). Chúng ta muốn rằng lời nhắn có thể có độ dài tùy ý, và ô nhập dữ liệu cũng phải cho phép nhập nhiều dòng văn bản. Hiện tại, ô nhập dữ liệu của trường Quote chỉ hiển thị được 1 dòng.

    Mở file Testimonial.cs và điều chỉnh code như sau:

    // ...
    namespace SimpleProjectManager.Module.BusinessObjects {
        // ...
        public class Testimonial {
            [FieldSize(FieldSizeAttribute.Unlimited)]
            public virtual string Quote { get; set; }
            // ...
        }
    }

    Hãy để ý dòng số 7. Chúng ta áp dụng attribute [FieldSize(FieldSizeAttribute.Unlimited)] cho thuộc tính Quote. Attribute này giúp chúng ta đặt giới hạn về lượng ký tự mà điều khiển tương ứng có thể chấp nhận. Attribute FieldSize có thể chấp nhận một giá trị đặc biệt: FieldSizeAttribute.Unlimited. Đây là một hằng số định nghĩa sẵn thể hiện điều khiển có thể nhận không hạn chế số lượng ký tự. Khi đặt giá trị này, XAF sẽ thay đổi hoàn toàn cách thể hiện của điều khiển.

    Đọc bài viết về attribute trong C# nếu bạn chưa nắm được vấn đề này.

    Chạy chương trình và bạn sẽ thu được kết quả như sau:

    Bạn có thể thấy, điều khiển cho thuộc tính Quote giờ đây là một ô nhập văn bản nhiều dòng, thay vì là một dòng như ban đầu.

    FieldSize chỉ là một trong số rất nhiều attribute được XAF xây dựng sẵn phục vụ cho việc tinh chỉnh giao diện trực tiếp trong quá trình xây dựng lớp thực thể. Trong quá trình học chúng ta sẽ còn tiếp xúc với nhiều attribute khác nữa.

    Điều chỉnh giao diện sử dụng Model Editor

    Tất cả thông tin về mô hình dữ liệu (tức là các lớp thực thể) được gọi là metadata và được lưu trong một file XML gọi là Application Model. File này có phần mở rộng là XAMFL và gặp trong tất cả các dự án của XAF. File XML này chứa mọi thông tin cần thiết để XAF tự động sinh ra các thành phần của ứng dụng, trong đó có giao diện đồ họa. XAF cung cấp sẵn một chương trình tích hợp trong Visual Studio, gọi là Model Editor, để hỗ trợ làm việc với file XML này.

    Hãy thực hiện theo các bước sau:

    (1) Tìm và click đúp vào file Model.DesignedDiffs.xafml trong dự án SimpleProjectManager.Module. Thao tác này sẽ mở ra chương trình Model Editor.

    (2) Nhìn trong danh sách các node bên tay trái, tìm đến node BOModel | SimpleProjectManager.Module.BusinessObjects | Customer. Khi click vào nút Customer sẽ mở ra danh sách các tham số có thể điều chỉnh trong cửa sổ bên phải.

    (3) Hãy cập nhật giá trị của thuộc tính ObjectCaptionFormat thành {0:FullName}.

    (4) Chạy ứng dụng và bạn có thể thấy sự thay đổi trên giao diện:

    ObjectCaptionFormat là một thuộc tính chỉ định cách thức hiển thị thông tin của một thực thể trên tab (gọi là caption) của DetailView. Mặc định, XAF sẽ sử dụng giá trị của trường đầu tiên có kiểu string trong lớp thực thể làm caption. Nếu bạn muốn caption hiển thị thông tin của một trường khác (trong trường hợp này là trường FullName), bạn có thể thay đổi nó trong Model Editor theo cách như chúng ta đã làm.

    Đây là một ví dụ đơn giản về cách tùy chỉnh giao diện sử dụng Model Editor. Bạn có thể thực hiện rất nhiều thao tác tùy chỉnh giao diện khác, kể cả thiết kế lại hoàn toàn giao diện, trên Model Editor. Chúng ta sẽ quay lại vấn đề này trong một bài học sâu hơn về thiết kế giao diện trong XAF.

    Sử dụng controller

    Controller là một loại class do người lập trình tự xây dựng để điều chỉnh và can thiệp gần như mọi khía cạnh của một ứng dụng XAF. Đây là công cụ mạnh mẽ nhất làm nên sức mạnh thực sự của XAF. Khi sử dụng controller, bạn có thể thực hiện được rất nhiều việc khác nhau: (1) Tạo thêm các nút bấm để người dùng tương tác với dữ liệu; (2) Thay đổi luồng thực thi thông thường của ứng dụng; (3) Thay đổi cách thức hiện thị của dữ liệu; (4) Tạo ra các chức năng mới cho ứng dụng.

    Việc sử dụng controller tương đối phức tạp, đòi hỏi sự hiểu biết sâu về cách tương tác với dữ liệu và giao diện trong ứng dụng XAF, và cũng đòi hỏi nhiều kỹ thuật lập trình nâng cao. Trong bài học này chúng ta sẽ sử dụng controller ở mức độ đơn giản để thêm một nút bấm tiện ích. Hãy thực hiện theo các bước sau:

    (1) Mở dự án Module, click phải chuột vào thư mục Controllers và thêm một file code mới đặt tên là ProjectTaskController.cs.

    (2) Viết code như sau cho file này:

    using DevExpress.ExpressApp;
    using SimpleProjectManager.Module.BusinessObjects;
    namespace SimpleProjectManager.Module.Controllers {
        public class ProjectTaskController : ViewController {
            public ProjectTaskController() {
                // chỉ định kiểu thực thể nào sẽ bị lớp Controller này tác động.
                TargetObjectType = typeof(ProjectTask);
                // chỉ định loại View nào sẽ bị lớp controller này tác động
                TargetViewType = ViewType.Any;
                SimpleAction markCompletedAction = new SimpleAction(this, "MarkCompleted", 
                    DevExpress.Persistent.Base.PredefinedCategory.RecordEdit) {
                    TargetObjectsCriteria = 
                    (CriteriaOperator.FromLambda<ProjectTask>(t => t.Status != ProjectTaskStatus.Completed)).ToString(),
                    ConfirmationMessage =
                    "Are you sure you want to mark the selected task(s) as 'Completed'?",
                    ImageName = "State_Task_Completed"
                };
                markCompletedAction.SelectionDependencyType = SelectionDependencyType.RequireMultipleObjects;
                markCompletedAction.Execute += (s, e) => {
                    foreach (ProjectTask task in e.SelectedObjects) {
                        task.EndDate = DateTime.Now;
                        task.Status = ProjectTaskStatus.Completed;
                        View.ObjectSpace.SetModified(task);
                    }
                    View.ObjectSpace.CommitChanges();
                    View.ObjectSpace.Refresh();
                };
            }
        }
        // ...
    }

    Chạy thử chương trình và mở mục Project Task, chọn một bản ghi bất kỳ, bạn có thể thực hiện được thao tác như sau:

    Bạn có thể để ý thấy là trên thanh công cụ của giao diện DetailView của Project Task đã xuất hiện thêm một nút bấm mới, Mark Completed, mà khi bấm vào, bản ghi hiện tại sẽ chuyển trạng thái thành Completed. Như vậy, bạn đã thêm được một nút bấm vào giao diện để người dùng có thể kích hoạt một tính năng của ứng dụng.

    Controller và action trong XAF

    Trong đoạn code trên, bạn đã tạo ra một class mới ProjectTaskController kế thừa từ lớp ViewController. ViewController là lớp do XAF xây dựng sẵn nhằm giúp điều chỉnh những gì liên quan đến giao diện. Những chi tiết cần điều chỉnh được thể hiện trong class con của ViewController.

    Bên trong hàm tạo của ProjectTaskController, bạn chỉ định kiểu thực thể và loại view nào sẽ chịu tác động của controller này:

    // chỉ định kiểu thực thể nào sẽ bị lớp Controller này tác động.
    TargetObjectType = typeof(ProjectTask);
    //chỉ định loại View nào sẽ bị lớp controller này tác động
    TargetViewType = ViewType.Any;

    Ở đây chúng ta chỉ định controller này chỉ tác động lên các bản ghi kiểu ProjectTask trên bất kỳ view nào. Các bản ghi thuộc kiểu khác sẽ không chịu tác động của controller này.

    Hãy nhớ lại đầu bài học này chúng ta đã nói rằng mặc định XAF tạo ra 3 loại view: DetailView, ListView, LookupListView. Mỗi controller có thể tác động lên một hoặc một số hoặc tất cả các view này. Trong code ví dụ, hằng số ViewType.Any thể hiện nó sẽ tác động lên tất cả các loại view của ProjectTask.

    Tiếp theo chúng ta tạo ra một đối tượng của lớp SimpleAction. Lớp SimpleAction giúp tạo ra một nút bấm đơn giản trên thanh công cụ. Nút bấm đơn giản là loại nút bấm cơ bản, không có thêm tính năng gì khác (như nút xổ ra, ô nhập tham số, mở cửa sổ popup). Người dùng chỉ bấm nút là tính năng có thể kích hoạt.

    SimpleAction markCompletedAction = new SimpleAction(this, "MarkCompleted", 
                    DevExpress.Persistent.Base.PredefinedCategory.RecordEdit) {
                    TargetObjectsCriteria = 
                    (CriteriaOperator.FromLambda<ProjectTask>(t => t.Status != ProjectTaskStatus.Completed)).ToString(),
                    ConfirmationMessage =
                    "Are you sure you want to mark the selected task(s) as 'Completed'?",
                    ImageName = "State_Task_Completed"
                };

    Khi tạo nút bấm chúng ta có thể chỉ định:

    Id của nút: trong ví dụ trên, MarkCompleted là Id của nút. Id là một chuỗi ký tự không trùng lặp để phân biệt nút này với các nút khác trong Application Model.

    Vị trí nút xuất hiện: trong ví dụ trên, nút bấm sẽ xuất hiện trong nhóm RecordEdit. Đây là nhóm do XAF định nghĩa sẵn dành cho các nút có tác dụng thay đổi dữ liệu.

    Tiêu chí để kích hoạt nút: nút bấm của XAF có đặc điểm là có thể chỉ định những trường hợp nút khả dụng. Trong ví dụ trên, nút bấm này chỉ khả dụng khi nhiệm vụ đang mở chưa hoàn thành. Nếu nhiệm vụ đang mở đã hoàn thành, nút bấm này sẽ bị mờ đi, người dùng không click được nữa. Tiêu chí kích hoạt nút được cung cấp qua thuộc tính TargetObjectsCriteria.

    Thông báo xác nhận: một popup đơn giản sẽ hiện thị thông báo xác nhận thực hiện lệnh. Thông báo xác nhận được cung cấp qua thuộc tính ConfirmationMessage.

    Ảnh đại diện của nút: được cung cấp qua thuộc tính ImageName. Có thể sử dụng ảnh trong thư viện sẵn có của XAF hoặc cung cấp từ ngoài. Giá trị của tham số này là tên ảnh cần sử dụng.

    Chỉ định hành động khi bấm nút: hành động khi bấm nút được cung cấp qua việc gán phương thức cho sự kiện Execute. Trong ví dụ trên, chúng ta chỉ đơn giản là gán giá trị thời gian hiện tại cho trường EndDate và chuyển giá trị trường Status thành Completed.

    markCompletedAction.Execute += (s, e) => {
                    foreach (ProjectTask task in e.SelectedObjects) {
                        task.EndDate = DateTime.Now;
                        task.Status = ProjectTaskStatus.Completed;
                        View.ObjectSpace.SetModified(task);
                    }
                    View.ObjectSpace.CommitChanges();
                    View.ObjectSpace.Refresh();
                };

    Để lưu lại những thay đổi vào cơ sở dữ liệu, chúng ta gọi phương thức View.ObjectSpace.CommitChanges().

    Ngoài SimpleAction, XAF cũng cung cấp thêm các class để tạo ra các loại nút bấm phức tạp hơn. Chúng ta sẽ xem xét cách tạo các loại nút bấm phức tạp trong một bài học riêng.

    XAF sử dụng cơ chế reflection để tự động tạo object của bất kỳ lớp nào kế thừa từ ViewController nên bạn không nhìn thấy code sử dụng lớp ProjectTaskController vừa tạo.

    + Nếu bạn thấy site hữu ích, trước khi rời đi hãy giúp đỡ site bằng một hành động nhỏ để site có thể phát triển và phục vụ bạn tốt hơn.
    + Nếu bạn thấy bài viết hữu ích, hãy giúp chia sẻ tới mọi người.
    + 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.
    Cảm ơn bạn!

    Kết luận

    Qua bài học này chúng ta đã làm quen với một khía cạnh khác của XAF, đó là tùy chỉnh giao diện. XAF cung cấp 3 phương pháp khác nhau để tùy chỉnh giao diện: sử dụng attribute, sử dụng model editor, và sử dụng controller.

    Trong 3 phương pháp này, controller là phương pháp phức tạp hơn cả do nó đòi hỏi viết code và hiểu biết chi tiết về hoạt động của XAF. Tuy nhiên, controller cũng là phương pháp mạnh nhất. Chúng ta sẽ có một số bài học riêng về controller và action.

    Theo dõi
    Thông báo của
    guest

    1 Thảo luận
    Cũ nhất
    Mới nhất
    Phản hồi nội tuyến
    Xem tất cả bình luận
    PhucTran

    Rất hữu ích