ฉันมีเคล็ดลับสำหรับคนที่บอกว่าTypeDescriptionProvider
โดย Juan Carlos Diaz ใช้งานไม่ได้และไม่ชอบการรวบรวมแบบมีเงื่อนไขเช่นกัน:
ก่อนอื่นคุณอาจต้องรีสตาร์ท Visual Studioเพื่อให้การเปลี่ยนแปลงในโค้ดของคุณทำงานในตัวออกแบบฟอร์ม (ฉันต้องสร้างใหม่อย่างง่ายไม่ได้ผล - หรือไม่ทุกครั้ง)
ฉันจะนำเสนอวิธีแก้ปัญหานี้สำหรับกรณีของแบบฟอร์มฐานนามธรรม สมมติว่าคุณมีBaseForm
ชั้นเรียนและคุณต้องการให้รูปแบบใด ๆ จากพื้นฐานนั้นสามารถออกแบบได้ (สิ่งนี้จะเป็นForm1
) สิ่งTypeDescriptionProvider
ที่ Juan Carlos Diaz นำเสนอก็ไม่ได้ผลสำหรับฉันเช่นกัน นี่คือวิธีที่ฉันทำให้มันใช้งานได้โดยการเข้าร่วมกับโซลูชัน MiddleClass (โดย smelch) แต่ไม่มีการ#if DEBUG
รวบรวมตามเงื่อนไขและมีการแก้ไขบางอย่าง:
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<BaseForm, BaseFormMiddle2>))]
public abstract class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
}
public abstract void SomeAbstractMethod();
}
public class Form1 : BaseForm
{
public Form1()
{
InitializeComponent();
}
public override void SomeAbstractMethod()
{
}
}
สังเกตแอตทริบิวต์บนคลาส BaseForm จากนั้นคุณก็ต้องประกาศTypeDescriptionProvider
และสองชั้นกลางแต่ไม่ต้องกังวลพวกเขาจะมองไม่เห็นและไม่เกี่ยวข้องสำหรับนักพัฒนาของ Form1 อันแรกใช้สมาชิกนามธรรม (และทำให้คลาสฐานไม่ใช่นามธรรม) อันที่สองว่างเปล่า - จำเป็นสำหรับผู้ออกแบบฟอร์ม VS จึงจะทำงานได้ จากนั้นคุณกำหนดสองชั้นกลางไปของTypeDescriptionProvider
ไม่มีการรวบรวมตามเงื่อนไขBaseForm
ฉันมีปัญหาอีกสองอย่าง:
- ปัญหาที่ 1:หลังจากเปลี่ยน Form1 ในตัวออกแบบ (หรือรหัสบางตัว) ก็แสดงข้อผิดพลาดอีกครั้ง (เมื่อพยายามเปิดในตัวออกแบบอีกครั้ง)
- ปัญหาที่ 2:ตัวควบคุมของ BaseForm ถูกวางอย่างไม่ถูกต้องเมื่อขนาดของ Form1 ถูกเปลี่ยนในตัวออกแบบและฟอร์มถูกปิดและเปิดใหม่อีกครั้งในตัวออกแบบฟอร์ม
ปัญหาแรก (คุณอาจไม่มีเพราะเป็นสิ่งที่หลอกหลอนฉันในโครงการของฉันไม่กี่แห่งและมักจะสร้างข้อยกเว้น "ไม่สามารถแปลงประเภท X เป็นประเภท X") ฉันแก้ไขTypeDescriptionProvider
โดยการเปรียบเทียบชื่อประเภท (FullName) แทนที่จะเปรียบเทียบประเภท (ดูด้านล่าง)
ปัญหาที่สอง ฉันไม่รู้จริงๆว่าทำไมตัวควบคุมของแบบฟอร์มพื้นฐานจึงไม่สามารถออกแบบได้ในคลาส Form1 และตำแหน่งของพวกเขาจะหายไปหลังจากปรับขนาด แต่ฉันได้แก้ไขแล้ว (ไม่ใช่วิธีแก้ปัญหาที่ดี - ถ้าคุณรู้ดีกว่านี้โปรดเขียน) ฉันเพิ่งย้ายปุ่มของ BaseForm ด้วยตนเอง (ซึ่งควรอยู่ที่มุมล่างขวา) ไปยังตำแหน่งที่ถูกต้องในวิธีการที่เรียกใช้แบบอะซิงโครนัสจากเหตุการณ์ Load ของ BaseForm: BeginInvoke(new Action(CorrectLayout));
คลาสฐานของฉันมีเพียงปุ่ม "ตกลง" และ "ยกเลิก" ดังนั้น กรณีเป็นเรื่องง่าย
class BaseFormMiddle1 : BaseForm
{
protected BaseFormMiddle1()
{
}
public override void SomeAbstractMethod()
{
throw new NotImplementedException();
}
}
class BaseFormMiddle2 : BaseFormMiddle1
{
}
และที่นี่คุณมีเวอร์ชันแก้ไขเล็กน้อยของ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)
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)
objectType = typeof(TBase);
return base.CreateInstance(provider, objectType, argTypes, args);
}
}
เท่านี้เอง!
คุณไม่จำเป็นต้องอธิบายอะไรกับนักพัฒนารูปแบบในอนาคตโดยอิงจาก BaseForm ของคุณและพวกเขาไม่ต้องใช้กลเม็ดใด ๆ ในการออกแบบฟอร์ม! ฉันคิดว่ามันเป็นโซลูชันที่สะอาดที่สุดเท่าที่จะเป็นไปได้ (ยกเว้นการเปลี่ยนตำแหน่งการควบคุม)
อีกหนึ่งเคล็ดลับ:
หากนักออกแบบยังคงปฏิเสธที่จะทำงานให้คุณด้วยเหตุผลบางประการคุณสามารถทำเคล็ดลับง่ายๆในการเปลี่ยนpublic class Form1 : BaseForm
to public class Form1 : BaseFormMiddle1
(หรือBaseFormMiddle2
) ในไฟล์โค้ดแก้ไขได้ใน VS form designer แล้วเปลี่ยนกลับอีกครั้ง ฉันชอบเคล็ดลับนี้มากกว่ารวบรวมเงื่อนไขเพราะมันมีโอกาสน้อยที่จะลืมและปล่อยรุ่นที่ไม่ถูกต้อง