Toon posts:

[C#] Rant: non-visual componenten zijn een crime

Pagina: 1
Acties:
  • 177 views sinds 30-01-2008
  • Reageer

Verwijderd

Topicstarter
...om te schrijven!

Vooral componenten waar een list/collection van andere objecten inzitten.

In Delphi is dat simpel, zorg ervoor dat alles wat je designtime wilt kunnen benaderen published is, en 't komt goed. De items in je list komen in de dfm (zeg maar de .designer.cs van Delphi) netjes binnen de definitie van je hoofdcomponent te staan, ze krijgen een naampje (aanpasbaar), en die naam is runtime ook nog te gebruiken! :)

Niet bij C#...

Om te beginnen moeten alle objecten in je list al afgeleid zijn van Component, en een constructor hebben die een IContainer als parameter accepteert, anders worden je designtime aanpassingen aan zo'n object niet eens bewaard.

Vervolgens worden die items uit die lijst gewoon plompverloren aangemaakt via .designer.cs, zonder ook maar een enkele koppeling met het component dat die lijst beheert. Runtime moet het hoofdcomponent dan eerst op zoek naar de items die in z'n lijst horen, en die zelf toevoegen. En omdat runtime die componenten geen naam meer hebben (beter gezegd geen Site, zodat je hun Site.Name niet op kunt vragen), moet je al een overbodige property opnemen om ze uberhaupt op te kunnen zoeken.

Nog leuker wordt 't als je een component hebt waar je designtime een ander component aan kunt koppelen die ervoor zorgt dat automatisch een lijst in het hoofdcomponent wordt gevuld. Bv. een component waar je een DAL component aanhangt met alle velddefinities voor een tabel in je database. Bij mij worden dan automatisch o.a. de edit control types aan de lijst in het hoofdcomponent toegevoegd, maar die moeten designtime wel wijzigbaar blijven.
Designtime zijn die extra objecten prima te persisten via "this.Site.Container.Add()" (overbodig in Delphi), en dan staan ze ook netjes in .designer.cs. Maar bij de eerstvolgende keer dat je dan dat form designtime oproept, word je overspoeld met foutmeldingen: op 't moment dat VS probeert om dat DAL component aan m'n hoofdcomponent te koppelen (waardoor die lijst met extra componenten wordt aangemaakt) begint 'ie te miepen dat die componenten al bestaan.

Uiteindelijk was dat wel op te lossen door de DesignerHost op te vragen, en te checken of 'ie Loading was (zo ja, dan een delegate toevoegen aan z'n LoadCompleted event), maar gecompliceerd is 't wel...

Of ik doe iets heel erg verkeerd (redelijk beginner in C#), of Anders Hjelberg (architect van zowel Delphi als C#) is met de overgang naar Microsoft een stukje vergeten...

  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

Het is me niet geheel duidelijk wat je hier probeert te doen? Een component dat gekoppeld is aan een DAL die automagisch items laad bij OnLoad?
Zo ja dan moet je dat in die component regelen met eventueel een property waarmee je de DAL aangeeft.
Of anders instanties van objecten beheren via de designer? Daar is dat ding IMHO niet voor bedoeld.

[ Voor 40% gewijzigd door MTWZZ op 27-05-2006 15:21 ]

Nu met Land Rover Series 3 en Defender 90


Verwijderd

Topicstarter
Ik heb een DAL laag gemaakt waarin elke tabel wordt omgezet naar een RecordObj en een RecordSet class, en een code generator die de afgeleiden voor iedere database aan kan maken. Nu nog MSSQL only, maar dat is snel aan te passen aan andere DB's of een WSDL definitie.

Vervolgens heb ik een component gemaakt dat gebruik kan maken van die DAL, en collega-ontwikkelaars moeten daar gebruik van kunnen maken, en designtime dingen in kunnen stellen (welke velden wil ik in browse laten zien, welke zijn editbaar, welke zijn verplicht, waar hang ik m'n validatie delegate, etc.).

Werkt ondertussen al heel aardig, maar in Delphi was 't 10x sneller gegaan omdat je daar niet of nauwelijks hoeft na te denken of je in DesgnMode werkt, en je ook gewoon objecten designtime kunt benaderen (wanneer ze onderdeel van een component zijn), en wanneer ze published properties hebben, worden die ook automatisch opgeslagen.

  • MTWZZ
  • Registratie: Mei 2000
  • Laatst online: 13-08-2021

MTWZZ

One life, live it!

Ah vergelijkbaar met de DataSet en DataAdapter componenten dus.
Tsja je zult zelf die dingen volledig moeten uitprogrammeren met design-time visible properties etc. Die overigens dan wel automagisch voor je worden bijgehouden.

Ik heb geen Delphi ervaring maar ik wil juist wel weten of ik in designmode zit of niet, ivm het ophalen van allerlei meuk uit een DB of whatever. Dingen die je juist niet in je designer wil doen :)

Nu met Land Rover Series 3 en Defender 90


Verwijderd

Topicstarter
Inderdaad, DataSet achtig, maar dan lightweight, gebaseerd op DataReader, en daar pluk ik alleen de dingen uit die ik nodig heb.
De attributes om properties wel of niet zichtbaar te krijgen, in welke categorie, etc. zijn me ondertussen wel bekend. (ik heb me afgelopen week leip gegoogled en ge-msdn'd) :)

En wanneer je designtime dingen wilt ontwikkelen moet je je natuurlijk in attributes, etc. verdiepen.
Maar dat je er dan designtime ook nog rekening mee moet houden of de DesignerHost loading is of niet, is niet normaal. Zoek maar 's op hoe je designtime uberhaupt bepaalt wie die host is...

In Delphi is 't overigens ook geen probleem om te weten of je in DesignMode bent:
"if (csDesigning in ComponentState)"

[ Voor 7% gewijzigd door Verwijderd op 27-05-2006 16:28 ]


  • mulder
  • Registratie: Augustus 2001
  • Laatst online: 18:01

mulder

ik spuug op het trottoir

In Delphi is 't overigens ook geen probleem om te weten of je in DesignMode bent:
"if (csDesigning in ComponentState)"
In C# ook niet
C#:
1
if(this.DesignMode)

Tja C# is geen Delphi, dus je zult moet leren het kunstje op een andere manier te doen.

oogjes open, snaveltjes dicht


Verwijderd

Topicstarter
Die check op DesignMode is bij C# juist niet genoeg, je moet ook nog 's checken of de DesignerHost loading is of niet. Waarschijnlijk een gevolg van het feit dat .NET componenten designtime zich hetzelfde gedragen als runtime (op die IContainer in de constructor na).
Dit sluit wel aan bij MS's COM/ActiveX verleden, een component of control gaat in z'n eigen assembly, en dat ding hoeft niet te weten of 'ie runtime of designtime wordt aangeroepen.

Pas geleden een in-house cursus .NET/C# gehad, en de docent (zonder enige Delphi ervaring, z'n 6 toehoorders wel ;)) was best wel verbaasd dat je een grote Delphi applicatie kon deployen zonder ook maar 1 assembly of ActiveX dll mee te leveren...

Maar waar 't me echt om gaat is het probleem dat designtime de items in zo'n lijst/collection prima bij het hoofdcomponent lijken te horen, maar runtime blijft daar niks meer van over.
Je moet dus idd het kunstje op een andere manier doen, maar m.i. had dat beter gekund in C#/VS/.NET.
Hjelberg had met z'n Delphi achtergrond beter moeten weten... ;)

[ Voor 3% gewijzigd door Verwijderd op 27-05-2006 22:28 ]


  • EfBe
  • Registratie: Januari 2000
  • Niet online
Don Facundo schreef op zaterdag 27 mei 2006 @ 18:22:
[...]

In C# ook niet
C#:
1
if(this.DesignMode)

Tja C# is geen Delphi, dus je zult moet leren het kunstje op een andere manier te doen.
Nee dat klopt niet, tenzij je class derived van Component. Het is beter te checken of de Site property niet null is, dat is nl. een member van IComponent, de interface die je normaliter inplementeert in je classes wanneer je design time troep wilt in winforms.

Design time code is niet leuk, en dat is al jaren zo, zeker het debuggen is een ramp want bij een exception kan deze overal vandaan komen, er is immers geen stacktrace! :X
Verwijderd schreef op zaterdag 27 mei 2006 @ 22:26:
Die check op DesignMode is bij C# juist niet genoeg, je moet ook nog 's checken of de DesignerHost loading is of niet. Waarschijnlijk een gevolg van het feit dat .NET componenten designtime zich hetzelfde gedragen als runtime (op die IContainer in de constructor na).
Dit sluit wel aan bij MS's COM/ActiveX verleden, een component of control gaat in z'n eigen assembly, en dat ding hoeft niet te weten of 'ie runtime of designtime wordt aangeroepen.
Wanneer moet je dat checken? Dat hoeft helemaal niet. Je design time code moet passief zijn, dus wachten op actie van de gebruiker. Als je serialization/deserialization code werkt heb je geen omkijken naar het re-instantieren van een form met jouw components en hoef je niet te checken of de DesignerHost loading is of niet, ik heb dat nog nooit gemoeten.
Pas geleden een in-house cursus .NET/C# gehad, en de docent (zonder enige Delphi ervaring, z'n 6 toehoorders wel ;)) was best wel verbaasd dat je een grote Delphi applicatie kon deployen zonder ook maar 1 assembly of ActiveX dll mee te leveren...
Mja, 3rd party dlls blijven dlls, ook in delphi. Maar als delphi zo goed is, waarom gebruik je dat dan niet? Ik snap het niet zo hoor: als een taal / omgeving je zo tegenwerkt, dan gebruik je toch wat anders?
Maar waar 't me echt om gaat is het probleem dat designtime de items in zo'n lijst/collection prima bij het hoofdcomponent lijken te horen, maar runtime blijft daar niks meer van over.
Je moet dus idd het kunstje op een andere manier doen, maar m.i. had dat beter gekund in C#/VS/.NET.
Hjelberg had met z'n Delphi achtergrond beter moeten weten... ;)
Sinds wanneer is een taal == de omgeving/IDE ?

[ Voor 57% gewijzigd door EfBe op 28-05-2006 11:13 ]

Creator of: LLBLGen Pro | Camera mods for games
Photography portfolio: https://fransbouma.com


Verwijderd

Topicstarter
Wanneer moet je dat checken? Dat hoeft helemaal niet. Je design time code moet passief zijn, dus wachten op actie van de gebruiker. Als je serialization/deserialization code werkt heb je geen omkijken naar het re-instantieren van een form met jouw components en hoef je niet te checken of de DesignerHost loading is of niet, ik heb dat nog nooit gemoeten.
In 't voorbeeld dat ik noemde:
"Nog leuker wordt 't als je een component hebt waar je designtime een ander component aan kunt koppelen die ervoor zorgt dat automatisch een lijst in het hoofdcomponent wordt gevuld."

Ik zal best iets heel erg verkeerd doen, maar in dit geval is 't voor mij echt noodzakelijk om af te vangen of DesignHost Loading is of niet. Ik zal morgen wel even wat source van de setter posten waarbij dat lijst-component wordt gekoppeld aan het hoofdcomponent (heb nu de source niet bij de hand).
Maar als delphi zo goed is, waarom gebruik je dat dan niet?
Heb je de situatie rondom Delphi een beetje gevolgd?
- Borland heeft al z'n talen/ontwikkelomgevingen (Delphi, C++ Builder, JBuilder) in de verkoop gegooid.
- Delphi 2006 is nog steeds .NET 1.1 only (en Win32 native natuurlijk).
- Danny Thorpe (na Hjelberg de grootste Delphi architect ooit) is vorig jaar naar Google gegaan, en Borland heeft nog geen vervanger gevonden.
- 3rd party ontwikkelaars als Mark Miller (nu bij DevExpress), Ray Konopka (nu bij Fallafel) en Lino Tadros richten zich nu vrijwel volledig op C# en .NET.

Delphi zal ik altijd een fantastische taal/omgeving blijven vinden, en ook gebruiken wanneer 't kan, maar 't heeft nu een beetje de smaak van een zinkend schip (al zal David Intersimone 't daar niet mee eens zijn). Dan ga je kijken naar een troonopvolger, en C# is dan helemaal niet zo'n slechte keus... :)
Sinds wanneer is een taal == de omgeving/IDE ?
Niet, vandaar dat ik ook expliciet C#/VS/.NET noemde. (taal, ontwikkelomgeving, platform)

  • LordLarry
  • Registratie: Juli 2001
  • Niet online

LordLarry

Aut disce aut discede

Verwijderd schreef op zondag 28 mei 2006 @ 21:32:
- Danny Thorpe (na Hjelberg de grootste Delphi architect ooit) is vorig jaar naar Google gegaan
... en werkt nu bij Microsoft

We adore chaos because we like to restore order - M.C. Escher


Verwijderd

Topicstarter
Hee!!! Wist ik niet! Ik heb z'n blog wel alvast gebookmarked, dank je.

Geniale opmerking in z'n blog overigens:
Now if I could just get the silly thing to do what I want rather than what I write, we'd be making progress!
_/-\o_

[ Voor 56% gewijzigd door Verwijderd op 28-05-2006 22:13 ]


Verwijderd

Topicstarter
Ik zal morgen wel even wat source van de setter posten waarbij dat lijst-component wordt gekoppeld aan het hoofdcomponent (heb nu de source niet bij de hand).
Bij deze:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
    private RecordObj recordObject;

    public RecordObj RecordObject
    {
      get { return recordObject; }
      set
      {
        RecordObj oldValue = recordObject;
        recordObject = value;
        if (recordObject != null)
        {
          if ((parentForm != null) && (oldValue == null))
          {
            parentForm.Load += new EventHandler(ParentFormLoad);
          }
          if (DesignMode)
          {
            IDesignerHost host = Site.GetService(typeof(IDesignerHost)) as IDesignerHost;
            if ((host != null) && (host.Loading))
            {
              host.LoadComplete += new EventHandler(ParentFormLoad);
            }
            else
            {
              for (int i = 0; i < items.Count; i++)
              {
                Site.Container.Remove(items.GetItem(i));
              }
              items.Clear();

              for (int i = 0; i < RecordObject.FieldList.Count; i++)
              {
                string diName = Site.Name + General.CamelCase(RecordObject.FieldList[i].FieldName, true, false);

                DataDescriptionItem di = null;
                foreach (Component cmp in parentForm.Site.Container.Components)
                {
                  if (cmp is DataDescriptionItem)
                  {
                    DataDescriptionItem tst = (cmp as DataDescriptionItem);
                    if (tst.Site.Name == diName)
                    {
                      di = tst;
                      break;
                    }
                  }
                }

                if (di == null)
                {
                  di = new DataDescriptionItem(this);
                  di.FieldName = RecordObject.FieldList[i].FieldName;
                  di.TableName = RecordObject.DbTableName;
                  di.FieldObject = RecordObject.FieldList[i];
                  parentForm.Site.Container.Add(di, diName);
                }
                items.Add(di);
              }
            }
          }
          else if (oldValue != null)
          {
            for (int i = 0; i < recordObject.FieldList.Count; i++)
            {
              int maxLength = 0;
              DbBaseField fo = items.GetItem(i).FieldObject;
              if (fo is DbStringField)
                maxLength = (fo as DbStringField).MaxLength;
              if (maxLength > 0)
                (recordObject.FieldList[i] as DbStringField).MaxLength = maxLength;
              items.GetItem(i).FieldObject = recordObject.FieldList[i];
            }
            RebuildItemLists();
          }
        }
      }
    }

    public void ParentFormLoad(object sender, EventArgs e)
    {
      if ((Site != null) && (Site.Container != null))
      {
        for (int i = 0; i < RecordObject.FieldList.Count; i++)
        {
          DataDescriptionItem di = null;
          foreach (Component cmp in Site.Container.Components)
          {
            if (cmp is DataDescriptionItem)
            {
              DataDescriptionItem tst = (cmp as DataDescriptionItem);
              if ((tst.TableName == RecordObject.DbTableName) && (tst.FieldName == RecordObject.FieldList[i].FieldName))
              {
                di = tst;
                break;
              }
            }
          }

          if (di != null)
          {
            di.FieldObject = RecordObject.FieldList[i];
            items.Add(di);
          }
        }
      }
    }

Het "probleem" wordt veroorzaakt door deze (noodzakelijke) regel:
C#:
1
parentForm.Site.Container.Add(di, diName);

Verwijderd

Topicstarter
Bescheiden kickje...
Met de diverse attributes als DesignerSerializationVisibleAttribute ben ik al wel een stuk verder gekomen en wat wijzer geworden. ;) Maar voor bovenstaand probleem heb ik nog geen oplossing.

@EfBe
Wanneer moet je dat checken? Dat hoeft helemaal niet. Je design time code moet passief zijn, dus wachten op actie van de gebruiker. Als je serialization/deserialization code werkt heb je geen omkijken naar het re-instantieren van een form met jouw components en hoef je niet te checken of de DesignerHost loading is of niet, ik heb dat nog nooit gemoeten.
Mee eens, en dat zou ook ideaal zijn. :)
Maar in dit geval is de actie van de gebruiker: het koppelen van een RecordObj afgeleide (beetje ongelukkige naamgeving, 't is in feite een BLL component) aan een DataDescription component (een presentation layer component).

Dat RecordObj component hoeft helemaal niks te weten over hoe z'n inhoud gepresenteerd wordt, maar door 'm designtime te koppelen aan die DataDescription worden automatisch voor alle velden (columns) bijbehorende DataDescriptionItems aan gemaakt die o.a. vastleggen welk type edit controls hierbij horen (text, date, decimal, integer, memo, etc.), hoe de waarde getoond moet worden in bv. een DataGridView, en er kunnen eventhandlers aan gehangen worden die op de presentatielaag kunnen worden afgehandeld.
Mag 'ie leeg zijn? moeten bij changes misschien ook nog andere controls worden aangepast? dat soort dingen.

Wanneer ik al die DataDescriptionItems designtime met 't handje aan die DataDescription (laat ;)) toevoegen, is er niks aan 't handje, maar in 9 van de 10 gevallen kan ik bij 't koppelen van een RecordObj aan een DataDescription prima automatisch goede defaults laten bepalen (maxlength voor een text editcontrol, is 't een primary key veld, dan is 'ie meteen ook verplicht, etc.).

Met Site.Container.Add() krijg ik de boel dan ook wel aardig in de .designer.cs, maar omdat de setter van RecordObject ook wordt uitgevoerd wanneer DesignerHost Loading is, heb ik dan 't probleem dat 'ie probeert om 2x hetzelfde DataDescriptionItem toe te voegen, en da's nie goe. :)

Vandaar bovenstaande lelijke en hopelijk heel kortstondige workaround met die DesignerHost check. 't Werkt, en m'n collega's kunnen wel verder met deze componenten, maar 't is eigenlijk doodgewoon een draak om te zien.

[ Voor 4% gewijzigd door Verwijderd op 30-05-2006 22:56 ]

Pagina: 1