Laden...

Properties werden nicht durch die IDE in die .Designer.cs serialisert

Erstellt von gelöschtem Konto vor 9 Jahren Letzter Beitrag vor 9 Jahren 3.885 Views
Gelöschter Account
vor 9 Jahren
Properties werden nicht durch die IDE in die .Designer.cs serialisert

Ich habe ein Scenario mit einem WindowsForms Custom Control das sehr viele Steuerungsmöglichkeiten anbietet. Das heisst das Control hat Eigenschaften die wiederum Eigenschaften haben die in eigenen Klassen gekapselt sind usw.
(Bitte keine typischen Hinweise auf den Serialiation Designer und die Attribute, das Szenario ist etwas spezieller)

Ich finde keine Möglichkeit wie ich es einfach erklären soll daher mache ich es am besten durch Code:

class MyClassA : System.Windows.Forms.Panel
{
   public string Name{get; set;}

   [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
   public MyClassB Property1 {get; private set;}
}

class MyClassB
{
   public string Name{get; set;}

   [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
   public MyClassC Property1 {get; private set;}

   [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
   public BindingList<MyClassC> Test{get; private set;}
}

class MyClassC
{
   public string Name{get; set;}
}

Was ich jetzt erwarten würde ist das mir der Designer folgenden Code generiert

classA1.Name = "Test1";
classA1.Property1.Name = "Test2";
classA1.Property1.Property1.Name = "Test3";

aber das macht er nicht sondern nur

classA1.Name = "Test1";
classA1.Property1.Name = "Test2";

Der Designer ignoriert die 2. Child Ebene(obwohl er das Test Property als Liste problemlos verarbeitet.

Vereinfacht zusammegefasst, der Designer steigt aus sobald er Child/Child Properties verarbeiten muss. Das hat bestimmt irgendeinen Grund bzw. Konzept aber trotz Suchmaschinen habe ich die Magie dahinter bisher noch nicht entschlüsseln können.

Ein Schubs in die richtige Richtung wäre mir gerade sehr willkommen.

5.657 Beiträge seit 2006
vor 9 Jahren

Hi Sebastian.Lange,

an welcher Stelle werden denn die Eigenschaften Property1 und Test initialisiert? Und wie weist du eigentlich die Name-Eigenschaften im Designer zu?

Christian

Weeks of programming can save you hours of planning

Gelöschter Account
vor 9 Jahren

Ich selbst weise garnichts zu.
Das ist ja hier ausdrücklich Aufgabe des Designers.
(evtl.habe ich deine Frage nicht richtig verstanden)

Ich habe das nunmehr mit einem weiteren Testprojekt nochmals überprüft. Der CodeGenerator in VS steigt bei der 2. Verschachtelungsebene für scalare Properties definitiv aus. Ich hab mir mal die Mühe gemacht mir anzusehen wie unsere hauseigenen Dritthersteller(also eingekaufe Komponenten) das machen(die solche Verschachtelungen anbieten) und da wird immer eine Klasse erstellt/ generiert die alle skalarem Settings enthält. Soweit ich es verstanden habe muss ich hier etwas umdenken 😉 und meine Settings für den Designer erstellbar/konstruierbar machen und auf public Foo{get; private set} verzichten. Warum und wieso ich das machen muss ist mir im Moment nicht klar.

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo Sebastian.Lange,

Der Designer ignoriert die 2. Child Ebene(obwohl er das Test Property als Liste problemlos verarbeitet.

mit der Property als Liste meinst du vermutlich, eine Property vom Typ List<T> (egal mit welchem konkreten T). Ich denke, das zeigt die Problematik. Die Liste kann gesetzt werden, aber warum sollten ihre Properties (z.B. List.Capacity) gesetzt werden (können)? Ich finde es also nicht ganz unverständlich, wenn nur die Properties auf der ersten Ebene gesetzt werden können.

Als Lösung sehe ich zwei Möglichkeiten. Entweder, wie du sagst, eine Klasse (direkt oder als Wrapper) verwenden, die alle zu setzenden Properties direkt enthält. Oder ein Klasse, bei der alle geschachtelten Properties über den Konstruktor gesetzt werden können (vorausgesetzt der Designer erlaubt Konstruktoren mit Parametern).

herbivore

5.657 Beiträge seit 2006
vor 9 Jahren

Das ist ja hier ausdrücklich Aufgabe des Designers.

Der Designer kann keine privaten Setter aufrufen. Wenn du das Property nicht im Konstruktor der Klasse initialisierst, ist der Wert immer null. Dann kann der Designer auch nicht die Properties in der "zweiten Ebene" zuweisen.

Christian

Weeks of programming can save you hours of planning

Gelöschter Account
vor 9 Jahren

Hmm nein, ich habe mich wohl missverständlich ausgedrückt daher probiere ich es nochmal anders.

Das Problem:

// geht
MyControl1.FirstLevel.Name = "Foo1";

// geht nicht
MyControl1.FirstLevel.SecondLevel.Name = "Foo1"; 

Meine Analyse:
Ab der 2. Child Ebene (im Beispiel: SecondLevel) arbeitet der Code Designer nicht mehr. Ich kann gerne ein Beispielprojekt posten das dies untermauert. Wäre mein Second Level Property jedoch eine BindingList<T> würde es funktionieren(warum weiss ich derzeit nicht). Da wir im Haus viele Third Party Components einsetzen die solche Verschachtelungen für irgendwelche einfachen Settings in beliebiger Tiefe im Designer anbieten und das problemlos funktioniert habe ich mir den Designer Code dieser Third-Party Komponenten mal angesehen. Aufgefallen ist mir hierbei das hier immer die Child-Child-(n) Klasse im Designer neu erzeugt und dann zugewiesen wird.

In etwa wie folgt:


MyChildChildSettings myChildSettings1 = new MyChildChildSettings();
myChildSettings1 .Name = "Foo1";
....
myRoot1.FirstLayer.SecondLayer = myChildSettings1;

Mein Ansatz ist ja ein anderer, also die Unterklasse intern zu instanziieren:

myRoot1.FirstLayer.SecondLayer.Name = "Foo1";

funktioniert so aber nicht. Der Designer ignoriert dieses Konstrukt einfach.

Das war mit dem public Foo{get; private set} etwas blöd erklärt. Ich hoffe das Problem wird dadurch etwas deutlicher beleuchtet. Ergo muss ich mir jetzt überlegen wie ich den Designer dazu bringen kann eine Klasse mit einen Child-Child Settings zu erstellen und meine Lösung auf dieses Prinzip umstellen. Wie das geht und warum das so laufen muss ist das wo ich für jeden Hinweis dankbar wäre.

49.485 Beiträge seit 2005
vor 9 Jahren

Hallo Sebastian.Lange,

hast du schon mal (testweise) versucht, deine Klassen so zu schreiben, wie die Third Party Components es machen? Und geht es dann?

Wenn es dir gelingt eine (äußere und innere) Klasse zu7 schreiben, mit der es im Designer funktioniert, kannst du schrittweise versuchen, sie wieder in die Richtung zu ändern, wie du es gerne hättest und schauen, wie weit zu damit kommt, bzw. welche Änderung den Designer aus dem Tritt bringt.

Möglicherweise ist es nur eine Kleinigkeit an deinen momentanen Klassen, die den Designer stört. Dann würde mit eben diesem kleinen Zugeständnis die Möglichkeit bestehen, relativ dicht an den von dir gewünschten (Ideal-)Zustand zu kommen.

herbivore

4.221 Beiträge seit 2005
vor 9 Jahren

Wirf mal Google an mit "Typeconverter instancedescriptor"

Google-Suche nach typeconverter instancedescriptor

Das hilft Dir im Designer-Generated-Code die SubObjekte (inkl. Aufruf eines parametrisierten Konstruktors) zu erstellen.

Gruss
Programmierhans

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

Gelöschter Account
vor 9 Jahren

Ich empfinde es als etwas respektlos mir hier die Google Variante für Doofies vorzuschlagen. (Zumindest gegenüber anderen Powerusern solltest du du das unterlassen, und selbst wenn nicht muss man den TypeConverter nicht zwangsläufig kennen und ich habe mir sehr wohl die Mühe gemacht das Problem nach Möglichkeit selbst zu lösen)
Mehr noch,){gray} kann nicht nicht erkennen wie ein TypeConverter hier helfen kann. Einen parametrisierten Ctor Aufruf im Designer kann ich sogar ohne TypeConverter realisieren.

Hinweis von herbivore vor 9 Jahren

Anscheinend hast du etwas in den falschen Hals bekommen. Ein Google-Link als solcher ist erstmal wertfrei. Wenn er neue Stichworte liefert, ist er grundsätzlich positiv zu bewerten. Alle Helfer haben dich respektvoll behandelt und nach bestem Wissen und Gewissen Informationen zur Lösung des Problems beigetragen. Es liegt an dir, die Hilfe so zu nehmen, wie sie kommt, und das beste daraus zu machen. Sollte dabei unklar sein, wie ein bestimmter Tipp zur Lösung führen kann, kannst du natürlich gerne sachlich nachfragen, jedoch bitte ohne den Helfern Respektlosigkeit oder sogar böse Absichten zu unterstellen. In diesem Sinne bitte ich alle Beteiligten, die Diskussion rein sachlich fortzusetzen.

4.221 Beiträge seit 2005
vor 9 Jahren


    public class MyClassA : System.Windows.Forms.Panel
    {
        private MyClassB _Property1 = new MyClassB();
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public MyClassB Property1 { get{return this._Property1;} private set{this._Property1=value;} }
    }

    [TypeConverter(typeof(ReferenceConverter))]
    public class MyClassB
    {
        private string _Name = "myClassB";
        public string Name { get{return this._Name;} set{this._Name=value;} }

        private MyClassC _Property1 = new MyClassC();
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public MyClassC Property1 { get{return this._Property1;} private set{this._Property1=value;} }

        private BindingList<MyClassC> _Test=new BindingList<MyClassC>();
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public BindingList<MyClassC> Test { get{return this._Test;} private set{this._Test=value;} }
    }

    public class MyClassC
    {
        private string _Name = "myClassC";
        public string Name { get { return this._Name; } set { this._Name = value; } }
    } 

Und im Form ergibt das dann:



            this.myClassA1 = new Test.MyClassA();
            this.SuspendLayout();
            // 
            // myClassA1
            // 
            this.myClassA1.Location = new System.Drawing.Point(79, 76);
            this.myClassA1.Name = "myClassA1";
            this.myClassA1.Property1.Name = "myClassB";
            this.myClassA1.Property1.Property1.Name = "myClassC";
            this.myClassA1.Size = new System.Drawing.Size(330, 152);
            this.myClassA1.TabIndex = 0;
            // 
            // Form1
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.ClientSize = new System.Drawing.Size(529, 297);
            this.Controls.Add(this.myClassA1);
            this.Name = "Form1";
            this.Text = "Form1";
            this.ResumeLayout(false);



Gruss
Programmierhans

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

4.221 Beiträge seit 2005
vor 9 Jahren

Meiner Meinung nach fährst Du aber besser wenn Du die inneren Klassen von Component ableiten lässt (Dann brauchst Du auch den ReferenceConverter nicht... und kriegst gratis den Designer-Support)

Also in etwa so:




    public class MyClassA : System.Windows.Forms.Panel
    {
        private IContainer components;
        private MyClassB _Property1 = new MyClassB();
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public MyClassB Property1 { get{return this._Property1;} private set{this._Property1=value;} }

        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.components.Add(_Property1);
        }
    }

    
    public class MyClassB:Component
    {

        private IContainer components;
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.components.Add(_Property1);
        }

        private string _Name = "myClassB";
        public string Name { get{return this._Name;} set{this._Name=value;} }


        private MyClassC _Property1 = new MyClassC();
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public MyClassC Property1 { get{return this._Property1;} private set{this._Property1=value;} }

        private BindingList<MyClassC> _Test=new BindingList<MyClassC>();
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public BindingList<MyClassC> Test { get{return this._Test;} private set{this._Test=value;} }
    }

    public class MyClassC:Component 
    {
        private string _Name = "myClassC";
        public string Name { get { return this._Name; } set { this._Name = value; } }
    } 


Schau Dir mal auf dem Interface IComponent vorhandenen Attribute an...

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...

Gelöschter Account
vor 9 Jahren

Hallo Programmierhans,

Den einzigen Unterschied den ich hier zu meinem Szenario ausmachen kann ist das MyClassB ReferenceConverter attributiert. Dieser besagte Converter ist mir bisher gänzlich unbekannt gewesen. Ich habs mit eigenen TypeConvertern elend lange versucht, (deswegen habe ich wohlmöglich auch etwas überrzeizt reagiert als du mir den lmgfty Hinweis gegeben hast

BTW: Die Ableitung von Component hatte ich tatsächlich auch schon versucht. Dadurch wurden mir auf einmal meine Instanzen im Components Panel angezeigt( da wo man die Timer und die FileDialogs sieht) was ich nicht möchte. Alle Versuche das abzuschalten haben nicht funktioniert. Nur soweit das sie dann auch in der Toolbox unsichtbar waren wo ich sie dann gerne gehabt hätte

Nach der Umstellung auf Component hatte ich für meine Properties die auf Struktur Typen basieren auf einmal unendlich viele Compiler Warnungen (weil die nicht von MarshalByRefObject ableiten) Das lässt sich zwar lösen, aber unterm Strich muss ich dadurch soviel umstellen das es mir das nicht wert ist. (Das bzgl. Projekt ist einer Spätphase wo man solche die ganze Architektur erschütternden Entscheidungen nicht mehr treffen kann)

Wenn das mit dem Reference Converter hinhaut bin ich erstmal glücklich, das warum und wieso kann ich erst in meinem Weihnachtsurlaub beleuchten. *thats life (hands auf herz: wer von euch kannte bisher den ReferenceConverter und wenn ja, woher?)

4.221 Beiträge seit 2005
vor 9 Jahren

Hallo Sebastian

Das mit dem Google für faule ist jeweils eher als Hilfe gedacht (ist doch bequem wenn man die Suche nicht selber tippen muss)... War sicher nicht böse gemeint.

Das mit dem ReferenceConverter... wie schon geschrieben schau mal welcher TypeConverter auf dem IComponent ist... und dann dessen Base... und schon haste den gefunden 😃

Sich in das Designerverhalten eigener Controls einzuarbeiten ist hart ... aber mit der Zeit verstehst Du da auch immer mehr...

Viel Spass beim experimentieren
Gruss
Programmierhans

Früher war ich unentschlossen, heute bin ich mir da nicht mehr so sicher...