Donnerstag, 8. Mai 2008

AttachedProperties richtig initialisieren

Manchmal ist es angebracht, nachdem sich ein Objekt bei einem AttachedProperty registriert hat, einige Initialisierungen durchzuführen. Hierbei kann in der Set Methode, nach dem Setzen des AttachedProperty, z.B. eine Initialisierungsmethode aufgerufen werden.

public
static
void SetAttachedProperty(DependencyObject obj, int value)

{

obj.SetValue(AttachedProperty, value);

InitializeProperty();

}

Die Set-Methode macht nichts Weiteres, als bei dem übergebenen DependencyObjekt SetValue aufzurufen. Der eigentliche Wert der AttachedProperty wird also wie eine ganz normale DependencyProperty gesetzt. Stellen Sie sich einfach vor, eine DependencyProperty ist nichts anderes als ein Schlüssel der zusammen mit einem Wert in einem Dictionary gespeichert wird. Jedes DependencyObject hat so ein Dictionary. Die Werte können aber auch von anderen Objekten abgefragt und gesetzt werden.

Um eine AttachedProperty zu setzen, reicht es tatsächlich lediglich die DependencyProperty am eigenen Objekt zu setzen und nicht am Containerobjekt durch Set[Property]! Das bedeutet aber auch, dass die Initialisierungsmethode nicht mehr aufgerufen wird, da die AttachedProperty über einen anderen Weg gesetzt wird. Nun kann man es natürlich zur Konvention machen in dem in einem API vorschreibt, dass AttachedProperties immer über Set[Property] gesetzt werden müssen. So ist der XAMLReader drauf angewiesen, dass wenn er eine AttachedProperty im Markup erkennt, auch eine entsprechende Set-Methode verfügbar ist, die er aufrufen kann.

Trotzdem ist es besser eine Initialisierung anders durchzuführen. Wie? Über einen Callbackmechanismus. Jede DependencyProperty verfügt über Metadaten die es erlauben, Code bzw. Callbacks für Validierungen, Änderungen etc. zu hinterlegen. Ändert sich z.B. der Wert einer DependencyProperty, so wird der Callback aufgerufen und die Initialisierung kann durchgeführt werden. Dieser Mechanismus funktioniert unabhängig davon wie eine DependencyProperty, gesetzt wurde.

public
static
readonly
DependencyProperty PropertyWithCallback;


 

//Definition der Metadaten mit Callback

PropertyMetadata metadata = new
PropertyMetadata();

metadata.PropertyChangedCallback += OnPropertyWithCallbackChanged;


 

//Registrierung der DependencyProperty

PropertyWithCallback = DependencyProperty.RegisterAttached("PropertyWithCallback",


typeof(int), typeof(Window1), metadata);


 

//Der Callback ruft die eigentliche Initializierungsmethode auf

private
static
void OnPropertyWithCallbackChanged(DependencyObject obj, DependencyPropertyChangedEventArgs value)

{

InitializeProperty();

}

Mittwoch, 7. Mai 2008

Eigene Linien in Grid Panel zeichnen

Das Grid ist das Mächtigste und am Häufigsten gebrauchte Panel in WPF.

Mit der Eigenschaft "ShowGridLines" lassen sich Hilfslinien anzeigen.

<Grid ShowGridLines="True">





Diese Linien dienen nur als Hilfestellung für den Entwickler, der so die Position der Spalten und Reihen erkennen kann.

Leider kann man nicht selber bestimmen wie diese Linien aussehen. Oder etwas doch?

Was passiert eigentlich im Grid wenn ShowGridLines=True gesetzt wird?

WPF-Controls können sich selbst zeichnen, in dem sie die OnRender Methode überschreiben. Wie dies auch bei OnPaint in WinForms der Fall ist.

Doch beim Grid wird davon nicht Gebrauch gemacht um die Linien zu zeichnen! Stattdessen wird zu den logischen UI-Elementen in dem Panel ein zusätzliches visuelles Element der "GridLinesRenderer" hinzugefügt. Dies geschieht nicht per ControlTemplate. (Templates ändern ebenfalls das Aussehen also den VisualTree), sonder programmatisch durch den Aufruf von AddVisualChild(visual) in der Basisklasse.

Der GridLinesRenderer ist von DrawingVisual abgeleitet. UIElement und DrawingVisual haben also den Selben Vorfahr "Visual". DrawingVisuals sind jedoch wesentlichen performanter als UIElements, da sie direkt auf den "Bildschrim" (WPF-Subsystem) zeichnen.

Der VisualTree im XamlPad zeigt, dass neben dem logischen Child – einem Button - noch ein visuelles Element - der GridLinesRenderer - in der Hierarchie zu finden ist.






 

Möglichkeit 1: Einen eigenen GridLinesRenderer schreiben

Meine erste Idee war es einen eigenen GridLinesRenderer zu schreiben. Ganz nach dem Vorbild des in dem Grid zu findenden Renderer.

Das Problem ist, dass es nicht möglich ist den Renderer in den VisuallTree des Grid einzufügen. Die Methode die dieses möglich machen würde – AddVisualChild – ist nämlich protected.

Ein eigenes Grid zu erstellen, nur um die GridLinien zeichnen zu können war mir dann aber zu aufwändig.

Es muss einen anderen Weg geben.

Möglichkeit 2: Einen GridLinesAdorner schreiben

Wenn es keine Möglichkeit gibt ein UIElement von Innen zu verändern ohne ein eigenes Control abzuleiten, so muss man es eben von Außen probieren. Das machen sog. Adorner.

Adorner "verzieren" ein UIElement. Wie, das bestimmt der Adorner. Wichtig ist, dass ein Adorner immer über einem UIElement gezeichnet wird. D.h. der Adorner verdeckt darunter liegende Zeichnungen.

Public
Class GridLinesAdorner


Inherits Adorner


 


Private Grid As System.Windows.Controls.Grid


 


'Übergabe des zu verzierenden Elements (hier Grid)


Sub
New(ByVal grid As System.Windows.Controls.Grid)


MyBase.New(grid)


Me.Grid = grid


End
Sub


 


Private _pen As Pen


 


'Stift um die Linien zeichnen zu können


Public
Property Pen() As Pen


Get


Return _pen


End
Get


Set(ByVal value As Pen)

_pen = value


End
Set


End
Property


 


 


'Zeichnen der Linien


Protected
Overrides
Sub OnRender(ByVal dc As System.Windows.Media.DrawingContext)


If Pen Is
Nothing
Then
Exit
Sub


 


If Grid.ColumnDefinitions.Count > 0 Then


Dim x As
Double


For i As
Integer = 1 To Grid.ColumnDefinitions.Count - 1

x = Grid.ColumnDefinitions(i).Offset

dc.DrawLine(Pen, New Point(x, 0), New Point(x, Grid.ActualHeight))


Next


End
If


 


If grid.RowDefinitions.Count > 0 Then


Dim y As
Double


For i As
Integer = 1 To Grid.RowDefinitions.Count - 1

y = Grid.RowDefinitions(i).Offset

dc.DrawLine(Pen, New Point(0, y), New Point(Grid.ActualWidth, y))


Next


End
If


 


End
Sub


 

End
Class


 

Die Methode OnRender führt nun das Zeichnen der Gridlinien durch. Über die Row- und ColumnDefinitions des Grid, kann der Offset der einzelnen Spalten vom Grid abgerufen werden. Diese dienen uns als Koordination um an diesen Stellen die entsprechenden Linien zu zeichnen.

Anwenden des Adorners

Um den Adorner zum Einsatz zu bringen, müssen wir nun noch die Code-Behind Datei anpassen. Leider ist es nicht möglich den Adorner in XAML direkt anzuwenden. (Die Klasse AdornerDecorator ist nur dafür da einen Layer für ein zu verzierendes Element bereitzustellen, worauf ein Adorner angewendet werden kann; aber nicht für den Adorner selbst.)

Class Window1


 


Private
Sub Window1_Loaded(ByVal sender As
Object, ByVal e As System.Windows.RoutedEventArgs) Handles
Me.Loaded


'Layer für das Element holen in dem der Adorner gezeichnet wird


Dim layer As AdornerLayer = AdornerLayer.GetAdornerLayer(Me.Grid)


'Adorner mit Stift erstellen


Dim adorner As
New GridLinesAdorner(Grid)

adorner.Pen = New Pen(Brushes.Black, 2)


'Adorner dem Layer hinzufügen

layer.Add(adorner)


End
Sub


 

End
Class


 

Das Ergebnis


 




Wer noch einen Rahmen um das Grid herum haben möchte, der brauch nur noch das Grid in ein Border zu packen.

Achtung: Wie man sieht wird der Adorner über das Grid gezeichnet. Daher sind die Ränder der im Grid befindlichen Buttons nicht ganz zu sehen.

Hier muss dann noch ein entsprechendes Margin auf die Buttons angewendet werden um den richtigen Abstand zu den Gridinnenseiten zu ermöglichen.


 


 


 


 

Dienstag, 29. April 2008

WPF goes Transitions

Effekte a la PowerPoint? Mit WPF kein Problem!?

WPF bietet zwar Animationsunterstützungen, doch diese müssen mühsam programmiert werden.

Nun gibt es ein Framework das Übergangseffekte für WPF bereitsstellt.
z.B. Wipe, Fade, oder RotatingCube. Auch aufwendige 3D Animation sind frei verfügbar.

Entwickelt hat dies Jaraed Bienz.
Download auf CodePlex: Transitionals

Wer jetzt vielleicht sagt: "He, das habe ich schonmal gesehen!"
der hat vollkommen recht. Auf der letzten Basta habe ich einen Vortrag über CAB gehalten und auch Acropolis vorgestellt. Für mich hatte Acropolis keine Zukunfstaussichten (Wurde ja auch eingestampft). Jedoch einige Bestandteile hatten Potential in WPF intigriert zu werden. U.a. auch das Transitionframework.

Zwar ist das Transitionframework (noch) kein Teil von WPF. Aber dennoch sehr nützlich, zumal frei zugänglich und erweiterbar.

Montag, 10. Dezember 2007

Basta! 2008 Spring Edition

Schon zum 2. Mal werde ich auf der BASTA! sprechen.

Meine Session(s)

Reflection in Action


Reflection gehört zu den coolesten und nützlichsten Features von .NET. In dieser Session schauen wir uns die bekannten und unbekannten Möglichkeiten von Reflection an. U.a. Wie performant ist Reflection und wie kann man die Performance optimieren? Wie generiert man dynamisch IL Code und kann ihn wieder auslesen!? Und vieles vieles mehr.

Allein dafür lohnt es sich auf die BASTA! zukommen.

Benny





Freitag, 7. Dezember 2007

PDC2008 announced!

Nachdem die PDC2007 ausgefallen ist, weil "zu wenig Neues" und die TechEd2007 Europe tatsächlich wenig Neues zu bieten hatte, findet sie nun statt:

die PDC2008 in Los Angeles!


Link

Vermutliche Themen:
- .NET Framework 4.0
- Volta (MSIL Recompiler)
- Software + Services
- WPF Composite Smart Clients
- (hoffentlich neue (WPF Controls bzw. Features))
- Silverlight 2.0 und DLR

Urlaub ist schon geplant!

Benny

Dienstag, 30. Oktober 2007

WPF Composite Clients formally known as Acropolis

Das war es also mit "Acropolis". Dem Produkt das eigentlich CAB (Composite UI Application Block) ersetzen sollte...

Hier die offizielle Bekanntmachung:
Acropolis Team Blog

Die Erkenntnisse aus dem Acropolis Projekt werden zerstückelt und fließen in die .NET Klassenbibliothek ein (Version 4.0?). Dies Vermutung habe ich bereits auf der BASTA! 2007 in Mainz geäußert, wo ich einen Vortrag über CAB gehalten habe und einen Ausblick auf Acropolis gezeigt habe. Ein Artikel für das dotnet Magazin war bereits in Arbeit. Aber eine innere Stimme hat mich zurückgehalten weiter zu machen. Jetzt weiß ich warum!

Von Seiten der CAB-Community hagelte es eine Menge Kritik an Acropolis. Der Ärger bestand darin dass CAB und die Smart Client Software Factory nicht mehr weiterentwickelt werden. Stattdessen wurde das ehrgeizige Projekt Acropolis hochgezogen, dass viel wollte aber nichts konnte. Das größte Problem war eine nicht existierende Roadmap für das Produkt.

Hier ein Kommentar dazu aus der CAB Comunity.

Requiem for Acropolis; Fanfare for CAB +


Aber es gibt schon wieder ein Neues Projekt: WPF Composite Clients

Diesmal wieder unter der Leitung der Patterns & Practices Group. Hier fließt die ganze Erfahrung aus CAB mit ein. Acropolis ist (war) nähmlich ein Projekt der UIFX Teams, das für ASP.NET und WPF zuständig ist.

Glen Block bescheibt in seinem Blog das neue Projekt.
Glenn Block

WPF Composite Client ist keine neue Version von CAB. Es wird eine neue Library und Guidance geben. Zielplattform ist WPF. Der Grund für den Aufbau einer neuen Klassenbibliothek besteht darin, dass CAB nicht für WPF designt wurde. WPF bringt eigene Konzepte wie Commands und RoutedEvents mit sich, die es in WinForms nicht gibt und somit auch nicht in CAB unterstützt werden.

WPF bietet außerdem eine komplette andere "experience". Es wird daher keinen Migrationspfad von WinForms geben. (So wie dies noch in Acropolis vorgesehen war).

Wie herausklang wird es weiter Verbesserungen an WinForms geben! WinForms geht also noch nicht in Rente.

Ich lass erstmal die Korken knallen, dass wir das Thema Acropolis hinter uns lassen können! Ich habe schon befürchtet, dass wir uns ernsthaft mit dem Produkt auseinandersetzen müssen!

Mittwoch, 10. Oktober 2007

CSV Dateien verarbeiten mit LINQ

Mein erster (langer) Artikel in der dotnetpro (http://www.dotnetpro.de/) ist erschienen!

Ich zeige, dass man mit LINQ nicht nur Datenbanken ansprechen kann, sondern ganz simple auch CSV Dateien einlesen, filtern und ausgeben kann. Mit LINQ und C# 3.0 ist das eine Sache von 3 Zeilen Code.

Auf die Idee bin ich durch den Contest der dotnetpro gekommen. Die Aufgabenstellung war CSV Daten zu verarbeiten. Es war abzusehen, dass die jeder mit einer Datenbank angehen würde. Der eine verwendet SQLServer, der andere MySQL. Besonders beliebt bei dem Contest war aber SQLLight.

Und der der Gewinner ist....

Gewonnen habe ich dann, mit meiner Lösung nicht. Und das obwohl ich 25% schneller war als die Siegerlösung!!!
(Ja Mama! Es waren wieder einmal die Flüchtigkeitsfehler)