Windows 8 Store Apps (HTML5/JavaScript) – CSS ist nicht gleich CSS

imageBei der Arbeit an einer Windows 8 Store App die ich mit HTML5 und JavaScript realisiere, bin ich auf folgendes Problem gestoßen (welches ich hier bereits auf Facebook gepostet habe):

Doch zuerst einmal ein kurzer Ausschnitt des Markup bei welchem das Problem auftritt:

<body>
	<div id="itemTemplate">
		<figure>
			<img id="profile-image" src="images/smalllogo.png" />
		</figure>
		<div>
			<span data-win-bind="innerText: text">Testen</span>
		</div>
	</div>
</body>

Im zugehörigen Stylesheet sind unter anderem folgende Styles definiert:

figure {
    margin: 0px;   
}

#itemTemplate figure img {
    margin: 30px;
}

So wie die Styles definiert sind, sollte das Image Element mit der ID “profile-image” eine Margin von 30px auf allen Seiten vorweisen.

Das funktioniert auch wenn man es in einem Browser aufruft (Sogar der IE funktioniert hierbei korrekt Smiley ).

imageWenn man diese Formatierung allerdings in einem “HTML5/JavaScript Windows a Store App” (was für ein BegriffTrauriges Smiley ) verwendet, greift der Style “#itemplate figure img” nicht in dem Image Element, welches in dem Figure Element eingebettet ist, sondern die Margin Einstellung des übergeordneten Elementes Figur hebt die Formatierung des darin eingebetteten Image Elementes auf.

Ein erstes kleines Fazit dabei ist, dass man sich nicht darauf verlassen kann, dass der zuletzt definierte Style tatsächlich Verwendung findet.
Wenigstens dann nicht wenn man übergeordnete globale Element Formatierungen verwendet.

XAML oder HMTL5

Zum Jahresende stellt sich für mich noch die “Frage aller Fragen”
(Also die aktuelle “Frage aller Fragen” werde sicherlich bald wieder eine neue haben :

image

Bisher konnte ich mich um dieses XAML Gedöns ganz gut herumdrücken.

  • Viel Backend Entwicklung (kein XAML notwendig)
  • WPF habe ich ausgesessen.
  • Silverlight ignoriert
  • Für die Dicken “Fat Clients” habe ich (immer noch) Winforms eingesetzt und für Webseiten neben C#, HMTL nur ein wenig JavaScript benötigt.

Und nun, stehen Windows 8 Apps (also die Kachel Programme Smiley) auf der Liste.

imageimageimage

Ultrabook – Lenovo Twist – WIN-8

Abstract (Zusammenfassung für ungeduldige):

imageWenn man einen Lenovo Twist bestellt, sollte man nicht erwarten ein Gerät in der gleichen Klasse wie einen Apple Mac Book Air (MBA) zu bekommen. Das wäre wohl auch zu viel verlangt, denn der Lenovo kostet einiges weniger als ein MBA

.Aber man bekommt ein Gerät, was meiner Meinung nach vor allem für den Betrieb mit Windows 8 einen guten Kompromiss, zwischen kleinem Gerät, Guter Leistung und akzeptablem Preis darstellt.

Leider kann man den Twist nicht in einer, meiner Meinung nach “brauchbaren” Ausstattung (SSD ist zu klein) direkt bestellen.

Man kann ihn, wie ich später noch beschreibe werde, aber relativ einfach mit der fehlenden größeren SSD aufrüsten.

Erste Eindrücke

Vollbild anzeigenNach 2 Tagen Einrichtung und Verwendung des Lenovo Twist möchte ich hier meine ersten Eindrücke mit dem Ultrabook und Windows 8 zum besten geben. Die meisten die mich kennen, wissen dass ich mich mit Windows 8 auf dem Desktop Rechner etwas schwer tue, ja schon gut, etwas sehr viel schwer tue Smileyist eventuell etwas untertrieben, aber nach dem Motto:

Gib Windows 8 eine Chance

Habe ich mir extra ein Gerät bestellt, welches meiner Meinung nach, die wichtigste Voraussetzung für Windows 8 erfüllt, und das ist:

Es besitzt ein Touch Display und kann damit die stärken (ups was habe ich das gesagt stärken ;-) ) von Windows 8 ausspielen.

Los geht’s

Das erste was mir aufgefallen ist und das war gleich so ein:

“Oh man was machen die da nur und warum ist das bei meinem MBA ganz anders Aufschrei”

Bei relativ wenig Aktivität setzt der Lüfter des Geräts sehr schnell ein und verursacht somit ein leises aber eben nicht unüberhörbares Geräusch. Das kenne ich vom MBA nur wenn ich mit einer Windows VM arbeite und die unter Höchstlast betreibe. Aber OK, das macht mein DELL Precision (Meine tragbare Workstation) auch hin und wieder, also ein Problem welches eher an Windows und weniger am Twist liegt.

Dann ist mir auch noch aufgefallen, dass auf der rechten Seite, da wo die Lüfter sind, auch schon mal die Temperatur ganz schön nach oben gehen kann.

Tastatur Touchpad

Die Tastatur des Twist ist, na sagen wir mal “OK”, das Touchpad hat eine recht gute Touch Gesten Unterstützung ist aber viel weniger Präzise als ich das von einem MBA her kenne. Es fühlt sich auch nicht so hochwertig an irgendwie so ein wenig nach Plastik. Ich finde aber trotzdem das man damit seine Arbeit machen kann und, dass es zu dem Preisgefüge des Twist passt.
Wo wir gerade bei der Tastatur sind, muss ich leider noch anmerken, dass es mir persönlich schon fehlt, dass diese nicht beleuchtet ist, dieses Feature ist doch gerade bei Geräten die man auch mal so auf dem Schoß betreibt, eigentlich recht angenehm.

Display

Das Display ist wie bereits erwähnt ein 12,5 Zoll Touch Display mit relativ geringer Auflösung. Die geringe Auflösung von maximal 1366 X 768 ist für die Kacheloberfläche absolut ausreichend. für etwas anspruchsvollere Desktopanwendungen würde man sich aber schon etwas mehr Auflösung wünschen.

Die Touch Bedienung funktioniert mit dem Display wie erwartet gut. Nach zwei Tagen kann ich aber schon sagen, dass ich überhaupt kein größeres Display an diesem Gerät haben wollte; 12,5” beschmiertes Display genügen vollkommen. Bisher habe ich mich nur bei Tablet Rechnern wie das iPad an die verschmierten Bildschirme gewöhnt aber nicht an “quasi” Desktop Rechner / Notebooks, ob ich mich damit jemals anfreunden kann, wer weiß.

Prozessor

Im Lenovo Twist ist ein I7 mit 2 Kernen verbaut der genügend Power hat um flüssiges arbeiten sicherzustellen.

Hauptspeicher

Ich habe die Variante mit 8 GB (maximale Ausbaustufe) bestellt.
Mit diesen 8 GB kann das ganze WIN-8 (Metro) Gedöns gut betrieben werden. Office Programme und auch Visual Studio funktionieren recht ansprechend mit der Speicherausstattung.
VM’s wollte ich damit allerdings nicht regelmäßig betreiben, aber dafür ist ein Ultrabook wie der Twist sicherlich auch nicht gedacht.

Festplatte / SSD

Auch bei der Festplatte habe ich die größte Ausbaustufe bestellt. Das ist aber leider nur eine SSD mit 128 GB.

Nachdem man WIN-8, Office und Visual Studio installiert hat bleiben gerade mal 40 GB freier Speicher und das reicht “mir” auf jeden Fall nicht aus.

Positiv ist aber, dass die SSD ist von außen zugänglich und nach lösen von nur einer Schraube ausgetauscht werden kann.

Leider ist die Aufrüstung einer größeren SSD trotz des einfachen mechanischen Austauschs nicht ganz so problemlos verlaufen, wie zuerst gedacht.

Das liegt hauptsächlich an zwei Dingen:

1) Zum Lenovo Twist wird “kein” Datenträger mit dem Betriebssystem und den Tools mitgeliefert.
Wobei ein Datenträger in Form einer CD/DVD in Ermangelung eines CD/DVD Laufwerks nicht direkt verwendet werden könnte. Doch ein ISO File welches man auf einen USB Stick zum booten kopieren könnte würde einem das Leben schon enorm erleichtern um ein System auf einer Platte neu zu installieren.

2)Die Clone Methode von Acronis True Image die ich eigentlich verwenden wollte, habe ich mit dem Lenovo Twist nicht zum funktionieren gebracht :-( .

Da auf der Original SSD verschiedene Volumes angelegt sind, darunter auch ein Recovery Volume, musste ich mir etwas einfallen lassen um diese Volumes auch auf die “neue” SSD zu übertragen, damit man im Notfall später mit der neuen SSD ein Recocery machen kann..

Da die Clone Methode mit Acronis True Image nicht funktioniert hat, habe ich über eine Backup / Restore Strategie die SSD gegen eine Samsung SSD 840 Pro mit 256 GB ausgetauscht.

Wenn jemand Probleme mit dem Austausch der SSD hat und noch Tipps braucht, einfach per Kommentar melden, ich kann dann noch ausführlicher erläutern was zu tun ist damit man die kompletten Volumes der Original SSD auf die neue SSD übertragen kann.

Akku / Laufzeit

Ich habe keine aufwendigen Testreíhen durchgeführt um die Akkulaufzeit zu ermitteln oder effektive Laufzeiten bestimmen zu können sondern dies hier sind einfach Eindrücke die beim arbeiten mit dem Lenovo Twist entstanden sind.

Ich kann “nicht” betätigen dass der Akku 7 Stunden Betriebszeit gewährleistet, aber ich kam bisher selbst, wenn ich den Twist gequält habe immer weit über 4 – 5 Stunden mit einer Akkuladung hin. Also bei nur ein bisschen Surfen oder Nutzung von Office sollte noch einiges mehr drin sein.

Resümees

Der Lenovo Twist fühlt sich auch dank der sehr griffigen Oberfläche gut an.

Das Display ist OK aber nicht überragend, genügt aber meinen Ansprüchen um Windows 8 mit der Kacheloberfläche betreiben zu können und, so vermute ich, auch um WIN-8 App Entwicklung mit Visual Studio darauf ausüben zu können (das wird sich in den nächsten Wochen zeigen).

Nachdem die SSD aufgerüstet ist, genügt die Hardware (Prozessor, Hauptspeicher “und” Festplatte) den aktuellen Ansprüchen um sich leistungsstarker Ultrabook nennen zu dürfen.

Google Music Manager – Couldn’t connect to Google Play

Seit einigen Wochen hat der Google Music Manager auf meinem Windows 7 Rechner seinen Betrieb eingestellt.

Da das synchronisieren der Musik in die Google Music Cloud nun nicht zu meinem Haupt Business gehört, war die Priorität zur Lösung des Problems auch nicht ganz so hoch angesiedelt wie das bei Problemen ist, die mich beim erledigen meiner eigentlichen Tätigkeit behindern.

Aber da mich das Problem doch so langsam genervt hat,  habe ich mich auf die Fehlersuche gemacht und bin auch fündig geworden.

Hier zuerst noch einmal kurz das Problem, das von Heute auf Morgen aufgetreten ist:

Beim Start des Music Manager konnte, wie unten zu sehen ist, dieser sich nicht mit Google Music verbinden.

SNAGHTMLf6c2a7

Deinstallieren und neu Installieren bracht auch keine Besserung.

Eine genauere Recherche hat dann ergeben, dass beim Versuch sich bei der Google Music Cloud anzumelden, ein Problem mit den in Windows installierten Root Zertifikaten eingestellt hat.

Ein einfaches Update dieser Zertifikate bringt schon Abhilfe und der Google Music Manager läuft wieder einwandfrei.

Hier der Link zum Update der Zertifikate (Für Windows ):

http://download.windowsupdate.com/msdownload/update/v3/static/trustedr/en/rootsupd.exe

Einfache herunterladen und ausführen.

Und nicht wundern, das Update erfolgt ohne weitere Ausgaben auf dem Bildschirm

Einlesen von “delimited with” Daten – Leicht gemacht mit Hilfe von Custom Attribut

So lange es EDV gibt und wohl auch, so lange es EDV geben wird ist der Austausch von Daten immer wieder ein Thema.

Um Daten zwischen Verschiedenen Systemen auszutauschen sind die Möglichkeiten fast unerschöpflich.

Eine der Möglichkeiten besteht darin Daten in eine Zeichenkennte zu schreiben und die Daten mit einem sogenannten Trennzeichen (Delimiter) zwischen den einzelnen Feldinformationen zu trennen (Auch noch im Zeitalter von Serialisierung und anderen tollen Dingen).

Die meisten Programme können Daten in diesem Format exportieren.

Viel interessanter wird es, wenn man diese Daten in seine eigenen Programmen verarbeiten möchte.

Ich möchte hier eine einfache Möglichkeit zeigen, wie man sich mit ein paar Zeilen Code eine schicke kleine Funktion und ein Custom Attribut schreiben kann die einem dabei behilflich sind, solche Daten einzulesen und diese Daten in Eigenschaften seiner eigenen Klasse zu schreiben.

Hier zuerst das Custom Attribute.  Das bietet schon ein wenig mehr Möglichkeiten als es hier in diesem Beispiel zum Einsatz kommt, aber der Beitrag soll auch nicht eine fertige Lösung darstellen, sondern nur behilflich sein, einen Weg zu zeigen:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class ImportFieldAttribute : Attribute
{
	/// <summary>
	/// Feldtype der zu importierenden Daten
	/// </summary>
	private readonly ImportFieldType fieldType;

	/// <summary>
	/// Position im Segment
	/// </summary>
	private readonly int position;

	/// <summary>
	/// Initializes a new instance of the <see cref="ImportFieldAttribute"/> class.
	/// Für Import von Sätzen mit Trennzeichen
	/// </summary>
	/// <param name="fieldType">Type of the field.</param>
	/// <param name="position">The position.</param>
	/// <param name="length">The length.</param>
	public ImportFieldAttribute(ImportFieldType fieldType, int position)
	{
		this.fieldType = fieldType;
		this.position = position;
	}

	/// <summary>
	/// Feldtype der zu importierenden Daten
	/// </summary>
	public ImportFieldType FieldType
	{
		get
		{
			return this.fieldType;
		}
	}

	/// <summary>
	/// Position im Segment
	/// </summary>
	public int Position
	{
		get
		{
			return this.position;
		}
	}
}

Schauen wir uns nun den Code an der sich mit Hilfe des Custom Attribute an die Daten ran macht.

public static void ImportClassFieldsFrom(object target, string buf, char delimiter)
{
	var fields = buf.Split(delimiter);

	const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public;
	var targetProperties = target.GetType().GetProperties(Flags);
	try
	{
		foreach (PropertyInfo propertyInfo in targetProperties)
		{
			var attribs =
				(ImportFieldAttribute[])propertyInfo.GetCustomAttributes(typeof(ImportFieldAttribute), true);

			if (attribs.Length > 0)
			{
				ImportFieldAttribute attrib = attribs[0];
				if (attrib.Position <= fields.Length)
				{
					var temp = fields[attrib.Position];
					propertyInfo.SetValue(target, temp.TrimEnd(), null);
				}
			}
		}
	}
	catch (Exception ex)
	{
		Exceptions.Log.LogError(buf, ex);
	}

}

Und so verwendet man diese Methode und das Custom Attribute:

Bei der Definition der Klasse, welche die Informationen aus dem “Delimited width” Zeichenkette aufnehmen soll, wende ich nun das Custom Attribute wie folgt an:

public class Palettes
{
	/// <summary>
	/// Gets or sets GrossWeight.
	/// </summary>
	[ImportField(ImportFieldType.Char, 1)]
	public string GrossWeight { get; set; }

	/// <summary>
	/// Gets or sets Height.
	/// </summary>
	[ImportField(ImportFieldType.Char, 2)]
	public string Height { get; set; }

	/// <summary>
	/// Gets or sets Lenght.
	/// </summary>
	[ImportField(ImportFieldType.Char, 3)]
	public string Lenght { get; set; }
}

Um die Klasseneigenschaften meiner Klasse mit den Werten aus der “Delimited width” Zeichenkette zu bekommen wird folgender Methodenaufruf durchgeführt:

var palette = new Palettes());
AttributHelper.ImportClassFieldsFrom(palette, "12,00~15,01~23,00", '~');

Fragen, Anregungen und Kritik wie immer, einfach als Kommentar

Fritzbox 7390 – Nortel VPN Client – VPN tunnel is disconnected due to routing table change

Nachdem ich vor einigen Wochen in meinem Homeoffice einen DSL Speedport von der Telekom gegen eine Fritzbox 7390 ausgetauscht habe, kam es bei der Verwendung der Nortel VPN Client Software zu ständigen Verbindungsabbrüchen.

imageDa ich dieses Verhalten kurz nach dem Einsatz der Fritzbox festgestellt habe, lag die Vermutung, dass es mit der Fritzbox zusammenhängt, natürlich mehr als nah.

Um die Vermutung zu verfestigen oder zu wiederlegen, habe ich mit der gleichen Hardware in verschiedenen Netzwerkumgebungen getestet, ob das Problem nicht doch an meinem Rechner oder der Konfiguration liegen könnte.

Das war aber nicht der Fall, das Problem trat nur in meinem Homeoffice auf.

Was lag also näher als den Support von AMV anzuschreiben um ihm meine Probleme zu schildern.

Gesagt getan und folgendes E-Mail an den Support gesendet:

>>>=================  START MAIL vom 03.05.2012 ================>>>

Hallo Support Team,

Ich verwende den Nortel VPN Client Version 10.04.016 um eine VPN Verbindung zu einem Firmennetzwerk herzustellen.

Ich kann die Verbindung herstellen, nach kurzer Zeit (nach einigen Sekunden bis Minuten) wird die Verbindung getrennt und es erscheint folgende

Fehlermeldung:

“VPN tunnel is disconnected due to routing table change”.

Wenn ich den gleichen Rechner in meinem Büronetzwerk verwende in dem ich einen Router von Vodafone einsetze funktioniert der VPN Zugang zum selben Firmennetzwerk den ganzen Tag ohne Unterbrechung.

Auf das VPN des Firmennetzwerk habe ich keinen Einfluss (das ist aber auch obsolet, da es für über 100000 Mitarbeiter weltweit funktioniert.

Für mich auch, wenn ich es nicht mit der FritzBox 7390 aus dem Homeoffice heraus verwende.

Der 7390 ist an einem VDSL 50000 Anschluss angeschlossen und funktioniert bis auf dieses VPN Problem ohne weitere Einschränkung. Leider kann ich ohne dieses VPN mein Homeoffice nicht nutzen :-( .

Ich hoffe Sie haben Lösungsansätze für mich

>>>================= ENDE MAIL vom 03.05.2012 ================>>>

Nicht gut aber schnell, nämlich am gleichen Tag habe ich dann eine erste Antwort erhalten

<<<================= START MAIL vom 03.05.2012 ===================

Guten Tag Herr Schelian,

vielen Dank für Ihre Anfrage an den AVM-Support.

Tipps und Hinweise zu VPN-Lösungen anderer Hersteller finden Sie in folgendem

Link:

http://service.avm.de/support/de/SKB/FRITZ-Box-7390/243:VPN-Loesung-eines-andere

n-Herstellers-mit-FRITZ-Box-verbinden

Über die Tipps in unserer FAQ hinaus leisten wir keinen Support für lokale oder individuelle VPN-Szenarien mit der Software anderer Hersteller.

<<<================= ENDE MAIL vom 03.05.2012 ===================

Außer einem Link zu einer furchtbaren FAQ die so überhaupt nichts mit dem von mir geschilderten Problem zu tun hat, nur “Schön dass Sie uns geschrieben haben”.

Aber ich wäre nicht Ich, wenn ich mich damit zufrieden gegeben hätte.

Also habe ich 2 Tage später folgendes Mail an den Support zurückgeschrieben:

==================== START MAIL vom 05.05.2012 ================>>>

Hallo Support Team,

leider hat mir Ihr Hinweis nicht geholfen.

Natürlich hatte ich bevor ich Sie kontaktiert habe die Q&A auf Ihrer Webseite angeschaut.

Ich nehme an, dass Sie mein Problem nicht richtig verstanden haben, ich versuche nicht mit irgendeiner VPN Lösung auf die Fritzbox zuzugreifen, sondern aus dem Netz welches über die Fritzbox über das Internet hergestellt hat auf ein Weltweites VPN Firmennetzwerk zuzugreifen.

Wie ich bereits in meiner ersten Nachricht geschrieben habe, kann ich mit dem gleichen Notebook aus meinem Firmennetz an dem ein Vodafone Router die Verbindung ins Internet herstellt, ohne Problem auf das externe VPN Netzwerk zugreifen.

Über einen Mobilen WLAN Hotspot funktioniert der Zugriff ebenfalls.

Eben nur nicht sobald ich über den Internetzugang gehe der über die Fritzbox hergestellt wird.

Ich hoffe dass Sie mir weiterhelfen können.

==================== ENDE MAIL vom 08.05.2012 ================>>>

Und tatsächlich erhalte ich nach nur 3 Tagen wieder eine Antwort

<<<================= START MAIL vom 05.05.2012 ===================

Guten Tag Herr Schelian,

vielen Dank für Ihre Rückmeldung.

Es tut mir leid, aber wir supporten wie erwähnt keine individuellen lokalen

VPN-Szenarien.

Freundliche Grüße aus Berlin

Jon Doe (AVM Support) (Name wurde auf Wunsch von AVM geändert)

<<<================= ENDE MAIL vom 08.05.2012 ===================

Es tut mir auch leid, ich konnte mir nicht verkneifen noch einmal zurück zu schreiben

==================== START MAIL vom 10.05.2012 ================>>>

Hallo Herr Doe, (Name wurde auf Wunsch von AVM geändert)

wenn das die Meinung von AVM ist!

Ich finde zwar nicht dass es sich bei meinem Problem im ein VPN Problem sondern um ein Router bzw. Firewall Problem Ihres Routers handelt. Aus irgend einem Grund ändert der Router die Routing Table und stört damit die bestendende Verbindung zu meinem VPN Server.

Vor allem wo dies mit verschiedener Firmware mal besser mal schlechter wird.

Mit der letzten Aktualisierung gerade mal wieder schlechter, bzw. mal wieder gar nicht benutzbar.

Dann werde ich wohl keine Fritz Router mehr einsetzen können.

==================== ENDE MAIL vom 10.05.2012 ================>>>

Aber wer denkt, der AVM Support würde aufgeben, der täuscht, ich erhalte also noch einmal eine Mail vom AVM Support

<<<================ START MAIL vom 11.05.2012 ===================

Guten Tag Herr Schelian,

vielen Dank für Ihre Rückmeldung.

Mein Kollege hat hier völlig recht. Es gibt hier keinen kausalen Zusammenhang mit der FRITZ!Box. Lesen Sie sich bitte dies hier in Ruhe

durch:

http://blog.michaelfmcnamara.com/2010/03/nortel-vpn-client-release-10-04-016-for-windows-7/#comment-3602

Dort ist exakt die Fehlermeldung aufgegriffen und auch die Ursache und eine Lösung beschrieben.

Freundliche Grüße aus Berlin

Max Mustermann (AVM Support) (Name wurde auf Wunsch von AVM geändert)

<<<================= ENDE MAIL vom 11.05.2012 ===================

Ich habe dort zwar keine Lösung für mein Problem gefunden, aber immerhin einen Workaround und konnte damit tatsächlich ermitteln, dass das Problem mit dem 7390 nur mit WLAN auftritt und nicht wenn ich den Rechner per Kabel angeschlossen habe.

Eigentlich hätte man die Sache damit bewenden lassen können, aber ich wäre nicht Ich wenn ich das Problem nicht an der Wurzel packen wollte.

Und so habe ich mich auf die Suche nach den Unterschieden in den verschiedenen W-LAN Netzen gemacht.

Um das ganze hier nicht ausufern zu lassen möchte ich nun aber zur Lösung des Problem kommen.

Hallo AVM Support hört gut zu, es gibt eine Fritz7390 Lösung für mein VPN spezifisches Problem.

Unter WLAN–>Funkkanal gibt es eine Einstellung:

image

Für 300 Mbit/s optimierte Kanäle verwenden.

Und diese Option ist im Default gesetzt (Und muss ausgeschaltet werden).

Das einschalten dieser Option führt dazu, dass die Fritzbox anstelle eines einzelnen Kanals einen Doppelkanal zum Beispiel Kanal 8 und Kanal 12 verwendet, und in diesem Zusammenhang kommt es zu wohl irgendwie zum internen Umschreiben der Routing Table oder irgend einem anderen Fritzbox internen Problem, dass zu dem in diesem Artikel beschriebenen Abbruch der VPN Verbindung kommt.

Übrigens konnte ich noch einen Zusammenhang mit dieser Einstellung feststellen.

image

Bei der Musik Übertagung vom iPhone/iPad via AirPlay auf einen Verstärker kam es immer wieder zu kurzen Unterbrechungen (Aussetzern)!

Diese sind nun auch verschwunden.

Hallo AVM Support Team, es war mir eine Freude behilflich sein zu können.

Ich schicke euch den Link per Mail, dann könnt Ihr eure FAQ ergänzen

DotNetNuke – Nach Upgrade auf DNN 6.1.X – HTML Modul speichert keine Änderungen mehr

Heute Vormittag habe ich den Anruf eines Kunden erhalten der mir folgendes Problem geschildert hat.

Nach der Aktualisierung eines DotNetNuke Portals von DNN 5.6.X auf DNN 6.1.1 werden Änderungen die man an Texten im HMTL Modul vornimmt nicht gespeichert.

Keine Fehlermeldung, kein Eintrag im Ereignisprotokoll, einfach nichts!

Nachdem ich mir die Konfiguration genauer angeschaut hatte, konnte ich einen Fehler in der Konfiguration des System (IIS und App Pool) ausschließen.

Das Problem war aber trotzdem relativ schnell gefunden.

Mit dem “früher” als Standardeditor eingesetzten FCK Editor gibt es mit der aktuellen Version (Ich glaube schon seit Version 5.6.X) von DotNetNuke an einigen Stellen Probleme, unter anderem eben auch im Text/HMTL Modul.

Irgendwelche JavaScripte des Editors kollidieren mit anderen JavaScript aufrufen in irgend einer der vielen verwendeten JavaScript Frameworks.

JavaScript eben Smiley

Ich weiß schon warum sich meine Begeisterung für dieses (Java)Script Gedöns in Grenzen hält.

OK, aber lassen wir das.

Ob es einen aktualisierte Version des FCK Editors gibt, die mit DNN Version 6.1 und höher läuft, weiß ich nicht, sollte jemand näheres dazu wissen, würde ich mich über einen entsprechenden Kommentar freuen.

Um das Problem mit den Bordmitteln von DNN zu lösen kann man aber einfach den Texteditor umstellen und anstelle des FCK Editors den Telerik Texteditor, der aktuell als Standardeditor von DotNetNuke verwendet wird.

Hierzu öffnet man die web.config und nimmt folgende Änderung vor:

Den defaultProvider von “FckHtmlEditorProvider” (siehe Vorher) auf “TelerikEditorProvider” (siehe Nachher) ändern

Vorher:

<htmlEditor defaultProvider="FckHtmlEditorProvider">
  <providers>
	<clear />
	<add name="TelerikEditorProvider" type="DotNetNuke.HtmlEditor.TelerikEditorProvider.EditorProvider, DotNetNuke.HtmlEditor.TelerikEditorProvider" providerPath="~/Providers/HtmlEditorProviders/Telerik/" toolsFile="~/Providers/HtmlEditorProviders/Telerik/Config/ToolsDefault.xml" configFile="~/Providers/HtmlEditorProviders/Telerik/Config/ConfigDefault.xml" FilterHostExtensions="True" />
	<add name="FckHtmlEditorProvider" type="DotNetNuke.HtmlEditor.FckHtmlEditorProvider.FckHtmlEditorProvider, DotNetNuke.FckHtmlEditorProvider" providerPath="~/Providers/HtmlEditorProviders/Fck/" CustomConfigurationPath="~/Providers/HtmlEditorProviders/Fck/custom/FCKConfig.js" EnhancedSecurityDefault="false" SecureConfigurationPath="~/Providers/HtmlEditorProviders/Fck/custom/FCKConfigSecure.js" ImageGalleryPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" ImageUploadPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" ImageAllowedFileTypes="gif,png,bmp,jpg" FlashGalleryPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" FlashUploadPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" FlashAllowedFileTypes="fla,swf" LinksGalleryPath="~/Providers/HtmlEditorProviders/Fck/fcklinkgallery.aspx" DynamicStylesGeneratorPath="~/Providers/HtmlEditorProviders/Fck/FCKStyles.aspx" DynamicStylesCaseSensitive="true" DynamicStylesGeneratorFilter="controlpanel|filemanager|mainmenu|wizard" StaticStylesFile="~/Providers/HtmlEditorProviders/Fck/FCKeditor/fckstyles.xml" StylesDefaultMode="dynamic" DynamicCSSGeneratorPath="~/Providers/HtmlEditorProviders/Fck/FCKCSS.aspx" StaticCSSFile="~/Providers/HtmlEditorProviders/Fck/FCKeditor/editor/css/fck_editorarea.css" CSSDefaultMode="dynamic" spellCheck="ieSpell" AvailableToolbarSkins="Office2003,Silver" DefaultToolbarSkin="Office2003" AvailableToolBarSets="DNNDefault,Default,NoGallery,Basic" DefaultToolbarSet="DNNDefault" DefaultImageGallerySkin="Default" DefaultFlashGallerySkin="Default" DefaultLinksGallerySkin="Default" FCKDebugMode="false" UseFCKSource="false" OptionsOpenMode="ShowModalDialog" ShowModuleType="true" FixOldDNNPostback="false" CustomOptionsDialog="Admin" />
  </providers>
</htmlEditor>

Nachher:

<htmlEditor defaultProvider="TelerikEditorProvider">
  <providers>
	<clear />
	<add name="TelerikEditorProvider" type="DotNetNuke.HtmlEditor.TelerikEditorProvider.EditorProvider, DotNetNuke.HtmlEditor.TelerikEditorProvider" providerPath="~/Providers/HtmlEditorProviders/Telerik/" toolsFile="~/Providers/HtmlEditorProviders/Telerik/Config/ToolsDefault.xml" configFile="~/Providers/HtmlEditorProviders/Telerik/Config/ConfigDefault.xml" FilterHostExtensions="True" />
	<add name="FckHtmlEditorProvider" type="DotNetNuke.HtmlEditor.FckHtmlEditorProvider.FckHtmlEditorProvider, DotNetNuke.FckHtmlEditorProvider" providerPath="~/Providers/HtmlEditorProviders/Fck/" CustomConfigurationPath="~/Providers/HtmlEditorProviders/Fck/custom/FCKConfig.js" EnhancedSecurityDefault="false" SecureConfigurationPath="~/Providers/HtmlEditorProviders/Fck/custom/FCKConfigSecure.js" ImageGalleryPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" ImageUploadPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" ImageAllowedFileTypes="gif,png,bmp,jpg" FlashGalleryPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" FlashUploadPath="~/Providers/HtmlEditorProviders/Fck/fckimagegallery.aspx" FlashAllowedFileTypes="fla,swf" LinksGalleryPath="~/Providers/HtmlEditorProviders/Fck/fcklinkgallery.aspx" DynamicStylesGeneratorPath="~/Providers/HtmlEditorProviders/Fck/FCKStyles.aspx" DynamicStylesCaseSensitive="true" DynamicStylesGeneratorFilter="controlpanel|filemanager|mainmenu|wizard" StaticStylesFile="~/Providers/HtmlEditorProviders/Fck/FCKeditor/fckstyles.xml" StylesDefaultMode="dynamic" DynamicCSSGeneratorPath="~/Providers/HtmlEditorProviders/Fck/FCKCSS.aspx" StaticCSSFile="~/Providers/HtmlEditorProviders/Fck/FCKeditor/editor/css/fck_editorarea.css" CSSDefaultMode="dynamic" spellCheck="ieSpell" AvailableToolbarSkins="Office2003,Silver" DefaultToolbarSkin="Office2003" AvailableToolBarSets="DNNDefault,Default,NoGallery,Basic" DefaultToolbarSet="DNNDefault" DefaultImageGallerySkin="Default" DefaultFlashGallerySkin="Default" DefaultLinksGallerySkin="Default" FCKDebugMode="false" UseFCKSource="false" OptionsOpenMode="ShowModalDialog" ShowModuleType="true" FixOldDNNPostback="false" CustomOptionsDialog="Admin" />
  </providers>
</htmlEditor>

Generische Methode zur Überschreibung der ToString Methode eigener komplexer Klassen – C#

Im Zuge eines Projektes musste eine komplexe ineinander verschachtelte Klassenstrukturen aufgebaut werden, um eine ebenfalls komplexe Interfacestruktur einer externen Anwendung abzubilden.

Um bei Fehlermeldungen und beim Debuggen (ja ich oute mich, ich debugge auch Code) ordentliche Daten, anstelle des reinen Objektnamen über die ToString() Methode dargestellt zu bekommen, überschreibt man in der jeweiligen Klasse die ToString() Methode und gibt darin an, welche Daten ausgegeben werden sollen.

So in etwas sieht es im Debugger aus, wenn man ToString() nicht überschreibt:

image

Für, ich will es mal “Nicht so komplexe Klassen” nennen, kann man das auch im folgenden Stil machen.

        public override string ToString()
        {
            return "Ausschreibungsstatus= " + this.Ausschreibungsstatus + 
                " " + "Ausschreibungstext= " + this.Ausschreibungstext;
        }

Das ist schon besser und sieht in etwa so aussehen:

image

Schon besser, aber ….

Ich würde gerne ohne großen Schreibaufwand in jeder Klasse die Public Eigenschaften mit den enthaltenen Werten angezeigt bekommen.

Also musste ich mir hierfür etwas mehr generisches ausdenken.

Und wieder einmal lag die Lösung meines Problems in der Verwendung von Reflection.

Hier nun meine “generische” Methode zur Bildung einer für mich “sinnvollen” ToString() Ausgabe für meine komplexen Klassen.

public static string ClassPropertiesToString(object obj)
{
    string retVal = obj.GetType().ToString() + "\n";
    const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy;
    var sourceProperties = obj.GetType().GetProperties(Flags);

    foreach (PropertyInfo pi in sourceProperties)
    {
        if (pi.PropertyType.Namespace != "System")
            continue;

        retVal += string.Format("{0}:{1}\n", pi.Name, pi.GetValue(obj, null));
    }

    return retVal;
}

Ich denke der Code ist soweit Selbsterklärend, falls nicht, einfach einen Kommentar abgeben und fragen, dann erläutern wir die Details.

Hier noch der Vollständigkeit halber, das Ergebnis im Debugger:

image

Und bevor ich es vergesse.

So sieht dann die Überschreibung in der jeweiligen Klasse aus:

public override string ToString()
{
    return Lager.Global.Common.Generic.ClassPropertiesToString(this);
}

Generische Methode um gleichnamige Properties einer Klasse zu einem Objekt einer anderen Klasse zu kopieren – C#

Bei meiner Arbeit kommt es häufiger vor, dass ich umfangreiche Klassen (welche die Daten einer Datenbank repräsentieren) verwende die mal schnell über 50 oder 100 Public Properties (Datenbankfelder) enthalten.

imageNun kommt immer wieder vor, dass ich zum Beispiel zum Archivieren solcher Daten, damit meine ich die Daten aus einer Tabelle in eine andere zu verschieben, nicht auf SQL Skripte auf dem Server zugreifen kann oder möchte, da dieses verschieben noch mit zusätzlicher Businesslogik hinterlegt werden muss.

Was bleibt ist dann so etwas in dieser Art:

var auftrag = new Auftrag
    { Position = 1, Artikel = "Mars", Beschreibung = "Schoko Riegel", Preis = 1.50 };

var auftragsArchiv = new AuftragArchiv();
auftragsArchiv.Artikel = auftrag.Artikel;
auftragsArchiv.Beschreibung = auftrag.Beschreibung;
auftragsArchiv.Preis = auftrag.Preis;

if (auftragsArchiv.Save())
{
    auftrag.Delete();
}

Diese Vorgehensweise kann bei umfangreichen Klassen sehr arbeitsintensiv sein.

Nun gibt es verschiedene Ansätze um sich diesem Problem anzunehmen. Nachfolgend möchte ich einige Lösungsansätze unter Verwendung von Reflection aufzeigen.

Ich nehme an, jeder kennt die Methode ToString().

Die Idee ist nun eine generische To…. Methode zu erstellen, welche die Aufgaben wie im obigen Beispiel dargestellt, universell erledigt.

Das Ziel soll sein, dass die Public Property Werte eines Objektes, durch Aufruf einer Methode, alle Werte die den gleichen Property Namen  haben in ein anderes Objekt einer anderen Klasse kopiert werden.

Hier nun die Variante 1 (Methode einer Klasse):

public T ToTobject<T>(T target)
{
    const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public;
    var sourceProperties = this.GetType().GetProperties(Flags);

    foreach (PropertyInfo pi in sourceProperties)
        {
            if (pi.CanWrite)
            {
				var propInfoObj = target.GetType().GetProperty(pi.Name);
				if (propInfoObj != null)
				{
					var propValue = pi.GetValue(this, null);
					propInfoObj.SetValue(target, propValue, null);
				}                             
            }
        }

    return target;
}

Der Aufruf um diese Variante zu verwenden sieht dann so aus:

var auftrag = new Auftrag
	{ Position = 1, Artikel = "Mars", Beschreibung = "Schoko Riegel", Preis = 1.50 };

var auftragsArchiv = new AuftragArchiv();

auftragsArchiv = auftrag.ToTobject(auftragsArchiv);

if (auftragsArchiv.Save())
{
	auftrag.Delete();
}

Sieht doch schon ganz gut aus.

Hier noch eine Variante, die noch etwas mehr generisch arbeitet und nur noch mitgeteilt bekommt, welchen Type ich als Rückgabe der Methode erwarte, die Instanz des Objekts der neuen Klasse wird in der Methode selbst erzeugt.

public T ToTobject<T>()
{
	var constructorInfo = typeof(T).GetConstructor(new Type[] { });
	if (constructorInfo != null)
	{
		var target = (T)constructorInfo.Invoke(new object[] { });

		const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public;
		var sourceProperties = this.GetType().GetProperties(Flags);

		foreach (PropertyInfo pi in sourceProperties)
		{
			if (pi.CanWrite)
			{
				if (!pi.IsDefined(typeof(XmlIgnoreAttribute), false))
				{
					var propInfoObj = target.GetType().GetProperty(pi.Name);
					if (propInfoObj != null)
					{
						var propValue = pi.GetValue(this, null);
						propInfoObj.SetValue(target, propValue, null);
					}
				}
			}
		}

		return target;
	}

	return default(T);
}

Diese Variante wird dann so verwendet:

var auftrag = new Auftrag
	{ Position = 1, Artikel = "Mars", Beschreibung = "Schoko Riegel", Preis = 1.50 };

var auftragsArchiv = auftrag.ToTobject<AuftragArchiv>();

if (auftragsArchiv.Save())
{
	auftrag.Delete();
}

Diese Variante gefällt mir schon etwas besser

Aber das ist noch nicht das Ende der Methoden Evolution!

Es geht noch generischer (und statisch für alle Klassen verwendbar) und diese Variante folgt nun.

public static T ToTobject<T>(object source)
{
	var constructorInfo = typeof(T).GetConstructor(new Type[] { });
	if (constructorInfo != null)
	{
		var target = (T)constructorInfo.Invoke(new object[] { });

		const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Public;
		var sourceProperties = source.GetType().GetProperties(Flags);

		foreach (PropertyInfo pi in sourceProperties)
		{
			if (pi.CanWrite)
			{
				var propInfoObj = target.GetType().GetProperty(pi.Name);
				if (propInfoObj != null)
				{
					var propValue = pi.GetValue(source, null);
					propInfoObj.SetValue(target, propValue, null);
				}
			}
		}

		return target;
	}

	return default(T);
}

Diese nun statische Variante kann auf jedes beliebige Objekt jeder beliebigen Klasse angewendet und für die Rückgabe jeder beliebigen Klasse verwendet werden. Natürlich nur solange diese Klassen auch gleiche Properties enthalten.

Und so wird diese statische Methode verwendet:

var auftrag = new Auftrag
	{ Position = 1, Artikel = "Mars", Beschreibung = "Schoko Riegel", Preis = 1.50 };

var auftragsArchiv = Auftrag.ToTobject<AuftragArchiv>(auftrag);

if (auftragsArchiv.Save())
{
	auftrag.Delete();
}

Mir hat es Spaß gemacht diese Methodik zu entwickeln, evtl. hilft es auch mal jemand anderem beim Lösen “seines” Problem.

Anregungen und Kritik wie immer gerne als Kommentar zum Beitrag.

ASCII Schnittstellen mit Hilfe von Custom Attributes komfortabel erstellen – C#

Viele kennen dass Problem, dass Daten zwischen verschiedenen Welten auch Heute noch immer durch Übergabe von Schnittstellendateien, seien es EDI oder auch ASCII Dateien, die einen festen Satzaufbau haben, übergeben werden.

Die Definitionen sehen oft wie folgt aus:

Wobei die nähere Definition etwas komplexer sein kann:

Art: A alphanumerisches Feld N numerisches Feld Länge L Gesamtlänge des Datenfeldes Nachkomma K davon Nachkommastellen Position von von Stelle ... im Datensatz bis bis Stelle ... im Datensatz M/O M Muss-Feld O Kann-Feld (optional) M/O abhängig von anderen Angaben handelt es sich wahlweise um ein Muss- oder ein Kann-Feld

Der Output, also die Übertragungsdatei enthält dann Daten die ähnlich der nachfolgenden Abbildung aussehen können.

image

Eine durchaus übliche Vorgehensweise solche Daten erstellen zu können ist es, sich Klassen zu erstellen, die dem eigentlichen benötigten Satzaufbau entsprechen, diese in einer Füll Routine mit Daten füllt und anschließend die Daten der Klasseneigenschaften mit string.Format Stück für Stück ausgibt.

Wäre es nicht wünschenswert, bereits bei der Definition der Klasse festlegen zu können, welche Ausgabeeigenschaften (Ausgabeformat wie führende Nullen, Links oder Rechtsbündig, Anzahl Nachkommastellen usw.) die jeweilige Eigenschaft besitzt und an welcher Position die Eigenschaft ausgegeben werden soll, anzugeben, damit man anschließend die Daten einfach mit einer einzelnen Methode im richtigen Format ausgeben kann.

Und genau da setze ich mit meiner Lösung an:

Man kann mit sogenannten Custom Attributes beliebig zusätzliche Informationen an jedes beliebige Klassenelement anheften. Hierzu muss man eine Klasse erstellen die von System.Attributes abgeleitet wird.

Siehe Nachfolgendes Beispiel, welches ziemlich selbsterklärend sein sollte.

using System;
    using System.Reflection;

    public enum ExportFieldType
    {
        /// <summary>
        /// Alphanumeric Field
        /// </summary>
        Alpha,

        /// <summary>
        /// Numeric Field
        /// </summary>
        Numeric
    }

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public class ExportFieldAttribute : Attribute
    {
        private readonly int index;

        private readonly int length;

        private readonly ExportFieldType fieldType;

        private readonly int precision;

        private readonly bool optionalField;

        private readonly int fromPos;

        private readonly int toPos;

        /// <summary>
        /// Initializes a new instance of the <see cref="ExportFieldAttribute"/> class.
        /// </summary>
        /// <param name="index">The index.</param>
        /// <param name="fieldType">Type of the field.</param>
        /// <param name="length">The length.</param>
        /// <param name="precision">The precision.</param>
        /// <param name="optionalField">if set to <c>true</c> [optional field].</param>
        /// <param name="fromPos">From pos.</param>
        /// <param name="toPos">To pos.</param>
        public ExportFieldAttribute(int index, ExportFieldType fieldType, int length, int precision, bool optionalField = false, int fromPos = -1, int toPos = -1)
        {
            this.index = index;
            this.fieldType = fieldType;
            this.length = length;
            this.precision = precision;
            this.optionalField = optionalField;
            this.fromPos = fromPos;
            this.toPos = toPos;
        }

        public int Index
        {
            get
            {
                return this.index;
            }
        }

        public ExportFieldType FieldType
        {
            get
            {
                return this.fieldType;
            }
        }

        public int Length
        {
            get
            {
                return this.length;
            }
        }

        public int Precision
        {
            get
            {
                return this.precision;
            }
        }

        public bool OptionalField
        {
            get
            {
                return this.optionalField;
            }
        }

        public int FromPos
        {
            get
            {
                return this.fromPos;
            }
        }

        public int ToPos
        {
            get
            {
                return this.toPos;
            }
        }
    }

Nun kann ich in der Klassendefinition in der ich die Datensatzstruktur der Exportdatei abbilde, dieses Attribut zusätzlich zu den Eigenschaften der Klasse verwenden um die für den korrekten Export benötigen Informationen anzuheften.

Die Klasse könnte so aussehen:

    public class Ksta
    {
        [ExportField(0, ExportFieldType.Alpha, 5, 0)]
        public string Satza
        {
            get
            {
                return "KSTA_";
            }
        }

        [ExportField(1, ExportFieldType.Numeric, 2, 0)]
        public int Gsber
        {
            get
            {
                return 1;
            }
        }

        [ExportField(2, ExportFieldType.Numeric, 8, 0)]
        public int Kvkda { get; set; }

        [ExportField(3, ExportFieldType.Numeric, 8, 0)]
        public int Dtkda { get; set; }

        [ExportField(4, ExportFieldType.Numeric, 5, 0)]
        public int Kvkvn { get; set; }

        [ExportField(5, ExportFieldType.Numeric, 5, 0)]
        public int Ssbpa { get; set; }

        [ExportField(6, ExportFieldType.Numeric, 8, 3)]
        public int Sskda { get; set; }
    }

Wie aber können wir diese zusätzlichen Attribute verwenden?

Ich möchte dies an einem einfachen Beispiel demonstrieren. Hierzu erweitere ich die Klasse ExportFieldAttribute um folgende Methode:

public static string ExportFieldToString(object obj, bool sorted = true)
{
	string buffer = string.Empty;
	PropertyInfo[] pi = obj.GetType().GetProperties();
	if (sorted)
	{
		var propertyInfoSorted = new PropertyInfo[pi.Length];
		foreach (var propertyInfo in pi)
		{
			var attribs = (ExportFieldAttribute[])propertyInfo.GetCustomAttributes(typeof(ExportFieldAttribute), true);
			if (attribs.Length > 0)
			{
				propertyInfoSorted[attribs[0].Index] = propertyInfo;
			}
		}

		pi = propertyInfoSorted;
	}

	foreach (var propertyInfo in pi)
	{
		if (propertyInfo == null)
		{
			continue;
		}

		var attribs = (ExportFieldAttribute[])propertyInfo.GetCustomAttributes(typeof(ExportFieldAttribute), true);

		if (attribs.Length > 0)
		{
			ExportFieldAttribute attrib = attribs[0];
			var o = propertyInfo.GetValue(obj, null);
			if (o == null)
			{
				o = string.Empty;
			}

			if (attrib.FieldType == ExportFieldType.Alpha)
			{
				string t = string.Format(o.ToString().PadRight(attrib.Length));
				buffer += t;
			}

			if (attrib.FieldType == ExportFieldType.Numeric)
			{
				if (attrib.Precision > 0)
				{
					string concat;
					var buf = o.ToString().Split(',');
					if (buf.Length > 1)
					{
						buf[1] = buf[1].PadRight(attrib.Precision, '0').Substring(0, attrib.Precision);
						concat = buf[0] + buf[1];
					}
					else
					{
						concat = buf[0] + string.Empty.PadRight(attrib.Precision, '0');
					}
					string t = string.Format(concat.PadLeft(attrib.Length, '0'));
					buffer += t;
				}
				else
				{
					string t = string.Format(o.ToString().PadLeft(attrib.Length, '0'));
					buffer += t;
				}
			}
		}
	}
	return buffer;
}

Ich möchte an dieser Stelle nun auch keine aufwändige Erklärung der Methode vornehmen, ich denke dass derjenige der sich mit diesem Problem beschäftigt erkennen wird was darin geschieht.

Und um diese Methode zu verwenden, also um den Satzaufbau der Daten so zu erhalten wie ich ihn mit den Attributen definiert habe, überschreibe ich einfach die ToString() Methode der Klasse in welcher ich die Datenstruktur abgebildet habe.

Das sieht dann so aus (In der Klasse Ksta):

        public override string ToString()
        {
            return ExportFieldAttribute.ExportFieldToString(this);
        }

Wenn ich nun eine Instanz der Klasse Ksta erstelle, diese dann mit Daten fülle und anschließen die ToString Methode aufrufe erhalten ich die Daten genau in der definierten Struktur.

Wenn jemand noch Fragen zu dem Thema hat, oder doch noch weiterführende Erläuterungen benötigt, dann einfach per Kommentar die Fragen oder Anregungen stellen.