Laden...

Problem mit Editor in PropertyGrid

Erstellt von Glassghost vor 17 Jahren Letzter Beitrag vor 17 Jahren 4.164 Views
G
Glassghost Themenstarter:in
18 Beiträge seit 2006
vor 17 Jahren
Problem mit Editor in PropertyGrid

Wunderschönen guten Abend,

folgende nicht ganz triviale Situation:
Wir haben eine Anwendung mit der der Benutzer auf einer Zeichenfläche veschiedene tolle Objekte (UserControls) zeichnen kann. Wir verwenden das PropertyGrid - Control um die Eigenschaften des momentan in der Zeichenfläche ausgewählten Elements dem Benutzer zur Bearbeitung anzubieten.

Da es sich dabei aber wie gesagt um UserControls handelt, werden ne Menge geerbter Eigenschaften angezeigt, die wir dem Benutzer garnicht anbieten wollen.
Daher verwenden wir Wrapper- Klassen, die uns die UserControls kapseln und nur die gewünschten Eigenschaften nach außen geben.

So weit so gut. Dummerweise handelt es sich bei den gewünschten Eigenschaften teilweise auch um Collections, die in den Originalklassen (also den UserControls) als Hashtable vorliegen. Um die nun wiederum über das PropertyGrid anzuzeigen und zu bearbeiten haben wir für diese Eigenschaften jeweils eigene Collection- Klassen geschrieben (also Klassen, die von System.Collections.CollectionBase abgeleitet sind), die in den Wrapperklassen als Propertyersatz für die Hashtables dienen.

Das Problem ist jetzt: Änderungen über den CollectionEditor an bestehenden EInträgen einer Collection werden in der Hashtable auch gemerkt, aber wie bekomme ich neue EInträge in mein Hashtable? Wenn ich über den CollectionEditor einen Eintrag in meine Collection der Wrapperklasse hinzufüge, bemerkt das meine Hashtable im Originalobjekt ja nicht... Der CollectionEditor scheint auch nicht meine Add- Methode meiner Collection-Klasse zu verwenden, um bei einem Klick auf den "Hinzufügen"- Button die Liste zu aktualisieren, denn wenn ich da nen Breakpoint setze, hält das Programm nicht an...?

Ich hoffe da steigt jemand durch, ansonsten kann ich natürlich versuchen, es besser zu erklären.

Ich versuch mal, einen Auszug aus dem Klassendiagramm zum besser Verständnis einzufügen. Wie aus den Namen der Wrapper- Klassen hervorgeht, handelt es sich bei der Anwendung um ein Tool, mit dem man Klassendiagramme zeichnen können soll... die eigentlichen Originalklassen, die umhüllt werden, sind auf diesem Klassendiagramm nicht zu sehen!

Beste Grüße
Bob

B
1.529 Beiträge seit 2006
vor 17 Jahren

*browser_maximier_und_trotzdem_nach_links_und_rechts_scroll*

Wie hast du denn Add angelegt? Mit new oder mit override?

G
Glassghost Themenstarter:in
18 Beiträge seit 2006
vor 17 Jahren

Hoi,

Sorry wegen der großen Grafik...

Was deine Frage angeht... eigentlich weder noch, weil CollectionBase ja Add nicht zur Verfügung stellt, das müsste doch vom IList Interface kommen, oder?


public int Add(modelAttribute item)
{
	item.DefaultID = System.Guid.NewGuid();
	this.origAttributes.Add(item.DefaultID, item);
	return List.Add(item);
}

Grüße
Bob

B
1.529 Beiträge seit 2006
vor 17 Jahren

Ich glaube, ich habe noch nicht so richtig verstanden, warum du eine HashTable als List wrappen willst.

Nutz du eigentlich das Framework 1.1 oder 2.0?
Falls 2.0 solltest du nicht HashTable, sondern Dictonary verwenden.

G
Glassghost Themenstarter:in
18 Beiträge seit 2006
vor 17 Jahren

Hallo nochmal,

Original von Borg
Ich glaube, ich habe noch nicht so richtig verstanden, warum du eine HashTable als List wrappen willst.

Ich will ja die Klasse, die die Hashtable(s) hat, im PropertyGrid anbieten (Hashtables können vom PropertyGrid wohl nicht aufgelöst werden), und da ich keine andere Lösung gefunden habe, wrapp' ich die Klasse und mach ne eigene Liste draus, die dann angezeigt werden kann...

Original von Borg
Nutz du eigentlich das Framework 1.1 oder 2.0?
Falls 2.0 solltest du nicht HashTable, sondern Dictonary verwenden.

Wir sind noch auf 1.1...

Danke und beste Grüße
Bob

B
1.529 Beiträge seit 2006
vor 17 Jahren

Ich merke gerade, dass ich mich mit diesem Thema bislang zu wenig beschäftigt habe. Den Informationen auf http://www.codeproject.com/csharp/DzCollectionEditor.asp zufolge, musst du jedoch scheinbar CollectionEditor ableiten.
Mehr weiß ich leider auch nicht, sorry.

121 Beiträge seit 2006
vor 17 Jahren

Hallo Glassghost,

ganz steige ich noch nicht durch, aber wenn Deine WrapperKlasse eine eigentständige Collection definiert, sind deren Inhalte zunächst einmal unabhängig von den Inhalten der Collection in der verpackten Klasse.

In Java kann man doch wenn ich nicht irre, beim Konstruktoraufruf einer Collection eine Refernz auf eine andere Collection angeben, sodass die erste Collection die zweite verpackt. Die "Wrapper"-Collection referenziert die ursprüngliche Collection, und die Elemente sind nur ein Mal vorhanden. Deshalb schlagen sich Adds und Deletes auf die WrapperKlassen-Instanz auch auf die zugrundeliegende Collection durch. Sowas bräuchte man hier ja auch.

Jetzt schaue ich bei C# ob es da auch so was gibt, finde aber immer nur Konstruktoren, die Kopien der Elemente erzeugen, was hier natürlich nicht zielführend ist.

Oder gibt es die Möglichkeit, sich bei Adds und Deletes einzuhängen und die Änderungen durchzureichen?

Schwierig, bin aber sehr gespannt auf die Ideen der anderen Mitleser.
Gruß Hape

B
1.529 Beiträge seit 2006
vor 17 Jahren

Das eigentliche Problem scheint ja nicht das Add zu sein, sondern dass der CollectionEditor die Add-Methode nicht nutzt.
So wie ich das verstanden habe, erzeugt der CollectionEditor ganz normal ein neues Item. Nur wie bekommt er dies in die Collection?
Erzeugt er eventuell ein Array von ItemType und erzeugt jedesmal eine neue Collection über den Konstruktor? Hast du das mal probiert?

G
Glassghost Themenstarter:in
18 Beiträge seit 2006
vor 17 Jahren

Hoi ihr beiden,

zunächst mal Danke für eure Gedanken.

Borg schrieb:
Das eigentliche Problem scheint ja nicht das Add zu sein, sondern dass der CollectionEditor die Add-Methode nicht nutzt.
So wie ich das verstanden habe, erzeugt der CollectionEditor ganz normal ein neues Item. Nur wie bekommt er dies in die Collection?

So prägnant hät ichs auch gern ausdrücken wollen 😉. Genau das ist mein Problem.

hape schrieb;
In Java kann man doch wenn ich nicht irre, beim Konstruktoraufruf einer Collection eine Refernz auf eine andere Collection angeben, sodass die erste Collection die zweite verpackt. Die "Wrapper"-Collection referenziert die ursprüngliche Collection, und die Elemente sind nur ein Mal vorhanden.

Eigentlich dachte ich, dass hätte ich so gemacht:


#region Variables
private Hashtable origAttributes;
#endregion

#region ctor
/// <summary>
/// ctor. Initializes a new modelAttributeCollection- Object.
/// </summary>
/// <param name="inAttributes">The original-Attributes for the collection.</param>
public modelAttributeCollection(Hashtable inAttributes)
{
	try
	{
		this.origAttributes = inAttributes;
		foreach(DictionaryEntry deTemp in this.origAttributes)
		{
			this.Add(deTemp.Value as modelAttribute);
		}
	}
	catch(Exception ex)
	{
		Gecco.Systembase.Base.ExceptionHandler.ShowAsStandardException(ex);
	}
}
#endregion

...

#region Interface Implementation

public int Add(modelAttribute item)
{
    item.DefaultID = System.Guid.NewGuid();
    this.origAttributes.Add(item.DefaultID, item);
    return List.Add(item);
}

#endregion

Borg schrieb:
Erzeugt er eventuell ein Array von ItemType und erzeugt jedesmal eine neue Collection über den Konstruktor? Hast du das mal probiert?

Also in den Konstruktor springt er auch nicht, wenn ich über den CollectionEditor was hinzufüge, also scheints das auch nicht zu sein. Könnte das EInfügen vielleicht irgendwie über REflektion gehen? Ich wüßt zwar nicht wie ohne irgendwann doch mein Add aufzurufen, aber... ?

Beste Grüße
Bob

B
1.529 Beiträge seit 2006
vor 17 Jahren

Hast du auch AddRange implementiert? Irgendetwas stand da, dass AddRange bevorzugt wird.

49.485 Beiträge seit 2005
vor 17 Jahren

Hallo Glassghost,

Sorry wegen der großen Grafik...

wenn du die Grafik an den Beitrag anhängst (Dateianhang), dann wird sie automatisch verkleinert und kann bei Bedarf in voller Größe abgerufen werden.

herbivore

G
Glassghost Themenstarter:in
18 Beiträge seit 2006
vor 17 Jahren

Hoi,

Original von Borg
Hast du auch AddRange implementiert? Irgendetwas stand da, dass AddRange bevorzugt wird.

Ja, hab ich auch gemacht... aber der Breakpoint dort bleibt auch jungfräulich.

Bob

G
Glassghost Themenstarter:in
18 Beiträge seit 2006
vor 17 Jahren

@herbivore

Danke für den Tipp!

Bob

B
1.529 Beiträge seit 2006
vor 17 Jahren

Dann bin ich jetzt etwas am Rätseln. Weitere Informationen zum CollectionEditor habe ich nicht gefunden. Auch auf der MS-Website sind die Informationen als sehr spärlich zu beschreiben.

Hier ist der Code von CollectionEditor.SetItem:

protected virtual object SetItems(object editValue, object[] value)
{
      if (editValue != null)
      {
            Array array1 = this.GetItems(editValue);
            int num2 = array1.Length;
            int num3 = value.Length;
            if (!(editValue is IList))
            {
                  return editValue;
            }
            IList list1 = (IList) editValue;
            list1.Clear();
            for (int num1 = 0; num1 < value.Length; num1++)
            {
                  list1.Add(value[num1]);
            }
      }
      return editValue;
}

Laut diesem wird IList.Add aufgerufen.

Ich denke, jetzt wird es Zeit, da mal auf MSDN Forum nachzufragen.

G
Glassghost Themenstarter:in
18 Beiträge seit 2006
vor 17 Jahren

Hallo Borg,

wie kommt man denn an so den Code ran?

Naja, ich bin deinem Rat jetzt mal gefolgt, und hab ein neues Thema bei microsoft erstellt. Werde euch auf dem laufenden halten, wenn ihr das wollt...

Gruß
Bob

B
1.529 Beiträge seit 2006
vor 17 Jahren
G
Glassghost Themenstarter:in
18 Beiträge seit 2006
vor 17 Jahren
Lösung!

Hallo zusammen,

@Borg: Danke für den Link, tolles Tool!

hab jetzt wohl ne (vorläufige) Lösung. Habe mir ne eigenen CollectionEditor gemacht, der aber nur die SetItems- Methode überschreibt:


public class ctrCollectionEditor : CollectionEditor
{
	public ctrCollectionEditor (Type type):base(type ){	}

	protected override object SetItems(object editValue, object[] value)
	{
		object returnValue = base.SetItems (editValue, value);
		
		if(editValue is Model.modelAttributeCollection)
		{
			foreach(modelAttribute aAttribute in value)
			{
				if( ! ((Model.modelAttributeCollection)editValue).OrigAttributes.Contains(aAttribute.DefaultID))
				{
					((Model.modelAttributeCollection)editValue).OrigAttributes.Add(aAttribute.DefaultID, aAttribute);
				}
			}
		}
		else if(editValue is Model.modelOperationCollection)
		{
			foreach(Model.modelOperationWrapper aOpWrapper in value)
			{
				if( ! ((Model.modelOperationCollection)editValue).OrigOperations.Contains(aOpWrapper.OrigOperation.DefaultID))
				{
					((Model.modelOperationCollection)editValue).OrigOperations.Add(aOpWrapper.OrigOperation.DefaultID, aOpWrapper.OrigOperation);
				}
			}
		}
		else if(editValue is Model.modelParameterCollection)
		{
			foreach(modelParameter aParameter in value)
			{
				if( ! ((Model.modelParameterCollection)editValue).OrigParameters.Contains(aParameter.DefaultID))
				{
					((Model.modelParameterCollection)editValue).OrigParameters.Add(aParameter.DefaultID, aParameter);
				}
			}
		}
		return returnValue;
	}
}

Muss noch getestet werden, scheint aber auf den ersten Blick seinen Zweck zu erfüllen. (Um das Entfernen brauch ich mich nicht extra kümmern, weil das zu entfernende Objekt durch DestroyInstance gnadenlos gekillt wird...)

Noch so als Nachtrag: Ich hab spaßeshalber mal in meine überschreibene SetItems den Code der original- Methode gepackt und die Basisimplementierung nicht auf grufen, um zu überprüfen, was in der Zeile



...
for (int num1 = 0; num1 < value.Length; num1++)
{
       list1.Add(value[num1]);
}
...


passiert... das weis ich jetzt zwar immer noch nicht, aber meine Add- Methode wird nicht aufgerufen...?

Beste Grüße
Bob