WebCam Bild schiessen und in Isolated Storage speichern

Man muss das Rad nicht immer neu erfinden. Im heutigen Beispiel kommt deshalb die Library Imagetools zum Einsatz. Diese verwendet wiederum eine Zip Bibliothek und die wiederum … Und am Ende wird meine Silverlight Anwendung davon profitieren. Der “Use Case” ist eine Webcam im Browser. Der Benutzer kann ein Foto Schießen und dieses dauerhaft speichern. Den Part mit der Webcam halte ich hier sehr kurz. Zur Darstellung wird ein einfaches Rechteck (Rectangle1) verwendet. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) CaptureDeviceConfiguration.GetAvailableVideoCaptureDevices(0) Dim wb As New VideoBrush wb.SetSource(cs) Rectangle1.Fill = wb If CaptureDeviceConfiguration.AllowedDeviceAccess Or CaptureDeviceConfiguration.RequestDeviceAccess Then cs.Start() End If End Sub Wenn der Benutzer auf den Button drückt läuft das aktuelle Bild als Video. Ein zweiter Button dient dazu den Snapshot zu erstellen und das Bild zu speichern. Dazu müssen die DLL’s ImageTools, ImageTools.IO.Png und ImageTools.Utils als Referenz eingebunden werden. Frage Nummer eins ist, wie kommt man ans Bild? Und in welchem Format? Wer in den Dokus nachschlägt wird immer das Event CaptureImageAsync finden. Allerdings kann man auch direkt die Writeablebitmap Klasse verwenden um einen Screenshot eines beliebigen UIElements zu erstellen. Auch bei CaptureImageAsync erhält man ein Obekt vom Typ WriteableBitmap. Braucht nur ein bisschen mehr Code. Ein weiterer Unterschied ist, das Writeablebitmap erst nach vollständigen Rendering den “Screenshot” erstellt. Mit CaptureImageasync  bekommt man den Screenshot schneller aber ohne Effekte ala Pixelshader. Für das Capturen von laufenden Videos braucht es aber noch ein wenig mehr. Dazu dient dann Videosink. Mehr dazu im Blog von Rene Schulte. Im Silverlight Isolated Storage kann dann das Bild serialisiert gespeichert werden. Dazu wird aus dem WriteableBitmap zunächst ein Bild vom Typ PNG erzeugt. Ein netter Trick ist die Extension Methode ToImage, die der WriteableBitmap Klasse zugewiesen wird. (enthalten in Utils). Der PngEncoder erzeugt das korrekte PNG Format. Es liegen auch noch Encoder für BMP, GIF und JPG bei. Private Sub speichern_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles speichern.Click Dim wb As New WriteableBitmap(Rectangle1, Nothing) Image1.Source = wb Dim iso As IsolatedStorageFile = IsolatedStorageFile.GetUserStoreForApplication Using sr As IsolatedStorageFileStream = iso.OpenFile("bild" + Date.Now.ToString("YYYMMDDHHmmss") + ".png", FileMode.OpenOrCreate) Using bw As BinaryWriter = New BinaryWriter(sr) Dim enc As PngEncoder = New PngEncoder() Dim bytesImage As Byte() Using ms As MemoryStream = New MemoryStream() Dim itImage = wb.ToImage() enc.Encode(itImage, ms) bytesImage = ms.ToArray() bw.Write(bytesImage) End Using End Using End Using End Sub Ziemlich cool ist, das die Bilder dann auch wirklich physikalisch auf der Festplatte rum liegen.

Bild aus Explorer in die Silverlight Anwendung ziehen

“Nur kein Code ist guter Code”. Ein Zitat aus meinem Munde [hannesp]. Entsprechend versuche ich immer mit minimalen Code mein Ziel zu erreichen. Vielleicht auch ein Grund warum ich mit Layern und MVxx recht sparsam umgehe. Deshalb bin ich auch ganz stolz auf mein Silverlight Bilder Drag & Drop Beispiel. Fünf Zeilen Code. Weniger geht kaum. Drag& Drop ist eine Funktion in Silverlight 4 die sowohl für OOB als auch in Browser Anwendungen funktioniert. Der Benutzer kann eine oder mehrere Dateien auf ein UIElement ziehen und dort fallen lassen. Dafür muss im UIElement das Attribut Allowdrop auf true gesetzt werden. Dann werden vier mögliche Events gefeuert. DragEnter DragOver DragLeaver Drop Wirklich wichtig ist nur das letzte Ereignis Drop. Die anderen Events könnten verwendet werden um den Benutzer durch z.B. Farbänderungen sichtbar zu machen wohin er gerade dropped. In Meinem Beispiel möchte ich auf ein Image Element ein Bild ziehen können. Da das Image ohne Bild nicht sichtbar ist, reagiert es auch auf keine Events und ist nicht abschätzbar wo die Grenzen sind. Ähnliches passiert meinen Silverlight Kurs Teilnehmer recht häufig mit einem Rechteck. Das ist auch nur clickbar wenn es mit einer Brush gefüllt ist. Um also den Rahmen zu zeichnen und das Drop Event zu bekommen, packe ich einfach das Image in ein Border der gefüllt ist. <Border HorizontalAlignment="Left" Height="211" Margin="75,0,0,0" Drop="Image1_Drop" AllowDrop="True" VerticalAlignment="Top" Width="202"> <Border.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="Black" Offset="0"/> <GradientStop Color="White" Offset="1"/> </LinearGradientBrush> </Border.Background> <Image MouseLeftButtonDown="Image1_MouseLeftButtonDown" x:Name="Image1" Stretch="Fill" /> </Border> Im Drop  Event bekommt man per e.Data die Liste der gedroppten Dateien. In meinem Fall nehme ich einfach die erste Datei im Index und gehe davon aus das es ein JPG oder PNG ist. Dann wird die Datei per Filestream geöffnet und einem BitmapImage zugewiesen. Dieses wiederum ist die perfekte Quelle für das Image Element. Private Sub Image1_Drop(ByVal sender As System.Object, ByVal e As System.Windows.DragEventArgs) Dim fi As FileInfo = e.Data.GetData(DataFormats.FileDrop)(0) Using fs As FileStream = fi.OpenRead Dim bi As New BitmapImage bi.SetSource(fs) Image1.Source = bi End Using End Sub   wie gesagt alles Beispiele aus meiner Silverlight Schulung. Der nächste Termin ist in Leipzig.

Silverlight Zwischenablage

Ein weiteres (sehr kleines) Beispiel aus meiner Silverlight Trickkiste. Irgendwann muss ich das mal strukturieren und ein Silverlight Tutorial draus machen. Da irgendwann in weiter Zukunft liegt zunächst mal was kleines. Die Zwischenablage des Betriebssystems kann in Silverlight 4 gelesen und geschrieben werden. Leider kann der Benutzer aber nur reine Texte kopieren. Es gibt drei Methoden auf dem statischen Clipboard Objekt. ContainsText GetText SetText Name ist Programm. Da der User den Zugriff auf die Zwischenablage (ähnlich der Webcam) explizit erlauben muss, gibt es eigentlich drei Fälle. Folgender Silverlight Code mit VB.NET zeigt die Szenarien. Try     If Clipboard.ContainsText() Then        TextBox1.Text = Clipboard.GetText()     Else        MessageBox.Show("Zwischenablage leer")     End If Catch ex As SecurityException     MessageBox.Show("Bitte Zugriff auf Zwischenablage erlauben") End Try Das funktioniert OOB ( out of browser) und auch für Browser Anwendungen.

Dynamische Silverlight UI mit C(l)ick

Manchmal kommen mir so Ideen und ich probiere rum bis es geht ohne eine Idee zu haben wofür eigentlich. Meine heutige Abendbeschäftigung sind entschärfte Commands um dynamisch ein Formular zu erzeugen. Meist sieht man den Einsatz von Commands im Zusammenhang mit MVVM. Ich will es aber einfacher. Zunächst die Ausgangsituation. Silverlight 4 besitzt ein Command Attribut mit denen man Commands deklarativ zuweisen kann. Weiters kann man mit dem XAMLReader ein UI Element aus einem String erzeugen lassen. Wenn man Events per Attribut zuweisen möchte meckert der XAML Parser. Also wie sieht normalerweise ein Button Event aus <Button Content="fester Button" Height="37" Click="Button1_Click" HorizontalAlignment="Left" Margin="42,35,0,0" Name="Button1" VerticalAlignment="Top" Width="102" /> Im VB.NET Code dann im Ansatz so Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Alternativ kann man auch im Command Attribut ein Command angeben. Dazu benötigt man aber eine Klasse die das Interface ICommand implementiert. In der minimal Ausstattung wie folgt mit einer simplen MessageBox. Public Class HannesCommand1 Implements ICommand Public Sub HannesCommand1() End Sub Public Function CanExecute(ByVal parameter As Object) As Boolean Implements System.Windows.Input.ICommand.CanExecute Return True End Function Public Event CanExecuteChanged(ByVal sender As Object, ByVal e As System.EventArgs) Implements System.Windows.Input.ICommand.CanExecuteChanged Public Sub Execute(ByVal parameter As Object) Implements System.Windows.Input.ICommand.Execute MessageBox.Show("hannescommand1") End Sub End Class Das Command kann aber nicht direkt verwendet werden. Der Umweg muss über die Ressourcen. In jedem Fall benötig man den Namensraum auf das aktuelle Silverlight Projekt das den Namen KoelnSL heißt. Der Namensraum wird willkürlich local genannt. xmlns:local="clr-namespace:KoelnSL" Das Command wird dann als Unterelement deklariert über den Namesraum local und den Klassennamen. <Button Content="Button" Height="38" HorizontalAlignment="Left" Margin="121,227,0,0" Name="Button1" VerticalAlignment="Top" Width="105" > <Button.Command> <local:HannesCommand1></local:HannesCommand1> </Button.Command> </Button> Alternativ besteht auch die Möglichkeit über eine Statische Ressource zu gehen. Die Instanz wird per x:Key willkürlich hCommand benannt. <UserControl.Resources> <local:HannesCommand1 x:Key="hCommand"></local:HannesCommand1> </UserControl.Resources> Im XAML des Button Elements kann man dann mit kurzer Syntax direkt per Command Attribut die Bindung deklarieren auf hCommand. <Button Content="Button" Height="38" Command="{StaticResource hCommand}" HorizontalAlignment="Left" Margin="121,227,0,0" Name="Button1" VerticalAlignment="Top" Width="105" > Soweit die Theorie zum Teil Commands. Als nächstes gehts um das dynamsiche laden von XAML. Die passende Klasse ist XAMLReader zu finden im Namenraum System.Windows.Markup. Damit kann aus einem String ein UIElement erstellt werden, das man an den XAML Tree anhängen kann. Der Parser benötigt allerdings den XAML presentation Namensraum im String. Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Dim s As String = "<Button xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" " + " Content=""dynamischer Button"" Command=""{StaticResource hCommand}"" Height=""37"" HorizontalAlignment=""Left"" " + " Margin=""42,107,0,0"" Name=""Button2"" VerticalAlignment=""Top"" Width=""189"" >" + "</Button>" Dim xaml As UIElement = XamlReader.Load(s) LayoutRoot.Children.Add(xaml) End Sub Das Problem war bei mir das der XAML Parser von Silverlight zur Laufzeit einen Fehler wirft, weil er hCommand nicht findet. Es scheint ein Problem in der Hierarchie geben, dem ich später auf den Grund gehen werde. Aber ich weis das wenn eine Resource nicht gefunden wird, der Parser in den Ressourcen von APP.XAML nachschlägt. Deshalb also einfach dort deklariert. <Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d=http://schemas.microsoft.com/expression/blend/2008 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" x:Class="KoelnSL.App" xmlns:local="clr-namespace:KoelnSL"> <Application.Resources> <local:HannesCommand1 x:Key="hCommand1"></local:HannesCommand1> </Application.Resources> </Application> Demnächst auch ein echtes MVVM Beispiel mit Binding.

Silverlight Transformationen Detailinfos

In meiner letzten Silverlight Schulung hat ein Teilnehmer ziemlich pentrant nachgefragt in welcher Reihenfolge Transformationen ausgeführt werden. Ich habe geantwortet gleichzeitig. Damit hatte ich recht und unrecht zugleich. Seit Silverlight 4 erzeugt Expression Blend Transformationen per Attribute in einem CompositeTransform Element <Button Content="Button" RenderTransformOrigin="0.5,0.5" UseLayoutRounding="False" d:LayoutRounding="Auto" Margin="5"> <Button.RenderTransform> <CompositeTransform TranslateY="-13" Rotation="19.549"/> </Button.RenderTransform> </Button> Wenn nun mehrere Buttons in einem Stackpanel platziert werden sieht man in Blend zwei Effekte Die Ursprüngliche Position des Buttons wird hellblau dargestellt. Das liegt daran das das Reendering zunächst durchgeführt wird als ob das UIElement ganz normal vorhanden wäre. Hier eben drei Buttons in einem Stackpanel. Das macht auch sicher Sinn. Wohin sollten die Buttons auch rutschen? Danach wird die Transformation angewandt. In diesem Beispiel eine Drehung und eine Positionsänderung auf der Y Achse. Vor Silverlight 4 hat Expression Blend (als 3 oder kleiner) eine Transformgroup erzeugt. Diese enthielt dann die Transformationen als Unterelemente. Eine Menge XAML Code und vor allem teuflisch da Animationen die Elemtente per Index angesteuert haben. Ein entferntes Transformation Element hat so ziemlich weit reichende Auswirkungen. Entsprechend ist der Lösungsansatz von Silverlight 4 per CompositeTransform auch wesentlich besser. Seltsamerweise enthalten die Silverlight 4 Design Vorlagen auch noch immer den alten Weg per Transformgroup <LinearGradientBrush EndPoint="0,1" MappingMode="Absolute" SpreadMethod="Repeat" StartPoint="20,1"> <LinearGradientBrush.Transform> <TransformGroup> <TranslateTransform X="0"/> <SkewTransform AngleX="-30"/> </TransformGroup> Nun zur zentralen Frage. Spielt die Reihenfolge der Element in der Transformgroup eine Rolle? Die Standardreihenfolge unter Blend war wie folgt. Dies habe ich auch nie in Frage gestellt und verändert. <TransformGroup> <ScaleTransform/> <SkewTransform/> <RotateTransform/> <TranslateTransform/> </TransformGroup>     Wenn man nun die Reihenfolge von TranslateTransform und RotateTransform wie folgt tauscht, ändert sich in der Tat die Oberfläche, was der Beweis dafür ist, das es eine entscheidende Rolle spielt. <TransformGroup> <TranslateTransform X="200"/> <RotateTransform Angle="45"/> </TransformGroup>

Selektierten Eintrag in Silverlight Listbox anders darstellen

Die Silverlight Listbox wird noch ein Thema für ein eigenes Buch. Wer bei Bing nach Beträgen dazu sucht wird vieles dazu finden. U.a. hier und hier und hier. Ein Kunde meiner Silverlight Schulung hat mich gefragt wie man einen Eintrag einer Listbox der ausgewählt (selected) ist, anders darstellt. Nun habe ich mich geistig auf die Suche nach dem passenden Template gemacht. Das ist ist aber falsch gedacht. Silverlight verwendet für die “aktiven” Parts den Visual State Manager der das Verhalten beeinflusst. Um an diesem VSM zu gelangen muss man in das generated Item Container Template der Silverlight Listbox. Dann erhält man umfangreichen XMAL Code. Die wichtigen Stellen habe ich gelb markiert. <UserControl.Resources> <Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem"> <Setter Property="Padding" Value="3"/> <Setter Property="HorizontalContentAlignment" Value="Left"/> <Setter Property="VerticalContentAlignment" Value="Top"/> <Setter Property="Background" Value="Transparent"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="TabNavigation" Value="Local"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="ListBoxItem"> <Grid Background="{TemplateBinding Background}"> <VisualStateManager.VisualStateGroups> <VisualStateGroup x:Name="CommonStates"> <VisualState x:Name="Normal"/> <VisualState x:Name="MouseOver"> <Storyboard> <DoubleAnimation Duration="0" To=".35" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="fillColor"/> </Storyboard> </VisualState> <VisualState x:Name="Disabled"> <Storyboard> <DoubleAnimation Duration="0" To=".55" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="contentPresenter"/> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="SelectionStates"> <VisualState x:Name="Unselected"/> <VisualState x:Name="Selected"> <Storyboard> <DoubleAnimation Duration="0" To=".75" Storyboard.TargetProperty="Opacity" Storyboard.TargetName="fillColor2"/> </Storyboard> </VisualState> </VisualStateGroup> <VisualStateGroup x:Name="FocusStates"> <VisualState x:Name="Focused"> <Storyboard> <ObjectAnimationUsingKeyFrames Duration="0" Storyboard.TargetProperty="Visibility" Storyboard.TargetName="FocusVisualElement"> <DiscreteObjectKeyFrame KeyTime="0"> <DiscreteObjectKeyFrame.Value> <Visibility>Visible</Visibility> </DiscreteObjectKeyFrame.Value> </DiscreteObjectKeyFrame> </ObjectAnimationUsingKeyFrames> </Storyboard> </VisualState> <VisualState x:Name="Unfocused"/> </VisualStateGroup> </VisualStateManager.VisualStateGroups> <Rectangle x:Name="fillColor" Fill="#FFBADDE9" IsHitTestVisible="False" Opacity="0" RadiusY="1" RadiusX="1"/> <Rectangle x:Name="fillColor2" Fill="red" IsHitTestVisible="False" Opacity="0" RadiusY="1" RadiusX="1"/> <ContentPresenter x:Name="contentPresenter" Margin="5" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" HorizontalAlignment="Left" /> <Rectangle x:Name="FocusVisualElement" RadiusY="1" RadiusX="1" Stroke="#FF6DBDD1" StrokeThickness="5" Visibility="Collapsed"/> </Grid> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources> Entsprechend sieht dann meine Liste nicht mehr besonders schön aber aus. Aber ich konnte das selektierte Item verändern und kann sogar die Animation dazu steuern. Und wieder ein Auftrag erfüllt.

Visual Studio 2010 und Expression Blend Datenbindung mit Designer unterstzung

Wenn man in Silverlight Projekten Datenklassen erstellt und dann bindet entsteht der Wunsch im Designer schon einen sinnvollen Preview zu haben. Für Expression Blend gibt es die Möglichkeit mit Designtime zu arbeiten. In diesem Silverlight Beispiel will ich aber direkt die DAL pimpen. Dafür muss einfach der Konstrukor herhalten, der für die verschiedenen Fälle unterschiedliche Daten generiert. Public Class person Implements INotifyPropertyChanged public Sub New() If DesignerProperties.IsInDesignTool Then _name = "hannesp ist ein Designer" Else If HtmlPage.IsEnabled Then _name = "hannesp nuttzt den Webbrowser" Else _name = "hannesp startet die Anwendung OOB" End If End If End Sub Die  Bindung wird voll deklarativ durchgeführt. <UserControl.Resources> <local:person x:Key="personDataSource" d:IsDataSource="True"/> </UserControl.Resources> <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource personDataSource}}"> <TextBox Height="23" Margin="151,23,12,0" TextWrapping="Wrap" Text="{Binding name, Mode=TwoWay}"> In Visual Studio 2010 (cidder) Im Webbrowser Out Of Browser (OOB)

Listbox eintrge die die ganze Breite einnehmen

Diesesmal hat mein Silverlight Schulungsteilnehmer (Stephan) mir eine cooles Silverlight Sample gezeigt. Während der Schulung war wieder mal die Listbox ein Thema (darüber könnte ich schon ein Buch schreiben. Es sollte eine Liste mit Namen angezeigt werden und dabei das Itemtemplate verändert werden. Das geht eigentlich ganz einfach entweder mit Expression Blend oder direkt im XAML Code ala <ListBox.ItemTemplate> <DataTemplate> <Border Margin="5" BorderBrush="#FF340CF9" CornerRadius="5" Height="32"> <Border.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="Black" Offset="0"/> <GradientStop Color="White" Offset="1"/> </LinearGradientBrush> </Border.Background> <TextBlock Text="{Binding}" Foreground="#FFF9F5F5" FontWeight="Bold" Margin="4,2"/> </Border> </DataTemplate> </ListBox.ItemTemplate> Das ganze sieht allerdings mehr nach Balkengrafik aus. Der Wunsch ist das alle Listboxitems gleich breit sein sollen. Eigentlich ganz einfach. Die Width der Broder gesetzt und… Aber es soll genau so breit sein wie die Listbox. Das geht indem man das HorizontalContentAlignment auf Stretch setzt. Da kommt man leider gar nicht so leicht ran. Entweder man nimmt Expression Blend und zerlegt die Listbox in sein Template. Contextmenü – Edit Template-Edit a copy.- und erhält 300 Zeilen XAML Code Oder man nimmt die relevante Setter Stelle einfach raus und setzt sie sozusagen auf dem kurzen Dienstweg im ItemContainerstyle der Silverlight Listbox <ListBox Margin="96,26,129,54" x:Name="listbox1" > <ListBox.ItemContainerStyle> <Style TargetType="ListBoxItem" > <Setter Property="HorizontalContentAlignment" Value="Stretch"/> </Style> </ListBox.ItemContainerStyle>   Perfektes Ergebnis im Browser

Einen Silverlight Clipping Path Animieren

Aktuell habe ich in meiner Silverlight Schulung einen Teilnehmer, der wirklich interessante Fragen aufwirft, deren Antwort ich dann im Nachgang suche und hier in meinem Blog poste. Schließlich sind nur glückliche Silverlight Kurs Teilnehmer auch gute Teilnehmer. In diesem Silverlight 4 Beispiel soll ein Bild beschnitten werden. Das geht auch ganz einfach mit dem Clipping Path Attribut. Mit Expression Blend nimmt man ein Bild, legt darüber eine Kurve (z.B. Kreis) und markiert beide. Dann wird im Context Menü Path- Make Clipping Path ausgewählt. Im XAML sieht das wie folgt  aus und man sieht nur mehr den Hintern der hübschen Person vom Foto . <Image Margin="30,41,-140,-124" Source="/maus[1].jpg" Stretch="Fill" Clip="M470.5,351.5 C470.5,387.3985 425.05692,416.5 369,416.5 C312.94308,416.5 267.5,387.3985 267.5,351.5 C267.5,315.6015 312.94308,286.5 369,286.5 C425.05692,286.5 470.5,315.6015 470.5,351.5 z"/> Leider lässt sich das so nicht mehr animieren. Viele Attribute eines UIElements lassen sich durch ein Unterelement aufsplitten. So auch das Clip Attribut zu Image.Clip. Der Pfad wird dann mit Geometry Elementen beschrieben. In unserem Beispiel eben eine Ellipse. <Image x:Name="image" Source="/maus[1].jpg" Stretch="Fill" RenderTransformOrigin="0.5,0.5" > <Image.Clip> <EllipseGeometry RadiusX="50" RadiusY="50" Center="100,100"> </EllipseGeometry> </Image.Clip> </Image> Insgesamt gibt es fünf solcher Geomtrien. EllipseGeometry GeometryGroup LineGeometry PathGeometry RectangleGeometry Jetzt bleibt nur mehr das Problem der Animation. Leider gibt es keine Unterstützung durch Expression Blend oder schon gar nicht Visual Studio 2010. Also ran per Hand. Da ich eine Punkt verschieben möchte ist die PointAnimation das passende. Recht Tricky ist die Adressierung des Center Propertys über die XAML Hierarchie. <Storyboard x:Name="Storyboard1"> <PointAnimation BeginTime="0" Duration="0:0:01" From="0,0" To="200,200" Storyboard.TargetName="image" Storyboard.TargetProperty="(UIElement.Clip).(EllipseGeometry.Center)"></PointAnimation></Storyboard> Gestartet wird die Animation per Expression Blend behavior. Am Ende eine Null Code Lösung. <i:Interaction.Triggers> <i:EventTrigger EventName="MouseLeftButtonDown"> <ei:ControlStoryboardAction Storyboard="{StaticResource Storyboard1}"/> </i:EventTrigger> </i:Interaction.Triggers> Funktioniert. Ich bin wieder mal begeistert.  

BusyIndicator in ObservableCollection einbinden

In meiner aktuellen Silverlight Schulung haben wir ein wenig auf Performance geschaut. Ein Ladevorgang einer sehr langen Liste dauert etwas länger und während dieser Zeit soll der eine Wartemeldung angezeigt werden. Im Silverlight Toolkit befindet sich auch passend ein entsprechendes Control der BusyIndicator. Dieser lässt sich auch in sein Template zerlegen und optisch anpassen. Während der Busyindicator läuft kann der User keine UI Elemente nutzen und so z.B. einen Button eben nicht zwei mal drücken. Die entscheidende Frage ist, wie zeige ich den BusyIndicator nun an. Zunächst hat dieser eine Boolsche Eigenschaft IsBusys die man setzen kann. Manchmal läuft die Logik aber in einer Komponente dahinter von der man keinen Zugriff aufs UI hat.  In meinem Fall eben eine Liste die von ObservableCollection erbt. Diese Liste wird zur Laufzeit durch einen länger laufenden Download gefüllt, zur Design Zeit mit Dummy Daten.  Diese Entscheidung wird duchr HTMLPagel.Isenabled getroffen. Die Observeable Collection muss ich nehmen damit das gebundene UI Element ( z.B. Datagrid oder Listbox) automatisch und asynchron die Daten auch anzeigen sobald sie fertig geladen sind. Zusätzlich verpasse ich der personen Liste ein Property isBusy. Dieses wird gesetzt wenn der Download startet. In der OpenreadCompleted Methode wird dann der Wert wieder auf false gesetzt. Public Class personen Inherits ObservableCollection(Of person) Public Sub New() If HtmlPage.IsEnabled Then Dim wc As New WebClient AddHandler wc.OpenReadCompleted, AddressOf fertig IsBusy = True wc.OpenReadAsync(New Uri("namen.txt", UriKind.Relative)) Else Add(New person With {.alter = 27, .Firma = "ppedv ag", .Name = "Hannes"}) Add(New person With {.alter = 32, .Firma = "ppedv ag", .Name = "Andreas"}) Add(New person With {.alter = 23, .Firma = "ppedv ag", .Name = "Bernhard"}) Add(New person With {.alter = 45, .Firma = "ppedv ag", .Name = "Stefan"}) Add(New person With {.alter = 12, .Firma = "ppedv ag", .Name = "Arnold"}) End If End Sub Private _IsBusy As Boolean Public Property IsBusy() As Boolean Get Return _IsBusy End Get Set(ByVal value As Boolean) _IsBusy = value OnPropertyChanged(New PropertyChangedEventArgs("IsBusy")) End Set End Property Im XAML wird dann der BusyIndicator an die IsBusy Eigenschaft gebunden. Dies ist auch der Grund für die onpropertyChanged Codezeile im vorigen Beispiel. Damit kann das UI Änderung der Daten im Personen Object “abbonieren”. Damit wird die Anzeige eben sofort erneuert. <toolkit:BusyIndicator Height="100" HorizontalAlignment="Left" Name="BusyIndicator1" IsBusy="{Binding IsBusy}" VerticalAlignment="Top" Width="150" > </toolkit:BusyIndicator>