| 

.NET C# Java Javascript Exception

4
Hallo,

ich möchte einem asp.net (4) UserControl eine oder mehrere Properties verpassen, die jeweils komplex sind, also vom Typ einer anderne Klasse sind und somit dann mehrere Informationen aufnehmen können. Diese sollen dann natürlich auch im Property Grid erscheinen und dort pflegbar sein. Vergleichbar mit der Angabe eines Font, welche Schriftgrösse, Schriftart etc. einschließt.

Beispiel-Struktur:
Klasse Autor: Properties: String Vorname, int Alter
UserControl: Properties: String BuchTitel, Autor BuchAutor

Was bisher halbwegs gelingt ist, dass man innerhalb des UserControl Elements des ascx auf der entsprechenden aspx Seite ein Element der komplexen Property einfügen kann, welches die zugehörigen Member-Werte aufnimmt. zB:
<uc1:BuchControl ID="BuchControl1" runat="server" BuchTitel="Codewahnsinn Teil 1">     
<BuchAutor Vorname="Hans" Alter="25" />
</uc1:BuchControl>


In dem Moment, indem man den Tag <Buchautor...> eingibt, springt das parallel angezeigte PropertyGrid um von den Feldern des BuchControl (zB "BuchTitel" auf die Felder von "BuchAutor", also zB "Vorname" oder "Alter"). Der Intellisense des Markup bietet ausserdem auch die korrekten Felder der Klasse "Autor" an, ggf. auch enum Werte zur Auswahl.

Anschließend zeigt der Designer der Seite allerdings das gesamte Control als fehlerhaft an und bringt die Meldung "Der Typ System.Web.UI.UserControl hat keine öffentliche Eigenschaft mit dem Namen Buchautor." Obwohl diese public property mit diesem Namen definitiv im UC vorhanden ist.

Teilweise wurde die Property BuchAutor auch im PropertyGrid angezeigt, aber nicht komplex sondern einzeilig, dort konnte man einen(!) string eingeben, sehr hilfreich ist das nicht.

Eigentlich möchte ich ja die Werte der / jeder BuchAutoren Property, die ich in dem UserControl angelegt habe, mir und anderen Kollegen direkt in dem Property Grid des UserControls zur Verfügung stellen (wie gesagt, wie zB bei einem Font). Ich habe gelesen, dasa man TypConverter benötigt, und dies auch ausprobiert.

Mit dem einzigen Ergebnis, dass die Property danach gar nicht mehr im Propertygrid vorhanden war.

Kurzum, hat jemand Erfahrung damit gemacht und kann einen Tip geben?

Gruße, Markus
News:
09.05.2012
Magier77 238 1 6
2 Antworten
0
Mit ASP.NET Controls habe ich leider keine Erfahrung, aber ich habe eben mal in der MSDN gestöbert und prinzipiell funktioniert das genauso, wie ich das von früheren Windows Forms Controls kenne.
Die zentrale Klasse ist ein ControlDesigner, die MSDN hält hierfür eine sehr ausführliche Seite bereit. Damit kannst Du grundsätzlich das DesignTime-Verhalten deines Controls implementieren inkl. Verbs (= Kontextmenübefehle) und ActionLists (= Dialog, der angezeigt wird, wenn man dieses kleine Dreieck am Control klickt). Über die Methoden des IDesigner-Interfaces (wird vom ControlDesigner implementiert) kannst Du Properties zur Entwurfszeit auch filtern oder komplett neue Properties hinzufügen, die Dein Control eigentlich gar nicht unterstützt.
Damit Deine komplexen Properties im PropertyGrid anständig funktionieren, hast Du die Möglichkeit, einen TypeConverter zu implementieren, vlt. auch einen ExpandableObjectConverter (dieser wird im PropertyGrid zu einem kleinen Plus, so dass Du die Property aufklappen kannst). Die letzte (mir bekannte) Variante ist ein UITypeEditor, damit kannst DU im PropertyGrid Dein eigenes DropDown realisieren.

Viel Info, kurzes Fazit: Wenns nur um die Anzeige einer Property von einem bestimmten Typ im PropertyGrid geht, ist ein TypeConverter die einfachste Möglichkeit.
Alles andere ist erstmal optional, verbessert aber die Entwurfszeiterfahrung Deines Controls ungemein.

EDIT:
Hab total übersehen, dass Du einen TypeConverter bereits ausprobiert hast. Aus Erfahrung kann ich berichten, dass die ziemlich hakelig sind. Wie hast Du den TypeConverter denn mit Deiner Property verbunden? Meines Wissens kann man das entweder über ein TypeConverterAttribute machen oder einen eigenen TypeDescriptor implementieren, der dann die Converter rausgibt.
Wichtig ist auch, dass Deine eigenen "Designerklassen" (ob TypeConverter, ControlDesigner, etc. ist hierbei irrelevant) vom VS Designer gefunden werden können. Pack die Assembly(s) mal testweise in den GAC. Das hat bei mir früher zuverlässig funktioniert.
Wenn Du nicht weiterkommen solltest, dann aktualisiere bitte Deine Antwort und poste etwas Beispielcode (vom Control und der Property, dem TypeConverter, ...).
09.05.2012
ffordermaier 8,4k 3 9
0
Erstmal danke für die Hilfe.... aber ich versteh es nicht... :)
Hier kommt was ich "habe"...

Den (ausprobierten) Typeconverter (siehe unten) habe ich aus verschiedenen Beispielen zusammengeklaubert. Wenn ich das richtig verstehe, dient dies nur dazu aus den im PropGrid eingegebenen (immer) Strings die korrekten Datentypen zu machen und umgekehrt...?

Die Klasse BUCH, so mein Verständniss und so sagt es ja auch der genannte Artikel über TypeConverterAttribute, wird mit dem TypeConverter "versehen". Check (Zeile 1).

Nun, was denn noch...?


[TypeConverter(typeof(myComplexPropertyConverter)), DescriptionAttribute("Expand to see the spelling options for the application.")]
public class Buch
{
private String _Titel;
public String Titel
{
get { return _Titel; }
set { _Titel = value; }
}
private Autor _BuchAutor;
public Autor BuchAutor
{
get { return _BuchAutor; }
set { _BuchAutor = value; }
}
public Buch()
{
this.BuchAutor = new Autor();
}
public override string ToString()
{
return this.Titel + " by " + this.BuchAutor.NachName;
}
}
public class Autor
{
private String _NachName;
public String NachName
{
get { return _NachName; }
set { _NachName = value; }
}
private int _Alter;
public int Alter
{
get { return _Alter; }
set { _Alter = value; }
}
}


public partial class WebUserControl1 : System.Web.UI.UserControl
{
private Buch _Buch;

[Browsable(true), NotifyParentProperty(true), EditorBrowsable(EditorBrowsableState.Always)]
[PersistenceMode(PersistenceMode.InnerProperty)]
[CategoryAttribute("Global Settings")]
public Buch Buch
{
get { return _Buch; }
set { _Buch = value; }
}

protected void Page_Load(object sender, EventArgs e)
{
this.Buch = new Buch();
Buch.Titel = "die top 10 Stolpersteine des propertygrid";
Buch.BuchAutor.Alter = 34;
Buch.BuchAutor.NachName = "Arno Aufschneider";
Label1.Text = Buch.ToString();
}
}



public class myComplexPropertyConverter : TypeConverter
{

public myComplexPropertyConverter()
{
}

public override bool CanConvertTo(ITypeDescriptorContext context, System.Type destinationType)
{
if (destinationType == typeof(Buch))
return true;
return base.CanConvertTo(context, destinationType);
}

public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType)
{
if (destinationType == typeof(System.String) &&
value is Buch)
{
Buch mybook = (Buch)value;
return mybook.BuchAutor.Alter.ToString();

}
return base.ConvertTo(context, culture, value, destinationType);
}

public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType)
{
if (sourceType == typeof(string))
return true;

return base.CanConvertFrom(context, sourceType);
}

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
return base.ConvertFrom(context, culture, value);
}


/// <summary>
/// Determines whether changing a value on this object should require a call to the
/// Size3DConverter.CreateInstance(System.ComponentModel.ITypeDescriptorContext,System.Collections.IDictionary)
/// method to create a new value.
/// </summary>
/// <param name="context">
/// A System.ComponentModel.TypeDescriptor through which additional context can
/// be provided.
/// </param>
/// <returns>
/// true if the System.Drawing.SizeConverter.CreateInstance(System.ComponentModel.ITypeDescriptorContext,System.Collections.IDictionary)
/// object should be called when a change is made to one or more properties of
/// this object.
/// </returns>
public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
/// <summary>
/// Retrieves the set of properties for this type. By default, a type does not
/// have any properties to return.
/// </summary>
/// <param name="context">
/// A System.ComponentModel.TypeDescriptor through which additional context can
/// be provided.
/// </param>
/// <param name="value">The value of the object to get the properties for.</param>
/// <param name="attributes">An array of System.Attribute objects that describe the properties.</param>
/// <returns>
/// The set of properties that should be exposed for this data type. If no properties
/// should be exposed, this may return null. The default implementation always
/// returns null.
/// </returns>
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
Type type = GetSize3DType(context);
return TypeDescriptor.GetProperties(type, attributes).Sort(new string[] { "Width", "Height", "Depth" });
}
/// <summary>
/// Determines whether this object supports properties. By default, this is false.
/// </summary>
/// <param name="context">
/// A System.ComponentModel.TypeDescriptor through which additional context can
/// be provided.
/// </param>
/// <returns>
/// true if the Size3DConverter.GetProperties(System.ComponentModel.ITypeDescriptorContext,System.Object,System.Attribute[])
/// method should be called to find the properties of this object.
/// </returns>
public override bool GetPropertiesSupported(ITypeDescriptorContext context)
{
return true;
}

private Type GetSize3DType(ITypeDescriptorContext context)
{
if (context == null)
return typeof(Buch);
return context.PropertyDescriptor.PropertyType;
}
}
16.05.2012
Magier77 238 1 6

Stelle deine .net-Frage jetzt!
TOP TECHNOLOGIES CONSULTING GmbH