Mittwoch, 8. Oktober 2008
PDC08: new XAML features
Sie findet hauptsächlich Anwendung in WPF, aber auch in WF und WCF
Auf der PDC wird sich eine Session rund um XAML drehen. Es wird gezeigt wie eine Anwendung komplett deklarativ in XAML geschrieben werden kann.
XAML wird außerdem mit XOML aus der Workflow Foundation vereinigt. Man darf gespannt sein auf welche Anwendungsbereiche XAML ausgeweitet werden wird.
Neue XAML Features werden sein:
- support für generics
- object references
- non default constructors
cu @ PDC
Benjamin
C# 4.0: dynamic

Mittwoch, 10. September 2008
Olso und D#
Don Box ist einer der Architekten von Oslo und gibt in seinem Blog kleine versteckte Hinweise. Heute wies er auf einen interessanten Artikel hin, der weitere Geheimnisse zu Olso enthüllt.
Nach LINQ wird dieses Jahr ein weiterer Hammer vorgestellt werden:
Textuelle Domain Specific Languages (DSL)
Microsoft hat bereits ein DSL Toolkit mit dem man graphische DSL erstellen kann. Diese können dann über einen Designer in VisualStudio verwendet werden. Das Neuartige and denOslo DSL ist, dass sie wie eine eigene Programmiersprache funktionieren; also textuell. Diese TDSL basieren auf dem Microsoft Research Projekt TLA+ (T-emporalen L-ogik der A-ktionen)
Übrigens der Codename für diese TDSL ist "D".
Vielleicht bekommen wir nach C# nun ein D# !?
C.U. at PDC
Benjamin
Donnerstag, 29. Mai 2008
PDC2008 I'm comming
Jetzt bin ich LIVE dabei!
Themen werden sein:
- The Live Developer platform, including the MESH.
- Cloud Computing" from computation, to storage and much more!
- Visual Studio 10" and .NET Framework 4.0
- ASP.NET Futures including v.next of MVC, Ajax and Dynamic data
- Silverlight Futures, including Silverlight for business applications
- And much, much more.
Donnerstag, 8. Mai 2008
StackPanel Items in umgekehrter Reihenfolge anzeigen
Das StackPanel in WPF kann Controls untereinander anordnen.
Mit der Eigenschaft Orientation=Horizontal ist es möglich auch die Elemente von Links nach Rechts anzuordnen.
Es gibt aber keine Eigenschaft StackItemsFrom="Top|Bottom".
Natürlich kann man ein eigenes StackPanel implementieren, das dies kann. Es geht aber auch einfacher.
Am Anfang war das ItemsControl
Wir benötigen zunächst ein ItemsControl. Ein ItemsControl kann beliebige Objekte, also auch Double, Boolean, Person etc., aufnehmen. Ein Panel hingegen nur Controls.
Das ItemControl benötigt jedoch ein Panel um die Objekte darstellen zu können. Standardmäßig ist dies ein StackPanel.
Über die Eigenschaft ItemsPanel können wir ein ItemsPanelTemplate definieren, und ein anderes von Panel abgeleiteten "Panel" angeben, in das nun die Items angeordnet werden.
Wie die Objekte aussehen bestimmt ein DataTemplate. Für jedes Objekt generiert das ItemsControl einen Container (ContentPresenter, für ListBox -> ListBoxItem). Da man auf diesen Container nicht direkt zugreifen kann, definiert das ItemsControl eine Property um den Style für den "ItemContainer" festlegen zu können.
Verkehrte Welt
Um nun die Elemente von unten nach oben statt von oben nach unten anordnen zu können, wie es das StackPanel macht, benötigen wir ein anderes Panel. Ein DockPanel ist geeignet, da es mehr als eine Möglichkeit gibt, Elemente in ihm anzuordnen. (Top,Left,Right,Bottom).
Nun müssen wir noch im ItemContainerStyle festlegen, dass die Elemente jeweils von Unten angeordnet werden sollen.
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<DockPanel VerticalAlignment="Top" LastChildFill="False" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="DockPanel.Dock" Value="Bottom"/>
</Style>
</ItemsControl.ItemContainerStyle>
<Button>1</Button>
<Button>2</Button>
<Button>3</Button>
</ItemsControl>
Et Voila
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
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.
