Im Silverlight Toolkit für Windows Phone 7 gibt es ein recht hübsches Control für die Eingabe eines Datums, das DatePicker Control. Es besteht aus der Kombination einer Textbox, welche das aktuell gültige bzw. gewählte Datum anzeigt, und einer eigenen vollen Seite, auf der man das Datum “touch-gerecht” eingeben kann; man errreicht diese Eingabeseite, in dem man die Textbox antippt. Einen Überblick über die Verwendung des Controls in einem Programm gibt es z.B. hier.
Ich wollte das Control etwas anders verwenden, ohne Textbox als Eingabefeld: Die Eingabeseite sollte auf ein Menukommando hin erscheinen, und das gewählte Datum zum Text in einer Textbox hinzugefügt werden.
Ein solches Anwendungsszenario ist von den Machern des Controls nicht vorgesehen, aber mit ein bisschen Tricksen geht es doch:
Um das Control programmatisch auszulösen und auf die Eingabeseite zu gelangen, muss man das Antippen der Textbox simulieren. Das geht, indem man eine eigene Klasse von DatePicker ableitet, damit man an einen Button herankommt, der ein Kind der Textbox ist, und diesen Button per Code “drückt”:
public class CustomDatePicker : DatePicker {
public void ClickTemplateButton() {
Button button = (GetTemplateChild("DateTimeButton") as Button);
ButtonAutomationPeer peer = new ButtonAutomationPeer(button);
IInvokeProvider provider = (peer.GetPattern(PatternInterface.Invoke) as IInvokeProvider);
provider.Invoke();
}
}
Gefunden hatte ich diesen Trick auf einer Website namens wp7-developer.com, aber diese ist offenbar einem Besitzerwechsel zum Opfer gefallen. Interessanterweise behaupten die Antworten zu dieser Frage auf Stack Overflow, es gäbe keine solche Möglichkeit.
Beim API-Minimalismus, der allgemein bei WP7 herrscht, habe ich mich übrigens gewundert, dass es eine Klasse wie ButtonAutomationPeer überhaupt gibt. Vielleicht hat ja bei der Implementation von Silverlight für WP7 nur jemand vergessen, diese Klasse zu löschen…
Auf der Eingabeseite gibt es 2 Buttons, einen, um das Datum zu akzeptieren, und einen, um die Wahl zu verwerfen. Normalerweise führt das Control selbständig seine Value-Property nach oder eben nicht, je nach dem, wie der Benutzer die Eingabeseite verlässt, und man muss im Programm nichts davon wissen.
In meinem Nicht-Standard-Anwendungsfall sieht das allerdings etwas anders aus: Bei Eingabe akzeptiert soll der Datumstext eingefügt werden, bei Eingabe verworfen nicht. Wie kriegt man das hin? Eine erste Suche nach einer Property, welche direkt aussagt, wie die Eingabe endete, war erfolglos.
Das DatePicker-Control bietet den üblichen ValueChanged-Event an, an den man sich hängen kann, um mitzubekommen, wenn der Wert ändert. Das Problem: Wenn der Wert nicht ändert, dann auch kein ValueChanged, selbst wenn der Benutzer die Eingabe bestätigt. Der Effekt bei Verwendung dieses Events ist darum, dass eine Eingabe des voreingestellten Datums nicht möglich ist.
Es gibt allerdings eine ganz einfache Lösung: DatePicker.Value ist nullable. Setzt man die Property auf null vor Aufruf des Controls, ist hinterher die Property gesetzt, wenn der Anwender bestätigte, und immer noch null, wenn er die Eingabe abbrach.
Die Implementation des Controls als eigene Seite mit einem Verhalten, das einem Popup entspricht, hat übrigens einige ganz interessante Aspekte, welche hier näher erläutert werden.