Dynamische Forms

In Word 2003 gibt es eine Dialogbox Dokumentvorlagen und Add-Ins, die noch nicht mitbekommen hat, dass Bildschirme heute mehr Auflösung bieten als VGA von Anno Domini 1987:

Dialogbox in Word 2003

Dialogbox in Word 2003

So kommt es dann zum hier gezeigten Witz des „vollständigen Pfads“, der je nach Pfad eben gerade nicht vollständig angezeigt wird, weil schlicht der Platz dazu fehlt in der Breite der Dialogbox:

Vollständiger Pfad?

Vollständiger Pfad?

Auch wenn Word 2007 in dieser Hinsicht unterdessen dazugelernt hat, sehe ich heute mit Windows bzw. mit Windows-Programmen ein grosses Problem bei Forms aller Art, deren Grössen fix sind, mit recht kleinen Grössen notabene, um eben auch mit niedrigen Auflösungen klarzukommen. Ich schätze, dass selbst unter modernen Programmen nur etwa die Hälfte über Forms verfügt, deren Grösse man ändern kann und bei denen sich die Controls intelligent an eine neue Grösse anpassen.

Eine Zeit lang sah es ja so aus, als würde sich das Problem entschärfen, weil man als Programmierer bzw. als Designer von Forms im Laufe der Jahre von stetig grösseren Bildschirmen bei den Anwendern ausgehen durfte. So war es z.B. für die Megos sehr hilfreich, dass der Kanton Aargau um 2000 herum dafür sorgte, dass in allen Steuerämtern des Kantons Bildschirme mit mindestens 1024 mal 768 Pixeln zur Verfügung standen, so dass wir die Forms der Applikation VERANA (wie z.B. diese hier) mit dieser Annahme vergleichsweise grosszügig gestalten konnten, und deshalb irgendwelche komplexe Dynamik-Eigenschaften kein Thema waren.

Aber in letzter Zeit scheint sich das Problem wieder zu verschärfen, weil die Vielfalt unterschiedlicher Bildschirmgrössen zunimmt: Wohl ist der Trend zu grösseren Monitoren bei „normalen“ stationären PCs ungebrochen, aber mehr und mehr Leute arbeiten mit Notebooks, wo den Auflösungen natürliche Grenzen gesetzt sind. Günstige Netbooks kommen zudem zum Teil mit Auflösungen daher, die man schon fast als endgültig überwunden betrachtet hatte, und wer weiss, vielleicht gibt es bald für viele Applikationen die neue Anforderung, sie müssten auch auf Smartphones bedienbar sein, mit der erwähnten VGA-Auflösung aus der PC-Steinzeit!

Für mich stellt sich deshalb die Frage nach dynamischen Forms stärker denn je, was mich auch zu diesem Blog-Eintrag bewogen hat.

Es gibt in .NET bei den Windows Forms einige Unterstützung, die man als Programmierer nutzen kann, wenn man seine Forms „dynamisch“ betreffend Grösse machen will. Sie wissen, was ich meine: Eigenschaften wie Anchor, Dock und AutoSize bei Controls, und Container Controls wie z.B. das FlowLayoutPanel, welche ihre Kinder nach bestimmten Regeln neu anordnen können, wenn ihre Grösse ändert. Aber insgesamt würde ich sagen, dass man damit nicht allzu weit kommt, wenn Forms ein wenig komplexer werden.

Natürlich sind die Windows-Forms-Klassen flexibel genug, dass ich selber etwas programmieren kann, damit meine Forms dynamischer werden. Aber es ist doch eben gerade der Sinn eines Frameworks wie .NET, dass nicht jedermann ein so grundsätzliches Problem, mit dem man immer und überall konfrontiert ist, stets wieder neu selber lösen muss!

Als Microsoft begannt, WPF als ernstzunehmende Alternative zu Windows Forms zu positionieren, war ich deshalb sehr gespannt, ob sich die Firma in Bezug auf dynamische Forms etwas hat einfallen lassen, das deutlich über den Stand des mittlerweile 10 Jahre alten VB6 hinausgeht. So wie es aussieht, ist WPF in dieser Hinsicht eine Enttäuschung.

Wie sähe denn eine gute Standard-Lösung für dynamische Forms aus? Ich kann auch keine solche aus dem Hut zaubern, aber mir gehen Gedanken durch den Kopf, die auf einen beschreibenden Ansatz hinauslaufen. Wie wäre es, wenn man bei einem Control Bedingungen formulieren könnte, wie etwa die folgenden: „Control X in Höhe und Breite wachsen lassen, wenn die umgebenden Controls sich verschieben und das zulassen, aber nur bis zu einer Maximalbreite n, und auf alle Fälle nicht breiter als Control Y. Wenn sich unter Control Z mindestens m Millimeter Platz ergeben, dann Control X dorthin umplatzieren und an die Breite von Control Z angleichen.“

Interessant kann natürlich auch sein, was andere Leute machen, denn schliesslich bietet nicht nur Microsoft Werkzeuge an, mit denen man Forms designen kann. Insbesondere Qt scheint mir dabei einen Blick wert zu sein, weil Qt als Cross-Plattform-System von je her stärker als .NET mit Dingen wie unterschiedlichen System-Fonts und anders gezeichneten Controls konfrontiert war und damit klarkommen musste. Zudem kommen gerade jetzt zu den unterstützten Plattformen S60 für Smartphones und Maemo für Nokias „Surf Tabletts“ mit den erwähnten kleinen Auflösungen hinzu.

Ich habe erst begonnen, mich in Qt einzuarbeiten, um mich inspirieren zu lassen bei meiner Suche auf einer Lösung für dynamische Forms, aber etwas ist mir auf Anhieb ins Auge gestochen: die Spacer Controls, die man zwischen andere Controls setzen kann, die dort wie Federn wirken und die Controls soweit schieben, wie sie können:

Spacer Controls in Qt

Spacer Controls in Qt

Vielleicht bringt das gegenüber Docking plus Anchoring nur wenig mehr an grundsätzlich neuen Möglichkeiten, aber mir scheinen diese „Federn“ und ihr Verhalten etwas zu sein, mit dem ich intuitiv sofort klarkomme – für mich eine Eigenschaft eines wirklich guten Ansatzes.

Advertisements
Veröffentlicht in Keine Kategorie. Schlagwörter: , , . Leave a Comment »

Eine Art neues Grid für den Designer

Es ist im Designer des Visual Studio 2008 sehr viel einfacher geworden als früher etwa mit dem VB6-Designer, gutaussehende Forms zu produzieren, vor allem weil dank der Snaplines das Positionieren von Controls sehr viel einfacher geworden ist.

Dies ist auch gut so, wenn die Megos ihre Moneysoft-Applikationen, bisher entwickelt in einem einem eigenen System namens EMBASSY, in den nächsten Jahren in .NET neu schreiben will, denn wie man an folgendem, schon etwas älteren Screenshot sehen kann, wird es da oft Forms geben mit Dutzenden von Controls:

Valor Transaktionen

(Die vielen Controls in dieser Form sind nicht etwa schlechtes Design, sondern es gibt schlicht und einfach zu sogenannten Transaktionen in unserer Wertschriftenbuchhaltung Valor eine Menge Angaben, und die wollen alle erfasst werden. Für Kontrollen ist zudem ein schneller Überblick nötig, ohne dass man x Tabs wechseln muss.)

Als ich einmal eine solche Form testweise mit dem Visual-Studio-Designer nachbaute, merkte ich allerdings schnell, dass selbst die Unterstützung durch Snaplines noch nicht das ist, was ich eigentlich gerne hätte, um wirklich produktiv arbeiten zu können. Die Crux ist die: Forms, die praktisch nur aus vielen Labels, TextBoxes und GroupBoxes bestehen, sehen am besten aus (und die Controls haben am besten Platz), wenn man die Controls so positioniert und ihre Grössen so setzt, als würde man mit einem sehr speziellen Grid arbeiten – einem Grid, das mit einer „Zeilenhöhe“ und einer „Durchschnittszeichenbreite“ funktioniert.

Der Visual-Studio-Designer ist sehr modular aufgebaut und bietet eine Menge Möglichkeiten, Design-Prozesse zu beeinflussen, etwa mit Hilfe von ControlDesigner-Klassen, aber eine Möglichkeit, das Verhalten des Grids zu steuern oder zu übersteuern, scheint es nicht zu geben. Will man trotzdem mit einem speziellen Grid wie dem soeben beschriebenen arbeiten, muss man also zu einem Trick greifen.

Mein Trick ist der folgende: Controls haben eine protected virtual, also überdefinierbare Methode namens SetBoundsCore, die für das Setzen der Position und der Grösse eines Controls zuständig ist. Hier greife ich ein und runde Koordinaten auf mein Spezial-Grid, bevor ich sie an die Basis-Methode übergebe.

Das ist eine zwar drastische, aber einfache und effektive Methode, um x und y auf bestimmte Werte zu zwingen. Nichts und niemand im ganzen System kommt um die von mir etablierte Rundung der Koordinaten herum, auch der Designer nicht. Der Effekt im Designer ist der, dass man zwar ein solches Control nach wie vor mit der Maus frei herumziehen kann, aber sobald man es „absetzt“, kommt die geänderte SetBoundsCore-Methode zum Zug, und das Control hüpft sozusagen von sich aus auf die „richtige“ Position gemäss Grid. Das ist natürlich ungewohnt und am Anfang auch etwas gewöhnungsbedürftig, fühlte sich aber für mich schon nach kurzer Zeit sehr angenehm an.

Folgendes kleine Code-Fragment zeigt ein Label-Control, welches man nur gemäss einem Grid positionieren kann, dessen Zellen 30 Pixel breit und 15 Pixel hoch sind (vielleicht nicht unbedingt nützlich, zeigt aber den Effekt im Designer sehr schön):

  public class GriddedLabel : Label {
    protected override void SetBoundsCore(int x, int y, int width, int height, BoundsSpecified specified) {
      x = (x / 30) * 30;
      y = (y / 15) * 15;
      base.SetBoundsCore(x, y, width, height, specified);
    }
  }
Veröffentlicht in Keine Kategorie. Schlagwörter: , , , . Leave a Comment »

Vorkonfigurierte Controls

Ich vermute, dass ausser mir eine ganze Reihe von .NET-Programmieren auch schon in folgender Situation waren: Man muss eine neue Form mit einer ganzen Reihe von Controls definieren im Designer, und für viele der Controls stehen eine Anzahl von Eigenschaften eigentlich schon von Beginn weg fest, und mit der Zeit wünscht man sich, der Designer würde – irgendwie! – diese Eigenschaften gleich beim Erzeugen der Objekte von selbst richtig setzen, so dass man das nicht für x Controls wieder und wieder von Hand selbst tun muss.

Ein Beispiel: Eine Firma arbeitet gewissen Normen punkto Buttons. Sie müssen alle dieselbe, festgelegte Grösse haben, und der Name sei abzuleiten vom Text des Buttons. Wie schön wäre es doch, wenn man dem Designer eine Funktion Erzeuge Standard-Button beibringen könnte, die nach Eingabe des Button-Textes den Button gleich so erzeugt?

Hier würde natürlich ein User Control Standard-Button auch helfen, aber wenn das Szenario nur etwas vielfältiger wird, wie im folgenden Beispiel, ist das kein geeignetes Mittel mehr:

Man portiert eine alte Applikation auf .NET und beginnt mit der Form nicht bei Null, sondern hat im Prinzip die Liste der Controls für die Form schon, als Tabelle in einer Textdatei, mit Namen, Typen und Längen, vom „alten“ Entwicklungs-System her. Es wäre doch schön, diese Tabelle irgendwie im Designer einlesen zu können und für jedes „alte“ Control ein entsprechendes .NET-Control zu erzeugen.

Wie viele Dinge im .NET-Framework geht ein solchermassen Code-gesteuertes Erzeugen von Controls im Designer ziemlich einfach, wenn man den „Kniff“ kennt:

Man schafft sich zuerst die Möglichkeit, eigenen Code im Designer-Kontext laufen zu lassen, z.B. indem man einen System.Windows.Forms.Design.ControlDesigner samt DesignerActionList implementiert, und kann anschliessend neue Controls mit Hilfe des aktiven IDesignerHost-Objekts ganz einfach erzeugen:

   IDesignerHost designerHost = (IDesignerHost)GetService(typeof(IDesignerHost));
   Button b = (Button)designerHost.CreateComponent(typeof(Button), buttonName);
Veröffentlicht in Keine Kategorie. Schlagwörter: , . Leave a Comment »

Forcierte Updates für den Forms-Designer

Es kann vorkommen, dass während der Arbeit im Windows-Forms-Designer an einem Control so etwas ändert, dass es der Designer nicht mitbekommt. Dies ist speziell dann der Fall, wenn man wie in einem früheren Blog-Eintrag hier beschrieben quasi „hinterrücks“ Eigenschaften eines Controls ändert, ohne dass der Designer direkt beteiligt ist, nämlich via Code beim Control selbst.

Konkret hatte ich das Problem, dass ich den Text eines Label-Controls änderte, aber die Markierung rund um das Control nicht an die neue visuelle Länge angepasst wurde.

Es hat sich zum Glück herausgestellt, dass es relativ einfach ist, an die Komponente des Designers heranzukommen, welche für Reaktionen auf solche Änderungen zuständig ist. Konkret in Code:

 protected override void OnMouseClick(MouseEventArgs e) {
   base.OnMouseClick(e);
   if (DesignMode) {
     // gewünschte DesignTime-Verarbeitung 
     IComponentChangeService changeService = (IComponentChangeService)Site.GetService(typeof(IComponentChangeService));
     if (changeService != null) {
       changeService.OnComponentChanged(this, null, "", "");
     }
   }
 }
Veröffentlicht in Keine Kategorie. Schlagwörter: , , . Leave a Comment »