Ab sofort steht unter http://devtyr.norberteder.com mein neues Blog zur Verfügung. Dieses Blog wird nicht weiter betreut, bleibt aber erhalten. Neue Eintr%auml;ge erfolgen nur mehr im neuen Blog. Kommentare werden ebenfalls nicht mehr behandelt. Wer weiterhin meinen Einträgen und Aktivitäten folgen möchte, möge bitte RSS-Feeds, Verlinkungen etc. an die neue Location anpassen.
Mit List.ForEach durch Listen iterieren
31.01.08 - .NET, Grundlagen, Base Framework Beitrag von Norbert Eder
Die meisten Leser werden wohl for und foreach kennen, um durch Listen zu iterieren. Es gibt jedoch bei den generischen Listen (auch Arrays) eine Methode ForEach.
Damit ist es möglich, eine Aktion auf jedes Element der Liste auszuführen. Nachfolgend ein Beispiel welches mit einer anonymen Methode arbeitet und die Methode ForEach verwendet:
List<int> testList = new List<int>();
for (int i = 0; i < 100; i++)
testList.Add(i);
int sum = 0;
testList.ForEach(delegate(int val)
{
sum += val;
});
Console.WriteLine(sum.ToString());
Der interessierte Leser fragt sich nun an dieser Stelle, wie es mit der Performance der einzelnen Möglichkeiten aussieht, woraus sich auch ergibt, welche Variante eingesetzt werden sollte (nicht ganz, denn in Bereichen an denen Performance nicht ganz so wichtig ist, entscheiden auch andere Aspekte).
29.01.08 - .NET, Grundlagen, Base Framework Beitrag von Norbert Eder
Über Extension Methods hatte ich bereits berichtet. Einfach können hier beliebige Typen um Methoden erweitert werden.
Nun gibt es die statische Methode String.IsNullOrEmpty um zu überprüfen, ob ein String einen Wert enthält oder nicht.
Thomas Freudenberg hat hierzu ein kleines Beispiel erweitert, wie diese statische Methode als Extension Method genutzt werden kann und daher bei öfterer Verwendung doch einiges an Zeit sparen kann. Nette Idee.
27.01.08 - .NET, Grundlagen, Base Framework, WPF, ASP.NET, Silverlight, Mobile Devices, Datenverwaltung, Visual Studio, Allerlei, Internet, Community Beitrag von Norbert Eder
Das .NET BlogBook ist ab sofort auf einer eigenen Webpräsenz zu finden. Der Hintergrund besteht aus mehreren Punkten, wovon ich einige hier nennen möchte.
.NET Casts Ursprünglich wurde das .NET BlogBook als Nebenprodukt von .NET Casts geführt. Thematisch passten beide Bereiche nicht optimal zusammen, wodurch diese nun getrennt wurden. Auf beiden Seiten wird nun der eigentilche Bereich entsprechend gefördert.
Andrang Das .NET BlogBook darf sich größter Beliebtheit erfreuen, wodurch eine eigene Webpräsenz fast unumgänglich wurde. Dadurch besteht nun die Möglichkeit, zukünftig besser auf unsere Leser eingehen zu können. Aber dazu möchte ich vorerst noch nicht zuviel verraten.
Dieser Schritt soll das .NET BlogBook zu einem eigenständigen Produkt ausbauen und zukünftig auch wesentlich zur Verbesserung der Qualität beitragen.
PS: Die Inhalte bleiben noch die nächsten Tage auf der .NET Casts Website erhalten, werden anschließend jedoch mit einer entsprechenden Meldung von der Site genommen.
25.01.08 - .NET, Allerlei, Internet, Community Beitrag von Norbert Eder
Aktuell gibt es ja wahrlich eine Flut von neuen .NET Communities. Dieses Mal hat sich Jan Welker von .NET Snippets aufgemacht, ein neues Forum rund um das Microsoft .NET Framework zu starten.
Wie es auch sein muss, setzt er mit dem Forum auf ein .NET basiertes System.
Ich bin schon gespannt, welche der neuen Communities sich langfristig im deutschsprachigen Raum etablieren kann.
24.01.08 - .NET, Allerlei Beitrag von Norbert Eder
Mein Arbeitgeber, die UPPER Network GmbH ist auf der Suche nach .NET Entwicklern. Als Standort kommen Wien oder Graz in Frage.
Weitere Informationen rund um uns, was wir tun, was wir bieten, was wir jedoch auch verlangen sind in der nachfolgenden Stellenausschreibung (PDF) zu finden:
Wie in jeder anderen Anwendung, kann natürlich auch in einer WPF-Applikation der Fehlerteufel stecken. Werden etwaige Fehlverhalten nicht abgefangen, kommt es zu ungewollten Abstürzen, die nicht nur beim Endkunden/-benutzer nur sehr unwillig aufgenommen werden. es gilt also, sämtliche Fehler abzufangen.
Grundsätzlich gibt es beim Exception-Handling keine Unterschiede zum Windows-Forms-Pendant, da es sich ja um eine Funktionalität des Frameworks handelt. Wobei es jedoch einen Unterschied gibt, ist das globale Abfangen von Fehlern.
Implementierung
Hat man bei einer Windows-Forms-Anwendung einen try-catch-Block um den Aufruf des Hauptfensters gelegt, steht in der Windows Presentation Foundation das Event DispatcherUnhandledException zur Verfügung. Hier stehen nun zwei Wege zur Verfügung, wie der dazu notwendige Eventhandler gesetzt werden kann.
Die erste Variante besteht in der Deklaration in der App.xaml, wie folgt:
Weiters kann das Event, wie unter Windows Forms, direkt per Code abonniert werden:
publicpartialclass App : Application
{
public App()
{
this.DispatcherUnhandledException +=
new DispatcherUnhandledExceptionEventHandler(
App_DispatcherUnhandledException);
}
}
Schlussendlich muss noch der Eventhandler selbst implementiert werden, welcher in diesem kleinen Beispiel lediglich eine MessageBox mit der Exception-Message ausgibt. An dieser Stelle wäre es durchaus sinnvoll, die nicht behandelten Exceptions in eine Log-Datei mitzuschreiben um später die Möglichkeit einer Auswertung zu haben.
Durch das Setzen der Eigenschaft Handled wird festgesetzt, dass WPF diese Exception nicht weiter behandelt, da dies bereits innerhalb des Handlers vorgenommen wurde.
Wichtige Hinweise
Durch das Event DispatcherUnhandledException werden alle unbehandelte Ausnahmen gemeldet, welche im Thread des User Interfaces laufen. Treten Fehler beispielsweise innerhalb eines Background Workers auf, gibt es darüber keine Benachrichtigung über DispatcherUnhandledException. In diesen Fällen müssen Sie sich selbst darum kümmern, in dem entweder eigene Fehlerbehandlungsroutinen geschrieben werden, diese Exceptions an den Haupt-UI-Thread übergeben werden bzw. diese im Haupt-UI-Thread nochmals werfen, ohne sie direkt zu behandeln. Auch in diesem Fall wird diese Exception dann über das Event DispatcherUnhandledException gemeldet.
Durch den Parameter DispatcherUnhandledExceptionEventArgs wird zum einen die geworfene Exception überreicht und weiters der Dispatcher, von dem aus die Exception geworfen wurde. Durch die Angabe der Exception kann nun auch überprüft werden, ob dieser Fehler überhaupt behebbar ist oder nicht. Nicht behebbar wäre beispielsweise eine StackOverflowException.
Fazit
Fehlerbehandlung ist ein absolut wichtiges Konzept, um den Benutzer vor Fehlern zu schützen und ein ungewolltes Beenden einer Anwendung zu verhindern. Verwenden Sie diese Methode, um alle behebbaren Exceptions abzufangen, die in ihrer Anwendung nicht behandelt werden. Achten Sie jedoch explizit auf Fehler, die in unterschiedlichen Threads auftreten.
... gibt es ein Update der Trickkiste. Seit der letzten Anpassung sind wieder einige Artikel hinzugekommen - vor allem bezüglich Windows Presentation Foundation. Diese wurden nun in die Trickkiste integriert und stehen dort nun in der Übersicht zur Verfügung und sollten somit schnell gefunden werden können, als auch einigen Lesern helfen.
An diesem Beispiel ist zu sehen, dass ein Button definiert wurde. Dieser reagiert auf das Click-Ereignis. Anstatt nun einen Eventhandler zu setzen, welcher in der Code-Behind-Datei behandelt wird, wird dieser über x:Code direkt im XAML behandelt.
Hinweise zum x:Code Element
Der Vollständigkeit halber, hier ein paar Hinweise zu diesem Element.
Da hier Code direkt in XAML eingefügt wird, sollte normalerweise ein CDATA verwendet werden. Hintergrund ist, dass beispielsweise das Einfügen von < und > zu einem invaliden XML führen würde - und XAML ist ja im Endeffekt uach nur XML. Im obigen Beispiel wurde CDATA weggelassen, da kein spezielles Zeichen vorkommt.
Weiters gibt es bei der Verwendung von x:Code einige Limitierungen. Bei der Kompilierung wird sämtlicher Code innerhalb eines x:Code Elements als Teil der dahinterliegenden partiellen Klasse angesehen. Daher ist es nicht möglich, zusätzliche Klassen zu definieren (ausser nested Classes). Wird auf Entitäten ausserhalb der partiellen Klasse verwiesen, kann dies nur voll qualifiziert passieren.
Resultierende Probleme
Aus der Verwendung des x:Code Elements ergeben sich einige Probleme, die es zu wissen gilt.
Interpretierung/Kompilierung Anwendungen, die XAML-Code interpretieren, müssen darin enthaltenen Sourcecode kompilieren können. Dies ist jedoch nicht immer der Fall. Beispielsweise kann dies der Internet Explorer nicht, Visual Studio hingegen schon. Dies bedeutet, dass obiger Code nicht im IE funktionieren würde, da die notwendigen Assemblies nicht im Hintergrund generiert werden können.
Trennung Design und Implementierung WPF besitzt den Vorteil, dass Design und Implementierung voneinander getrennt werden können. Bei entsprechenden Ressourcen kann als XAML durch einen Designer aufbereitet werden, während ein Entwickler die tatsächliche Implementierung vornimmt. Wird nun das x:Code Element verwendet, wird der Präsentationsteil mit der Logik verbunden, was zum einen eine hohe Koppelung bringt und zudem einen wesentlich erhöhten Wartungsaufwand. Selbst Microsoft weist darauf hin:
In terms of architecture and coding philosophy, maintaining a separation between markup and code-behind keeps the designer and developer roles much more distinct.
Limitierung Da für das x:Code Element (wie oben beschrieben) Limitierungen bestehen, kann kaum etwas ernsthaftes in ein XAML eingebettet werden.
Daher stellt sich die grundsätzliche Frage, warum es dieses Element überhaupt gibt, da es schlussendlich verleitet, eben mal schnell dann doch den Code direkt ins XAML zu schreiben.
Fazit
Warum es das x:Code Element gibt, kann schwer beantwortet werden. Möglicherweise gibt es einen guten Grund, welcher sich mir im Moment entzieht. In der Praxis sollte dieses Element jedoch nicht verwendet werden. Selbst für eine kurze Demonstration sollten man doch lieber zur sauberen Variante greifen.
Animationen mittels WPF sind nicht schwer. Dieser Artikel soll eine kleine Einführung liefern und zeigen, wie einfach eine kleine Animation mit Fotos gemacht werden kann. Das Resultat ist nachfolgend kurz zu sehen.
Erste Schritte
Bevor es an die Animation selbst geht, entwerfen wir ein UserControl, welches für die Darstellung der Fotos zuständig ist. Hier auch gleich der entsprechende Code.
publicpartialclass PictureControl : UserControl
{
private Size _originSize;
private Point _originLocation;
publicstatic DependencyProperty OriginSizeProperty =
DependencyProperty.Register(
"OriginSize",
typeof(Size), typeof(Size));
publicstatic DependencyProperty OriginLocationProperty =
DependencyProperty.Register(
"OriginLocation",
typeof(Point), typeof(Point));
public PictureControl()
{
InitializeComponent();
}
publicvoid SetImage(string path)
{
ImageControl.Source = BitmapFrame.Create(new Uri(path));
}
public Size OriginSize
{
get { return _originSize; }
set { this._originSize = value; }
}
public Point OriginLocation
{
get { return _originLocation; }
set { _originLocation = value; }
}
}
Darin befinden sich keine Besonderheiten. Es wird lediglich eine Eigenschaft SetImage angeboten. Darüber kann das gewünschte Foto gesetzt und somit angezeigt werden.
Weiters wird dieses erstellte Control nun in ein Window eingebettet, damit auch tatsächlich Fotos angezeigt werden können. In dieser Demoanwendung werden insgesamt vier Fotos dargestellt - also wenig dynamisch.
Da nun die grundlegenden Arbeiten erledigt sind, werden wir uns dem Thema Animation zuwenden.
Animation erstellen
Wichtigster Bestandteil für eine Animation ist das Storyboard. Dabei handelt es sich um eine Timeline, die auf einzelne Elemente und dazugehörige Animationen verweist. Mittels Begin werden die in einem Storyboard enthaltenen Animationen ausgeführt. Der Ablauf des StoryBoards kann mittels Stop angehalten werden. Weiters stehen Pause, Resume und Seek zur Verfügung. Zusätzlich stehen Events zur Verfügung, um beispielsweise auf die Beendigung des Durchlaufs eines Storyboards reagieren zu können. Es empfiehlt sich einen Blick auf die Storyboard Übersicht zu werfen.
Durch die WPF werden bereits einige vordefinierte Animationen unterstützt. Diese leiten von AnimationTimeline. Diese Klasse basiert wiederum auf Timeline.
Folgende Ableitungen von AnimationTimeline stehen zur Verfügung, worauf dann weitere Timelines basieren.
DoubleAnimationBase
BooleanAnimationBase
ByteAnimationBase
CharAnimationBase
ColorAnimationBase
DecimalAnimationBase
Int16AnimationBase
Int32AnimationBase
Int64AnimationBase
MatrixAnimationBase
ObjectAnimationBase
Point3DAnimationBase
PointAnimationBase
QuaternionAnimationBase
RectAnimationBase
Rotation3DAnimationBase
SingleAnimationBase
SizeAnimationBase
StringAnimationBase
Vector3DAnimationBase
VectorAnimationBase
In unserem Beispiel wird DoubleAnimation verwendet (basiert auf DoubleAnimationBase). Jede dieser Animationen beschränkt sich (wie zu sehen ist) auf einen bestimmten Typ, welcher verändert werden kann.
Hier ein Beispiel zur Verdeutlichung:
DoubleAnimation doubleAnimHeight = new DoubleAnimation();
doubleAnimHeight.From = 10;
doubleAnimHeight.To = 100;
doubleAnimHeight.Duration = new Duration(
TimeSpan.FromSeconds(0.5));
Diese defnierte DoubleAnimation bewirkt, dass innerhalb einer bestimmten Duration (also Zeitpannse) der Wert von 10 auf 100 erhöht wird. Andere Animationen funktionieren auf dieselbe Art und Weise.
Im zugrunde liegenden Beispiel soll nun bei einem Klick auf ein Foto, dieses in den Contentbereich verschoben und vergrößert werden. Bei nochmaligem Klick, soll sich das Foto an die ursprüngliche Position zurück animieren. Dafür benötigen wir wir insgesamt vier DoubleAnimations, jeweils für die Höhe, Breite, Top-Position und Left-Position:
DoubleAnimation doubleAnimWidth = new DoubleAnimation();
doubleAnimWidth.From = picControl.Width;
doubleAnimWidth.To = size.Width;
doubleAnimWidth.Duration = new Duration(
TimeSpan.FromSeconds(0.5));
DoubleAnimation doubleAnimHeight = new DoubleAnimation();
doubleAnimHeight.From = picControl.Height;
doubleAnimHeight.To = size.Height;
doubleAnimHeight.Duration = new Duration(
TimeSpan.FromSeconds(0.5));
DoubleAnimation doubleAnimTop = new DoubleAnimation();
doubleAnimTop.From = Canvas.GetTop(picControl);
doubleAnimTop.To = location.Y;
doubleAnimTop.Duration = new Duration(
TimeSpan.FromSeconds(0.5));
DoubleAnimation doubleAnimLeft = new DoubleAnimation();
doubleAnimLeft.From = Canvas.GetLeft(picControl);
doubleAnimLeft.To = location.X;
doubleAnimLeft.Duration = new Duration(
TimeSpan.FromSeconds(0.5));
Wie zu sehen ist, werden die Eigenschaften From und To nicht fix zugewiesen, sondern stammen von einer zuvor definierten Logik:
PictureControl picControl = (PictureControl)sender;
Point location = new Point(
Canvas.GetLeft(picControl),
Canvas.GetTop(picControl));
Size size = new Size(
picControl.ActualWidth,
picControl.ActualHeight);
if (location != _targetLocation)
{
location = _targetLocation;
size = _targetSize;
}
else
{
location = picControl.OriginLocation;
size = picControl.OriginSize;
}
Zu beachten ist, dass sich dieser Code im MouseDown-Eventhandler befindet. Bei der Instanzierung der Picture-Steuerelemente wird die ursprüngliche Position und Größe gesetzt. Mit lokalen Variablen kann nun die aktuelle Position abgefragt werden, entspricht diese der Position des Contentbereichs, muss das Foto wieder an den ursprünglichen Platz zurück animiert werden, andernfalls ist der Contentbereich zu füllen.
Im abschließenden Schritt muss noch das Storyboard mit den Animationen und den notwendigen Zuweisungen gefüttert werden:
board.Children.Clear();
board.Children.Add(doubleAnimWidth);
board.Children.Add(doubleAnimHeight);
board.Children.Add(doubleAnimTop);
board.Children.Add(doubleAnimLeft);
Storyboard.SetTargetName(
doubleAnimWidth,
picControl.Name);
Storyboard.SetTargetName(
doubleAnimHeight,
picControl.Name);
Storyboard.SetTargetName(
doubleAnimLeft,
picControl.Name);
Storyboard.SetTargetName(
doubleAnimTop,
picControl.Name);
Storyboard.SetTargetProperty(
doubleAnimWidth,
new PropertyPath(PictureControl.WidthProperty));
Storyboard.SetTargetProperty(
doubleAnimHeight,
new PropertyPath(PictureControl.HeightProperty));
Storyboard.SetTargetProperty(
doubleAnimLeft,
new PropertyPath(Canvas.LeftProperty));
Storyboard.SetTargetProperty(
doubleAnimTop,
new PropertyPath(Canvas.TopProperty));
board.Begin(this);
Zuerst werden alle Kindelemente unseres Storyboard namens board mittels Clear gelöscht und alle zuvor definierten Animationen hinzugefügt. Danach muss das Zielelement definiert werden. Dieses wird beim MouseDown-Event durch das Objekt sender präsentiert, worüber wir den Namen des Objektes abfragen und dem Storyboard mittels SetTargetName übermitteln. Hier muss auch die entsprechende Animation als Parameter übergeben werden, damit das Storyboard weiß, welche Animation auf welches Element angewandt werden muss.
Ist dies erledigt, muss dem Storyboard noch mitgeteilt werden, welche Properties durch die Animationen betroffen sind. Hier gilt es nun, ebenfalls pro Animation den PropertyPath des Zielelementes zu definieren.
Schlussendlich wird das Storyboard mittels Begin gestartet.
Fazit
Dies war eine kurze Einführung in die Welt der Animationen unter WPF. Natürlich sind weitaus komplexere Animation möglich. Auf dieser Basis ist es aber möglich, sich weiter in dieses Thema zu vertiefen und verbesserte Lösungen zu schaffen.
Eingaben sollten immer validiert, also auf Korrektheit überprüft, werden, um Fehler bereits frühzeitig abzufangen und so eventuell einen langen Verarbeitungsweg einzusparen. Dieser Artikel zeigt, wie die Eingabe von Datumswerten in Textfelder unter WPF auf Gültigkeit hin überprüft werden können.
Einführung
In der Windows Presentation Foundation gibt es die sogenannten ValidationRules, welche an einem Databinding hängen und darüber auch verwendet werden können. Mit Hilfe dieser ValidationRules können nun Eingaben auf einfache Art und Weise überprüft werden. Es wird jedoch vorausgesetzt, dass der zu prüfende Wert per Databinding an das jeweilige Control gebunden wurde.
Erstellung einer ValidationRule
Das Erstellen einer ValidationRule ist sehr einfach gehalten. Dazu muss von der abstrakten Klasse System.Windows.Controls.ValidationRule abgeleitet werden. Weiters muss die abstrakte Methode Validate überschrieben werden. Diese bekommt als Parameter den geänderten Wert der Eingabe übermittelt, welcher in weiterer Folge überprüft werden muss. Als Ergebnis wird ein Objekt des Typs ValidationResult zurück geliefert. Darin wird vermerkt, ob die Validierung erfolgreich war (oder nicht) und ein Fehlerobjekt (das kann beispielsweise eine Fehlermeldung sein).
Für das hier gezeigte Beispiel wird eine ValidationRule für die Validierung eines Datums implementiert und kann wie folgt aussehen:
publicclass DateTimeValidationRule : ValidationRule
{
publicoverride ValidationResult Validate(
objectvalue,
System.Globalization.CultureInfo cultureInfo)
{
string val = valueasstring;
DateTime dtResult;
bool bRet = DateTime.TryParse(val, out dtResult);
ValidationResult vr = new ValidationResult(bRet, "No valid date");
return vr;
}
}
Damit ist die notwendige ValidationRule erstellt. Da diese jedoch nur auf ein Binding angewendet werden kann, wird noch eine Datenklasse benötigt, welche den entsprechenden Wert an unser Textfeld bindet.
Binding erstellen
Für das Binding wird eine einfache Datenklasse erstellt. Diese enthält nur den angezeigten Datumswert und wird bei der Instanzierung mit dem aktuellen Datum befüllt (siehe Konstruktor).
publicclass MyData
{
privatestring _date;
publicstring Date
{
get { return _date; }
set { _date = value; }
}
public MyData()
{
_date = DateTime.Now.ToShortDateString();
}
}
Der nächste Schritt besteht nun darin, ein Window mit den entsprechenden Feldern und dem Binding zu erstellen.
WPF Window und Bindings erstellen
Für das Fenster der Testanwendung müssen einige kleine Punkte erledigt werden. So ist die Datenquelle anzubinden, eine TextBox zu erstellen und das DataBinding zu setzen. Weiters muss dem DataBinding die ValidationRule bekannt gemacht werden, sowie ein entsprechender Trigger gesetzt werden (PropertyChanged). Letztere Schritt wird benötigt, um Änderungen an der Eingabe zu erkennen und dadurch die Validierung zu starten.
In den Ressourcen für das Window wird im ersten Schritt die Datenquelle für das Binding angegeben. my ist lediglich ein Namespace, welcher auf das aktuelle Projekt verweist, damit die Klasse MyData gefunden werden kann.
Interessant ist der definierte Style, welcher an den Typ TextBox gebunden wird und einen Trigger auf die Eigenschaft Validation.HasError besitzt. Dieser wird also immer dann ausgelöst, wenn die Validierung einen Fehler zurück liefert. Ist dies der Fall, wird die Schriftfarbe gesetzt, als auch der ToolTip befüllt (dieser ist zuständig, um die zurück gelieferte Fehlermeldung anzuzeigen).
Schließlich fehlt noch die Definition des Textfeldes, welches der grundlegende Bestandteil dieser Demo ist.
Es wird deutlich, dass hier sowohl ein ErrorTemplate zugewiesen wird und ein entsprechender Style. Zudem findet ein Binding auf unsere zuvor definierte Datenquelle statt. Dieser wird ein UpdateSourceTrigger zugewiesen, der bei einer Veränderung des eingegebenen Wertes die ValidationRule startet. In diesem Fall DateTimeValidationRule, welche zu Beginn dieses Artikels erstellt wurde.
Der Rest besteht eigentlich nur darin, um die TextBox herum die grundlegenden Bestandteile eines Fensters zu definieren. In diesem Beispiel wurde mit einem Grid gearbeitet, was durchaus auch an der Definition des Textfeldes zu erkennen ist.
Fazit
Mit diesem Beispiel sollte es nun möglich sein, eigene Validierungen für Felder zu implementieren. Es ist durchaus sinnvoll, ein wenig mit dem oben gezeigten Code zu experimentieren und einige Einstellungen etc. zu ändern, um daraus zu lernen.