Tôi có một số lời khuyên cho những người nói rằng công cụ TypeDescriptionProvider
của Juan Carlos Diaz không hoạt động và không thích biên dịch có điều kiện:
Trước hết, bạn có thể phải khởi động lại Visual Studio để các thay đổi trong mã của bạn hoạt động trong trình thiết kế biểu mẫu (tôi phải làm vậy, xây dựng lại đơn giản không hoạt động - hoặc không phải lúc nào cũng vậy).
Tôi sẽ trình bày giải pháp của tôi cho vấn đề này cho trường hợp của Biểu mẫu cơ sở trừu tượng. Giả sử bạn có một BaseForm
lớp học và bạn muốn bất kỳ biểu mẫu nào dựa trên nó có thể được chỉ định (điều này sẽ là Form1
). Những TypeDescriptionProvider
gì được trình bày bởi Juan Carlos Diaz cũng không phù hợp với tôi. Đây là cách tôi làm cho nó hoạt động, bằng cách kết hợp nó với giải pháp MiddleClass (bởi smelch), nhưng không có#if DEBUG
biên dịch có điều kiện và với một số sửa đổi:
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<BaseForm, BaseFormMiddle2>))] // BaseFormMiddle2 explained below
public abstract class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
}
public abstract void SomeAbstractMethod();
}
public class Form1 : BaseForm // Form1 is the form to be designed. As you see it's clean and you do NOTHING special here (only the the normal abstract method(s) implementation!). The developer of such form(s) doesn't have to know anything about the abstract base form problem. He just writes his form as usual.
{
public Form1()
{
InitializeComponent();
}
public override void SomeAbstractMethod()
{
// implementation of BaseForm's abstract method
}
}
Lưu ý thuộc tính trên lớp BaseForm. Sau đó, bạn chỉ cần khai báo TypeDescriptionProvider
và hai lớp giữa , nhưng đừng lo lắng, chúng vô hình và không liên quan đối với nhà phát triển của Form1 . Cái đầu tiên thực hiện các thành viên trừu tượng (và làm cho lớp cơ sở không trừu tượng). Cái thứ hai trống - nó chỉ cần thiết để trình thiết kế biểu mẫu VS hoạt động. Sau đó bạn gán thứ hai tầng lớp trung lưu cho TypeDescriptionProvider
của BaseForm
. Không có điều kiện biên dịch.
Tôi gặp phải hai vấn đề nữa:
- Sự cố 1: Sau khi thay đổi Form1 trong trình thiết kế (hoặc một số mã), nó lại xuất hiện lỗi (khi cố gắng mở lại trong trình thiết kế).
- Vấn đề 2: Các điều khiển của BaseForm được đặt không chính xác khi kích thước của Form1 được thay đổi trong trình thiết kế và biểu mẫu đã bị đóng và mở lại trong trình thiết kế biểu mẫu.
Vấn đề đầu tiên (bạn có thể không mắc phải vì đó là thứ ám ảnh tôi trong dự án của tôi ở một vài nơi khác và thường tạo ra ngoại lệ "Không thể chuyển loại X thành loại X"). Tôi đã giải quyết nó TypeDescriptionProvider
bằng cách so sánh tên kiểu (FullName) thay vì so sánh các kiểu (xem bên dưới).
Vấn đề thứ hai. Tôi thực sự không biết tại sao các điều khiển của biểu mẫu cơ sở không thể chỉ định trong lớp Form1 và vị trí của chúng bị mất sau khi thay đổi kích thước, nhưng tôi đã giải quyết vấn đề đó (không phải là một giải pháp hay - nếu bạn biết bất kỳ điều gì tốt hơn, vui lòng viết). Tôi chỉ cần di chuyển các nút của BaseForm theo cách thủ công (phải ở góc dưới bên phải) đến vị trí chính xác của chúng trong một phương thức được gọi không đồng bộ từ sự kiện Tải của BaseForm: BeginInvoke(new Action(CorrectLayout));
Lớp cơ sở của tôi chỉ có các nút "OK" và "Hủy", vì vậy trường hợp là đơn giản.
class BaseFormMiddle1 : BaseForm
{
protected BaseFormMiddle1()
{
}
public override void SomeAbstractMethod()
{
throw new NotImplementedException(); // this method will never be called in design mode anyway
}
}
class BaseFormMiddle2 : BaseFormMiddle1 // empty class, just to make the VS designer working
{
}
Và ở đây bạn có phiên bản sửa đổi một chút của TypeDescriptionProvider
:
public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
public AbstractControlDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(TAbstract)))
{
}
public override Type GetReflectionType(Type objectType, object instance)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
return typeof(TBase);
return base.GetReflectionType(objectType, instance);
}
public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
objectType = typeof(TBase);
return base.CreateInstance(provider, objectType, argTypes, args);
}
}
Và đó là nó!
Bạn không cần phải giải thích bất cứ điều gì cho các nhà phát triển tương lai của biểu mẫu dựa trên BaseForm của bạn và họ không phải thực hiện bất kỳ thủ thuật nào để thiết kế biểu mẫu của mình! Tôi nghĩ đó là giải pháp sạch sẽ nhất có thể (ngoại trừ việc định vị lại các nút điều khiển).
Một mẹo nữa:
Nếu vì lý do nào đó mà trình thiết kế vẫn từ chối làm việc cho bạn, bạn luôn có thể thực hiện thủ thuật đơn giản là thay đổi public class Form1 : BaseForm
thành public class Form1 : BaseFormMiddle1
(hoặc BaseFormMiddle2
) trong tệp mã, chỉnh sửa nó trong trình thiết kế biểu mẫu VS và sau đó thay đổi lại. Tôi thích thủ thuật này hơn là biên dịch có điều kiện vì nó ít có khả năng quên và phát hành phiên bản sai hơn .