Thomas Wölfers Baustatik-Blog

Thomas Wölfers Baustatik Blog

Ein besserer Taschenrechner


Jeder Statiker hat vermutlich sowieso immer einen Taschenrechner auf seinem Schreibtisch liegen - aber eigentlich wäre das ja gar nicht notwendig: Schließlich hat Windows ja einen prima Taschenrechner eingebaut - und der kann (im richtigen Betriebsmodus) auch mit allen notwendigen Formeln umgehen.

Für das normale 'Calc' gibt es aber auch einen aufwendigeren (und kostenlosen) Ersatz: Calculator Plus. Zusätzlich zu normalen Rechnenoperationen und den wissenschaftlichen Funktionen hat der Calculator weitere Features: Er kann nämlich zusätzlich noch umrechnen. Und zwar alles mögliche: Währungen, Längen, Volumen, Energie - und so weiter und so fort. 'Meter' kann man zum Beispiel in 'Feet' umrechnen - aber auch in Lichtjahre, in 'hunh' (was immer das ist), in parsecs, in picas und duzend von anderen Einheiten: Wirklich sehr praktisch - für jeden Ingenieur ein guter Ersatz für den normalen Windows-Rechner.

Natürlich kein Ersatz für unsere Statiksoftware :-)


Hilfe bei Speicher-Lecks


Der Garbage Collector ist (für einen Programmierer mit C/C++ Hintergrund) einer der großen Vorteile der .Net CLR. Man erhält deutlich mehr Freiheiten beim programmieren, weil man sich eben nicht mehr darum kümmern muss Speicher auch wieder freizugeben.

Nur stimmt das leider nicht: Man kann auch in .Net Programmen prima Speicherlecks produzieren - und aufgrund bestimmter Funktionalitäten sind die sogar noch besser versteckt als das bei nativem Win32 Code der Fall ist.

Bei .Net tritt ein Speicherleck dann ein, wenn man unbeabsichtigterweise einen Zeiger auf ein Objekt behält: Solange diese Zeiger (diese 'Referenz') auf das Objekt besteht, kann das Objekt nicht garbage collected werden - und stellt effektiv ein Leck dar.

Das klingt zunächst nicht so dramatisch - aber man muss sich vergegenwärtigen, wie leicht man so eine Referenz erzeugen und vergessen kann: Angenommen Sie haben einen Eventhandler für ein bestimmtes Ereignis in einem anderen Objekt. Wenn Sie den nicht irgendwann wieder entfernen, dann bleibt die Referenz bestehen - und das 'andere' Objekt ist effektiv ein Speicherleck sobald es nicht mehr gebraucht wird. Genau, Sie haben richtig gelesen: Natürlich ist auch ein Eventhandler eine Referenz die dazu führt das der Garbage Collector nicht anspringt.

Solche Probleme sind schwer zu finden, es gibt aber Tools die dabei helfen. Zum Beispiel das hier: Der SciTech .NET Memory Profiler. Das Programm verschafft einen jede Menge interessante Einblicke - und ist mit 100 Dollarn auch nicht besonders teuer.


Lokalisierbare Enums


Um Objekte bzw. deren Texte lokalisierbar zu machen kommen im Fall von 'Enums' die TypeConverter ins Spiel. Die Idee dabei ist, das man einfach einen TypConverter der die Konvertierung von und nach 'string' beherrscht an beliebige Enums dranhängt - und die dann im Zuge der Konvertierung gleich die lokalisierten Strings liefern.

Nichts einfacher als das - es gibt sogar eine praktische Basisklasse dafür: Den EnumConverter. Die Dokumentation dazu macht einen aber reichlich kirre: Zwar gibt es einen extra Hinweis für Leute die davon ableiten wollen - nur gibt es dann absolut nichts zum Thema Konstruktor. Der EnumConverter hat nämlich einen privaten Defaultkonstruktor, und mindestens einen weiteren der einen Parameter bekommt. Der ist aber in der normalen Doku nicht dokumentiert.

Mit anderen Worten: Auf der anderen Seite soll man zwar erben können, auf der anderen Seite wird aber nicht verraten wie. Wer nun meint 'Kein Problem, dann erbt man eben einfach von 'TypeConverter' - der hat natürlich Recht, und genau das habe ich vor etwa zwei Jahren auch getan.

Heute bin ich aber wieder mal über den Code gestolpert und habe nachgesehen - und mittlerweile ist der Konstruktor zum EnumConverter dokumentiert und kann damit (offiziell) benutzt werden. (Die Doku sagt zwar, der Konstruktor wäre Teil der Infrasturktur die man nicht benutzen soll, aber der Hinweis für 'Erbende' widerspricht diesem Teil der Dokumentation ja eindeutig....)

Lange Rede kurzer Sinn: Geerbt von EnumConverter ist der Lokalisierbare EnumConverter extrem einfach implementiert. (Tipp: Der Parameter ist natürch der Typ des Enums, aber das hätte man auch mit Reflector herausfinden können.)


Mehrere Schriftköpfe für den Ausdruck


Wenn Sie Ergebnisse oder Eingabedaten unserer Statikprogramme ausdrucken möchten, dann können Sie das im 'klassischen' oder 'normalen' Format tun. Beide Formate haben ihre Vor- und Nachteile - es ist aber in beiden Formaten möglich unterschiedliche Schriftköpfe auf dem Ausdruck zu verwenden.

Beim 'normalen' Format ist das relativ klar: Sie verwenden einfach eine andere Layout-Datei - fertig.

Beim 'klassischen' Format ist das weniger klar. Was Sie hier tun müssen ist, Sie müssen einen 'Schriftkopfsatz' anlegen. Ein 'Schriftkopfsatz' enthält Informationen über die Texte in der Kopf- und Fußzeile, über die Schriftgröße und Schriftart usw.

Sie können mehrere Schriftkopfsätze definieren - welcher dann verwendet wird können Sie in der Dialogbox 'Schriftkopf einstellen' auswählen: Dazu gibt es auf dieser Dialogbox ganz oben eine Auswahl-Liste zum ausklappen. Es reicht, wenn Sie den gewünschten Satz auswählen und dann OK klicken - der nächste Ausdruck erscheint dann mit dem ausgewählten Schriftkopf.


Nochmal: Context-Menus, diesmal in einer TreeView


Irgendwie scheint sich das bei mir zum Monat der Context-Menüs zu entwickeln... hier und hier gibt schon mehr...

Trotzdem nochmal ein Nachschlag - diesmal gehts um ein Context-Menü in einer TreeView. Einmal angenommen ein rechter Mausklick auf eine Node in der TreeView soll die Node als selektiert markieren und dann ein Popup-Menü für die Node öffnen.

Nichts leichter als das: Man legt in der TreeView einen Handler für das Popup-Event des Menüs an, und wenn der Eintritt dann setzt man die Node eben als selektiert (oder verändert deren Farben, je nachdem welchen Effekt man will), füllt das Menü ganz nach Wunsch mit Befehlen auf - und Windows zeigt das Ding dann an.

Das klappt auch wunderbar - nur leider bekommt man dann einen extrem unschönen Flacker-Effekt in die TreeView: Das Ding wird komplett neu gemalt und das führt zu einem echt irritierenden Blitzen.

Grund: Wenn das Popup-geöffnet wird darf man eben nichts mehr malen, der PopUp-Event ist einfach der falsche Zeitpunkt.  Das steht zwar nirgends (zumindest habe ich nichts gefunden), ist aber relativ leicht mit Spy++ herauszufinden wenn man sich die WM_ERASEBKGNDs und die WM_PAINTs ansieht - vor allem deren Reihenfolge, denn die ändert sich wenn man das malen während des Popup-Handlers weglässt.

Man muss also folgendes tun: Im MouseUp-Event führt man die Selektion durch und setzt die Node eben als ausgewählt und im Popup-Event füllt man nur das Menü auf. Dann ist beides an der richtigen Stelle und das flackern hat ein Ende.


Fürs Wochenende: Zweimal verkehrt


Als Wochenend-Unterhaltung gibt es diesmal: Reverse - eine interessante Art die Hand-Auge Koordinaten zu trainieren und elgooG damit das suchen wieder interessanter wird.

Achtung: Beide Links zeigen auf gespiegelte Seiten, der Rest des Internet bleibt aber dankbarerweise unverändert.


Unterstützung für Din 1045-1


Wir haben da nicht besonders drauf hingewiesen - aber natürlich unterstützen unsere Statikprogramme auch die kommende DIN 1045-1. Die Unterstützung für Bemessung nach Din 1045-1 ist im Laufe des Jahres Stück für Stück in die Programme eingebaut worden und wird zur Zeit in einigen Stellen noch weiter integriert: Prinzipiell ist die 'neue' DIN aber in (nahezu) allen Programmen fertig eingebaut. Das gilt zum Beispiel für den Durchlaufträger, das FEM-Plattenprogramm und auch das Tragwerksprogramm.

In den online verfügbaren Programmbeschreibungen wird darauf noch nicht in allen Fällen hingewiesen - eingebaut ist die Sache aber trotzdem.

Dabei zeigt sich auch wieder einmal der Vorteil eines Work&Cash Vertrages: Wer unsere Programme im Rahmen von Work&Cash nutzt hat alle Updates bezüglich der neuen DIN völlig kostenlos erhalten - genau wie allen anderen anfallenden Programm-Updates und Neuerungen. Anders als andere Hersteller haben wir die neue DIN eben nicht zum Anlass kostenpflichtigen Zwangs-Updates (immehin kommt man ja nicht daran vorbei nach den neuen DIN zu rechnen) zu verkaufen - Work&Cash Kunden sparen also mal wieder bares Geld.


Nicht vergessen: Idle-Handler entfernen


Wenn man in einer Dialogbox (Form) das UI updaten will, dann bietet sich dafür ein Idle-Handler an: In dem kann man prima Buttons disablen, Texte setzen und ähnliches tun. Den Handler installiert man ganz einfach nach InitializeComponent(), den Code dazu plaziert man einfach als private Methode in die Form:

Application.Idle += new EventHandler(Application_Idle);

Das Problem: Damit hat man der Anwendung einen neuen Idle-Handler zugewiesen - und was man dabei leicht vergisst: Der verschwindet natürlich nicht von selbst, nur weil man das Fenster schliesst. Resultat: Die Anwendung hat einen Zeiger auf den Idle-Hander, der Idle-Handler befindet sich in der Form - und die Form wird deshalb nie Disposed(). Einer der bösen Fälle wie man mal so eben auf die schnelle mit .Net ein Speicherleck erzeugt.

Lösung: Man merkt sich den Idlehandler in einer privaten Variable und trennt sich in Form.Closing() oder Form.Closed() wieder von der Anwendung ab.

// Init ....
idleHandler =
new EventHandler(Application_Idle);
Application.Idle += idleHandler;

// Closed ...
Application.Idle -= idleHandler;

Morgen: Wieder etwas über unsere Statikprogramme.


Nachschlag: Dynamisch veränderte Menüs


Hier noch ein kurzer Nachschlag zum Beitrag übers verhindern von Flackern bei der dynamischen Veränderung von Menüs von vorgestern.

Wenn man wie dort angegeben das neuzeichnen verhindert, dann passiert es natürlich auch: Es wird nichts neu gezeichnet. Das ist schön und macht kein Flackern - wenn sich aber auch das 'MainMenu' der Form zwischenzeitlich ändert, dann wird das natürlich auch nicht neu ausgegeben.

Resultat: Das sichtbare Menü hat dann nicht mehr viel mit dem echten zu tun. Fährt man dann mit der Maus über die Menupunkte, dann kommt dabei nach und nach das 'richtige' Menü zutage.

Man muss sich also darum kümmern, das das Menü nach der Aenderung am Menü zumindest einmal ausgegben wird.

Invalidate( true) der Parent-Form reicht dabei nicht aus, weil dabei wie früher bei Win32 zwar die Kindfenster, nicht aber die Non-Client Area und auch nicht das Menü neu gemalt werden. Statt dessen braucht es eine weitere Methode die man nur per Interop erreichen kann: DrawMenuBar(). Das Ding erwartet einen Parameter, und das ist das Handle des Fensters dessen Menü neu gezeichnet werden soll. Die Signatur für Interop sieht wie folgt aus:

[DllImport("User32.dll", CharSet=CharSet.Auto)]
private static extern int DrawMenuBar(IntPtr hWnd);

Der Aufruf (irgendwo im Form-Code) ist dann einfach:

Invalidate( true);  // die restlichen Kinder auch invalidieren
DrawMenuBar(
this.Handle);  // das Menu neu zeichnen