Windows 8 Listview gruppiert V2

Ich bin gerade dabei alle Blog Einträge auf WinRT und Windows 8 RP zu aktualisieren. Das Gruppieren wurde anhand einer Listview und CollectionViewSource gezeigt.  Nun folgt Version zwei mit weniger VB.NET Code. Für die Gruppierung muss eine CollectionViewSource zwischen geschaltet werden die im XAML im Resources Bereich deklariert wurde. <CollectionViewSource x:Name="cvs1" IsSourceGrouped="true" ItemsPath="Items" .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Ganz wichtig ist das Attribut isSourceGrouped und der Verweis auf das Items. Um diese Gruppe zu erzeugen wird ein LINQ Statement zwischen geschaltet. Dort gibt es die Möglichkeit zu gruppieren mit Group by. Dim pers = New personen Dim q = From p In pers Group p By key = p.sex Into Group Select New With {.key = key, .Items = Group} cvs1.Source = q .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Hier fallt natürlich auf, das die erzeugten Daten mit den Ursprungsdaten wenig gemein haben. Die Gruppe wird in einer generischen Liste in der Eigenschaft Group zwischen abgelegt und dann sozusagen im selben Atemzug den Items als neuer anonymer Typ zugewiesen. .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Der XAML Code benötigt ein Template für die Gruppe und eines für die Listen Einträge. Im Headertemplate wird aus dem true oder false des Key per Converter ein Text. <ListView ItemsSource="{Binding Source={StaticResource cvs1}}" > <ListView.GroupStyle> <GroupStyle> <GroupStyle.HeaderTemplate> <DataTemplate> <Grid Margin="7,7,0,0"> <Button Content="{Binding key,Converter={StaticResource fmConverter}}" /> </Grid> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> </ListView.GroupStyle> <ListView.ItemTemplate> <DataTemplate> <StackPanel Orientation="Horizontal"> <TextBlock Text="{Binding name}" FontSize="20" Margin="10" FontWeight="Bold"/> <TextBlock Text="{Binding alter}" FontSize="20" Margin="10" FontWeight="Bold"/> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView> .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }  

Windows 8 mitten ins Herz-Share Target

Wenn man sich mitteilen möchte muss auch ein Empfänger bereit stehen. In WinRT nennt sich das Share Target. Eine METRO App kann Ziel für bestimmte Typen oder alles sein. Festgelegt wird dies im Application Manifest. Um sich Aufwand zu sparen empfehle ich den Wizard von Visual Studio 2012 zu verwenden. Dieser erzeugt ein neues Item im VS Projekt vom Typ Share Target Contract. Ich finde die Bezeichnung im METRO Projekt eigentlich falsch. Es wird eigentlich eine XAML Page erzeugt, die aufgerufen wird, wenn jemand das Share aus einer anderen APP aufruft. Die Verbindung zwischen dieser XAML Seite und dem Share wird vom Wizard im app.xaml.vb Event  ShareTargetActivated hergestellt. Protected Overrides Sub OnShareTargetActivated(ByVal e As Windows.ApplicationModel.Activation.ShareTargetActivatedEventArgs) Dim shareTargetPage = New ShareTargetPage1 shareTargetPage.Activate(e) End Sub Das Manifest (Package.appxmanifest) legt fest, wann die eigene  Anwendung im Charm als Target aufscheint. Auch das erledigt der Projekt Template Wizward. Aus der Windows 8 Foto Anwendung kann dann, in die METRO Mail App geteilt werden. Der Benutzer wählt aus den möglichen Apps aus und erhält dann den Dialog der vorhin mit dem Namen ShareTargetPage1 erzeugt wurde. Diese XAML Seite und den VB.NET Code wird man dann modifizieren, so das er aussieht wie in der Mail APP. Es wird also statt der APP die Share Target Seite gestartet. Diese überlagert funktioniell wie ein modaler Popup Dialog, die aktuelle APP zu ca 45% von rechts. Dies geschieht im Activated Event, das  durch den Wizard schon mit VB.NET Code vorbelegt ist. Public Async Sub Activate(args As ShareTargetActivatedEventArgs) Me._shareOperation = args.ShareOperation ' Communicate metadata about the shared content through the view model Dim shareProperties = Me._shareOperation.Data.Properties Dim thumbnailImage = New BitmapImage() Me.DefaultViewModel("Title") = shareProperties.Title Me.DefaultViewModel("Description") = shareProperties.Description Me.DefaultViewModel("Image") = thumbnailImage Me.DefaultViewModel("Sharing") = False Me.DefaultViewModel("ShowImage") = False Window.Current.Content = Me Window.Current.Activate()…. Wenn man das Konzept ein wenig studiert, stolpert man auch noch über LayoutAwarePage im Verzeichnis Common die von Page erbt und eine ObservableDictionary als MiniViewModel implementiert. Von dieser Seite erbt dann die Share Target Page und weist wie oben im VB.NET Code Beispiel die Werte als Key Value der Page zu. In meinem Versuchen bleibt  das Search Target Fenster nicht aktiv bzw verschwindet sofort wieder wenn der Visual Studio debugger attached ist. Aktualisiert auf Windows 8 RP

Windows 8 mitteilsam. DataTransferManager

Zwei Windows 8.1 Stores Apps ( METRO APP) tauschen  über Contracts Daten aus. Eine APP ist Source und eine Target. Dabei ist es natürlich egal, mit welcher Sprache C#, VB.NET, C++ oder JavaScript die Anwendung programmiert wurde. Relevanter ist schon welche Art von Information man teilen möchte. Das muss vom Typ zusammen passen. Möglich ist vieles wie Text, Bilder oder Links. In diesem Blog Beitrag werden wir eine Anwendung beschreiben, die Ihre Daten teilt. Einfacher Text per Share Charm an eine andere Anwendung. Die Klasse  DataTransferManager liefert den Context, Methoden und Events. Da jede Page innerhalb einer APP meist etwas anderes zu sharen hat, üblicherweise im Pageload. 1: Dim dm As DataTransferManager 2: Private Sub Page_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded 3: dm = DataTransferManager.GetForCurrentView() 4: End Sub .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Es gibt ein spezielles Event DataRequested das ausgelöst wird, wenn per Charm vom Benutzer das Share initiiert wird. Das Event muss abonniert werden um es dann behandeln zu können. Dort werden dann die Daten zugewiesen. 1: AddHandler dm.DataRequested, AddressOf reqHandler .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Alternativ kann der Sharing Context UI der WinRT API auch per Code angezeigt werden. 1: DataTransferManager.ShowShareUI .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } In der Ereignis Behandlung wird in den Argumenten eine Referenz auf das angeforderte Share Objekt übergeben. Eigenschaften wie Titel und Beschreibung müssen dann per VB.NET Code zugewiesen werden. Andere Methoden beziehen sich spezifisch auf den Inhalt. Wie SetBitmap, SetRtf oder SetData für HTML. Neu in Windows 8.1 sind z.B. SetWebLink oder SetApplicationLink und ersetzen damit setURI (obsolet). 1: Private Sub reqHandler(sender As DataTransferManager, args As DataRequestedEventArgs) 2: args.Request.Data.Properties.Title = "dataPackageTitle" 3: args.Request.Data.Properties.Description = "dataPackageDescription" 4: args.Request.Data.SetText("dataPackageText") 5: End Sub Windows 8.1 erlaubt nun ein Sharing aus jeder APP. Es wird einfach der Screenshot angeboten. Die vollständige Doku findet sich in der MSDN online. Im folgenden VB.NET Beispiel wird ein fehlerhafter Share simuliert. Der Benutzer sieht dann beim nächsten Aufruf des Charm die Fehlermeldung. Dies wird benötigt um in der Source App den Share per Programmierlogik abbrechen zu können. 1: Private Sub errorHandler(sender As DataTransferManager, args As DataRequestedEventArgs) 2: args.Request.Data.Properties.Title = "dataPackageTitleError" 3: args.Request.Data.Properties.Description = "dataPackageDescriptionError" 4: args.Request.FailWithDisplayText("so nicht ganz boeser Fehler") 5: End Sub 6: Private Sub Button_set(sender As Object, e As RoutedEventArgs) 7: AddHandler dm.DataRequested, AddressOf reqHandler 8: DataTransferManager.ShowShareUI() 9:   10: End Sub .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }

Protokoll Bindungen in Windows 8

Wer mit HTML zu tun hat, kennt das mailto:info@ppedv.de?subject=hallo. Beim Click auf den Hyperlink öffnet sich das Mail Programm und die To und Betreffzeile ist gefüllt. Auch ein anderes Protokoll nämlich http://www.ppedv.de kennt man, der Browser öffnet sich und zeigt die Website an. In Windows 8 kann man nun eigene Protokolle definieren. In Vorbereitung eines Fax Clients wird es pp-fax werden. Der erste Weg führt, wie so oft in WinRT in das Manifest der Anwendung. Hier der Screenshot von Visual Studio 11 Als nächstes wird in der app.xaml.vb das entsprechende Event OnActivated überladen. Im Event wird dann einen Seite,in dem Fall einfach ein Usercontrol instanziiert und die Eventargs übergeben. darin befinden sich nämlich das wichtigste Attribut, die ursprüngliche URI. Protected Overrides Sub OnActivated(ByVal args As IActivatedEventArgs) Dim pwindow As New protAct pwindow.pargs = args Window.Current.Content = pwindow Window.Current.Activate() End Sub Wenn der Benutzer in der Explorer Leiste dann eingibt wie folgt. pp-fax://meinehost/irgendwas?faxnummer=23235345345 eingibt wird die METRO App gestartet. Internet Explorer geht natürlich auch. Jetzt kann in der Seite die Information ausgewertet werden. Dieses VB Beispiel legt dafür einfach ein Property an vom Typ ProtocolActivatedEventArgs. Im Setter werden dann Textboxen befühlt. Die Klasse URI stellt eine Reihe praktischer Helfer bereit um die URL in Ihre Bestandteile Host, Pfad und Querystring zu zerlegen. Private _pargs As ProtocolActivatedEventArgs Public Property pargs() As ProtocolActivatedEventArgs Get l Return _pargs End Get Set(ByVal value As ProtocolActivatedEventArgs) _pargs = value textbox2.Text = _pargs.Uri.Host textbox3.Text = _pargs.Uri.Query 'pargs.Uri.AbsolutePath '_pargs.Uri.Segments() End Set End Property In der vorliegenden Windows 8 Consumer Preview, ist es nicht möglich per HyperlinkButton die Protokoll Aktivierung einer fremden METRO App zu erzwingen. Allerdings klappt es mit der Launcher Klasse eine externe Anwendung zu starten. Private Async Function Button_Click_2(sender As Object, e As RoutedEventArgs) As Task Await Launcher.LaunchUriAsync(New Uri("mailto:INFO@ppedv.de?subject=hannes")) End Function

Suspension Manager

Nein es geht nicht um Frührente. der Lebenszyklus von Windows 8 METRO Anwendungen etwas eigenwillig. Kurz gesagt, kann man das Betriebssystem einen in den Hintergrund gelegte Anwendung aus dem Betriebssystem Sheduler entfernen. Der Modus nennt sich suspended. warum und wieso dauert etwas länger zu erklären.  Relevant ist, was man aus Coding Sicht tun muss. Eigentlich muss man gar nichts tun. Die Anwendung wird ja nicht geschlossen, insofern kommt sie in dem Status zurück wie man sie zuletzt gesehen hat. Eine Textbox beinhaltet noch immer den Text. Aber Windows 8 hat das rechteine Suspended APP komplett zu entfernen. Ich habe das zwar noch nie gesehen, aber  bei kommenden ARM Geräten mit wenig Arbeitsspeicher wird das wohl passieren. Sämtliche Beispiele aus dem SDK enthalten eine Hilfsklasse die sich SuspensionManager nennt.Ich kopiere die aktuell in meine Projekte, auch wenn sie nicht optimal ist. Module SuspensionManager Private sessionState_ As New Dictionary(Of String, Object) Private knownTypes_ As List(Of Type) = New List(Of Type) Private Const filename As String = "_sessionState.xml" ' Provides access to the currect session state Public ReadOnly Property SessionState As Dictionary(Of String, Object) Get Return sessionState_ End Get End Property ' Allows custom types to be added to the list of types that can be serialized Public ReadOnly Property KnownTypes As List(Of Type) Get Return knownTypes_ End Get End Property ' Save the current session state Public Async Function SaveAsync() As Task ' Get the output stream for the SessionState file. Dim file As StorageFile = Await ApplicationData.Current.LocalFolder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting) Dim raStream As IRandomAccessStream = Await file.OpenAsync(FileAccessMode.ReadWrite) Using outStream As IOutputStream = raStream.GetOutputStreamAt(0) ' Serialize the Session State. Dim serializer As New DataContractSerializer(GetType(Dictionary(Of String, Object))) serializer.WriteObject(outStream.AsStreamForWrite, sessionState_) Await outStream.FlushAsync End Using End Function ' Restore the saved sesison state Public Async Function RestoreAsync() As Task ' Get the input stream for the SessionState file. Try Dim file As StorageFile = Await ApplicationData.Current.LocalFolder.GetFileAsync(filename) If file Is Nothing Then Exit Function End If Dim inStream As IInputStream = Await file.OpenSequentialReadAsync ' Deserialize the Session State. Dim serializer As New DataContractSerializer(GetType(Dictionary(Of String, Object))) sessionState_ = CType(serializer.ReadObject(inStream.AsStreamForRead), Dictionary(Of String, Object)) Catch ex As Exception ' Restoring state is best-effort. If it fails, the app will just come up with a new session. End Try End Function End Module In der Applikation Klasse app.xaml.vb wird dann diese aufgerufen wenn die Anwendung schlafen geht oder wieder aufwacht. Protected Async Sub OnSuspending(ByVal sender As Object, ByVal args As SuspendingEventArgs) Handles Me.Suspending Dim deferral As SuspendingDeferral = args.SuspendingOperation.GetDeferral Await SuspensionManager.SaveAsync deferral.Complete() End Sub Protected Overrides Async Sub OnLaunched(ByVal args As LaunchActivatedEventArgs) If args.PreviousExecutionState = ApplicationExecutionState.Terminated Then ' Do a synchronous restore Await SuspensionManager.RestoreAsync End If Spannend ist der Part in dem man per Getdeferral WinRT bittet die Anwendung vom Suspending zu verschonen. Schließlich kann das speichern ja etwas länger dauern und damit wird der 5 Sekunden Zyklus unterbrochen. Ob die Anwendung vom Suspended oder Terminated Modus kommt, spielt für das Restore der Settings natürlich eine Rolle. Im eigentlichen VB.NET Programmcode wird auch der Suspensionmanager um einer Art Session wie in ASP.NET relevanten Infos abzulegen. So kann das ausgewählte Listenelement  wieder hergestellt werden. If SuspensionManager.SessionState.ContainsKey("SelectedScenario") Then Dim selectedScenarioName As String = TryCast(SuspensionManager.SessionState("SelectedScenario"), String) startingScenario = TryCast(Me.FindName(selectedScenarioName), ListBoxItem) End If

Windows 8 flippt aus

Vermutlich wird es zwei Wege geben. Schlichtweg ignorieren oder Design Orgien mit Expression Blend. Windows 8 Anwendungen können in mindestens vier verschiedenen Layouts daher kommen. Relativ klar ist Hochformat oder Querformat. Man kann in Visual Studio 11 die METRO styled APP auch dazu zwingen in einem bestimmten Format zu starten. Dazu kommt noch der jeweilige Flipped Mode der quasi das Bild um 180 Grad dreht und so den physikalischen Windows 8 Start Button nach oben bringt. Je nach Anwendungstyp kann es sinnvoll sein sich als Entwickler vorher festzulegen. Typische Reader werden meist im Hochformat arbeiten. Videos wird man eher quer betrachten. Dann sind da noch die flexiblen Anwendungen die alles können. Die Weather APP wird gerne als Beispiel genannt. Es gibt Fullscreen (ganzer Bildschirm), Snapped (Randdbereich)  und Filled(das dazwischen. Je in der Landscape und Portrait Variante. Das erkennen des aktuellen Zustandes gestaltet WinRT noch einfach Private Sub UserControl36_Loaded(sender As Object, e As RoutedEventArgs) Handles Me.Loaded AddHandler ViewManagement.ApplicationView.GetForCurrentView().ViewStateChanged, AddressOf gedreht End Sub Private Async Function gedreht(sender As ApplicationView, args As ApplicationViewStateChangedEventArgs) As Task Dim msg = New MessageDialog(args.ViewState.ToString) Await msg.ShowAsync End Function Wie  aber gestaltet man den XAML Code dazu? Die Beispiele arbeiten alle mit Viewstates. Es findet sich folgender VB.NET Beispiel Code dazu um den State zu wechseln. Private Sub MainPage_ViewStateChanged(ByVal sender As ViewManagement.ApplicationView, ByVal args As Windows.UI.ViewManagement.ApplicationViewStateChangedEventArgs) Select Case args.ViewState Case Windows.UI.ViewManagement.ApplicationViewState.Filled VisualStateManager.GoToState(Me, "Fill", False) Case Windows.UI.ViewManagement.ApplicationViewState.FullScreenPortrait VisualStateManager.GoToState(Me, "FullScreenPortrait", False) Case Windows.UI.ViewManagement.ApplicationViewState.FullScreenLandscape VisualStateManager.GoToState(Me, "FullScreenLandscape", False) Case Windows.UI.ViewManagement.ApplicationViewState.Snapped VisualStateManager.GoToState(Me, "Snapped", False) Case Else End Select End Sub Visual Studio 11 kann zwar exzellent XAML editieren, aber keine Animationen und damit auch Viewstates. Wenn man das nicht in XAML runtertippen möchte bleibt nur Expression Blend. Dort kann man für jeden Status Animationen definieren. Diese können Controls auf dem Userinterface bewegen, drehen oder auch schmäler machen.  Man kann auch komplette Controls ein und Ausblenden und so aus einem Grid eine Listview machen. Bisher kenne ich keine Anwendung die das konsequent umsetzt. So wird beim Kalender aus einer Tagesansicht in der snapped Darstellung eine Monats Übersicht, Auch wenn der Visual State Manager von Expression Blend hilft, ein vollständiger Designer für die Sichten ist er nicht. So gehe ich davon aus das die ersten Windows 8 METRO Anwendungen aus dem Store alle nur auf eine Sicht hin optimiert sind. Nur wenige APPs werden auch im ersten Schritt die Notwendigkeit haben sozusagen aktiv auf der Seite zu laufen, gerade weil auch über Toasts Anwendungen im Suspended Modus sich beim Benutzer melden können.

MVVM mit weniger Code

Durch Zufall bin ich darüber gestolpert. In Windows 8 Projekten legt Visual Studio 11 im Common Verzeichnis eine Klasse BindableBase an. Diese kapselt, auch dank eines neuen Attributes CallerMemberName in .NET 4.5, sehr elegant den Code für die Implementierung des Event Systems. Dies ist nötig um den View, als das per XAML definierte Userinterface, über Änderungen an den Daten zu informieren. Der bisherige Ansatz implementiert das Interface INotifyPropertyChanged in der Viewmodell Klasse. Dann wird das Event per RaiseEvent für jede Eigenschaft einzeln gefeuert. Aber nur wenn die Daten sich auch geändert haben. Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged Private _BenutzerName As String Public Property BenutzerName() As String Get Return _BenutzerName End Get Set(ByVal value As String) If _BenutzerName <> value Then _BenutzerName = value RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs("BenutzerName")) End If End Set End Property End Class Auch wenn das Raisevent in der Praxis meist in eine Methode OnPropertyChanged gekapselt wird, noch ganz schien viel Code. Nun zum Vergleich der ungleich schönere Code mit der geerbten BindableBase Implementierung. Vor allem die unschöne und von Intellisense nicht unterstützte Schreibweise mit dem PropertyName als String fällt weg.   Public Class PersonViewModel Inherits BindableBase Private _BenutzerName As String Public Property BenutzerName() As String Get Return _BenutzerName End Get Set(ByVal value As String) _BenutzerName = value SetProperty(_BenutzerName, value) End Set End Property Das VB.NET Codebeispiel wurde in WinRT geschrieben, sollte auch in .NET 4.5 Windows Desktop Anwendungen funktionieren.

Windows 8 Settings speichern

Unter dem Motto alles ganz anders und doch ähnlich, steht das speichern von Settings mit WinRT. ganz ähnlich wie bei Silverlight kann man einzelne Werte in ein Key Value Dictionary anlegen oder direkt komplexe Datentypen serialisieren und per Dateizugriff lesen und schreiben. Zusätzlich kann man aber nun die Dictionarys in Container gruppieren. Vermutlich um Gruppen von Einstellungen leichter handhaben zu können. Der Große Unterschied ist, das es kein IsolatedStorage gibt. Die Daten werden entweder lokal, Temporär oder Roaming und damit in der Cloud gespeichert. Dim localSettings As Windows.Storage.ApplicationDataContainer = Windows.Storage.ApplicationData.Current.LocalSettings Dim temporaryFolder As Windows.Storage.StorageFolder = Windows.Storage.ApplicationData.Current.TemporaryFolder Dim roamingSettings As Windows.Storage.ApplicationDataContainer = Windows.Storage.ApplicationData.Current.RoamingSettings .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Speichern eines Wertes per Key lokal localSettings.Values("hannes") = textbox1.Text + " " + Date.Now .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Gruppieren per Container localSettings.CreateContainer("hannescontainer", ApplicationDataCreateDisposition.Always) localSettings.Containers("hannesontainer").Values("eins") = textbox1.Text + " " + Date.Now .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Speichern im Temp Folder ( bei mir C:\Users\pre\AppData\Local\Packages\BUILD.588a3b42-fdba-460e-8a17-f527a66ad183_89gf582k2a27c\TempState ) Dim tf As StorageFile = Await temporaryFolder.CreateFileAsync("hannes.txt", CreationCollisionOption.ReplaceExisting) Await FileIO.WriteTextAsync(tf, textbox1.Text + " " + Date.Now) .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Bisher konnte ich nicht herausfinden wann der Inhalt des Tempfolders wieder geleert wird. Die letzte Möglichkeit ist das Speichern in der Cloud, als Roaming bezeichnet. Damit kann der Benutzer von einem Gerät zum anderen wechseln und dort weiter arbeiten. Um das Änderungen in der Konfiguration mitzubekommen gibt es auch ein Event DataChanged (Windows.Storage.ApplicationData.Current.DataChanged ). Mit folgendem VB.NET Code Beispiel wird gesichert roamingSettings.Values("hannesroaming") = textbox1.Text + " " + Date.Now .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } In der Microsoft Cloud gibt es natürlich auch Beschränkungen, die man per Code auslesen kann. ' textblock1.Text = ApplicationData.Current.RoamingStorageQuota textblock1.Text = ApplicationData.Current.RoamingFolder.Path .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; } Das speichern erledigt WinRT allerdings asynchron. Zuerst werden die Settings in der normalen Settings.dat Datei lokal gespeichert. Erst nach einigen Sekunden, funkt Windows 8 zu Microsoft und übergibt in einem komprimierten XML Format die Daten, wie der Screenshot mit Fiddler zeigt. Ohne es zu wissen, kann das Roaming nur über die Windows ID (Live ID) funktionieren. Bei meinem Samsung XE700 melde ich mich mit der Live ID an. Wie das ist mit einer Domain Anmeldung, habe ich noch nicht getestet und auch  nicht in der Doku gefunden.

Bilder in Windows 8 METRO Styled Apps

Wenn man in Windows 8 Tiles oder Popups Bilder anzeigen möchte stellen sich mehrere Fragen. Zunächst das Format. Möglich ist png und jpg (auch jpeg).  Auch Transparenz in PNG funktioniert. Typischerweise wird ein Bild in das APPX Paket eingepackt und damit in die Visual Studio 11 Solution. In Silverlight Anwendungen war das eher verpönt, weil der Download eine Rolle gespielt hat.Bei WinRT wird die Anwendung ohnehin dauerhaft installiert und damit das oder die Images. Der Eigenschaftsdialog muss dann das Bild als Content zeigen. Dann lässt sich im Image Source Attribut über die neuen Protokoll Spezifikationen ms-appx auf das Bild verweisen. In diesem Fall liegt das Bild im Unterverzeichnis Images. <Image HorizontalAlignment="Left" Height="100" Margin="36,39,0,0" VerticalAlignment="Top" Width="347" Source="ms-appx:///Images/ppedv.jpg"/> Wenn Bild Link fehlerhaft ist erscheint keine Fehlermeldung. Die zweite Möglichkeit ist,  das Bild lokal auf der Festplatte in den Settings der Anwendung zu speichern (oder Roaming, dazu aber wann anders mehr).  Der folgende Code liest das Verzeichnis aus. Dim localSettings As Windows.Storage.ApplicationDataContainer = Windows.Storage.ApplicationData.Current.LocalSettingsDim localFolder As Windows.Storage.StorageFolder = Windows.Storage.ApplicationData.Current.LocalFolder Textbox1.Text = localFolder.Path Am Ende kommt ein bekannter Pfad raus, der den Namen des Pakets aus den Visual Studio appx Manifest enthält plus einen Salt Wert. C:\Users\pre\AppData\Local\Packages\BUILD.588a3b42-fdba-460e-8a17-f527a66ad183_89gf582k2a27c\LocalState Ich habe eine Grafik Datei per Hand dort hin kopiert. Referenziert wird diese mit einer weiteren WinRT Protokoll Spezifikation mx-appdata und dem fixen Zusatz local. <Image HorizontalAlignment="Left" VerticalAlignment="Top" Source="ms-appdata:///local/windows.png"/> Natürlich kann man das Bild auch aus dem Web laden mit dem HTTP Protokoll. Entgegen meiner Erwartungen wird das Bild auch gecacht. Konkret per Etag und 304 Meldung, so das die Anwendung dauerhaft dasselbe  Bild nicht. <Image HorizontalAlignment="Left" VerticalAlignment="Top" Source="http://www.firmenpresse.de/adpics/76898.jpg?id=2212"/>   Das Caching lässt sich nicht beeinflussen Abhilfe schafft ein alter Trick mit einem angehängten wechselnden  Querystring.

Live Tiles in Windows 8 angewendet

Wenn sich der Inhalt einer Kachel (also dem Menüpunkt) ändert, spricht man von einer Live Tile. Der Internet Explorer 10 Metro Styled nutzt das um Websites in das METRO Menü zu bringen. In der Appbar einfach Pin auswählen und schon kann die Seite in Zukunft vom Benutzer direkt angesprungen werden. Wenn die Webseite ein Icon (32x32) enthält, wird dieses dann in die Kachel eingebaut. Außerdem kann man die Beschriftung per Metatag vorbelegen. Der Benutzer kann den Titel aber immer anpassen. <head runat="server"><title></title> <link href="favicon.ico" rel="shortcut icon" /> <meta name="application-name" content="ppedv Demo" /> <meta name="msapplication-badge" content="frequency=30;polling-uri=http://localhost/badge.aspx" /> </head> .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }   In der letzten Zeile der Metatags (Badge) kann man sogar ein aktives Element definieren. Das heißt eine andere Webseite liefert einen WinRT Badge typischen XML String. In diesem Beispiel verwende ich eine komplett entkernte ASPX Datei. Es sind wirklich nur die beiden Zeilen XML bzw. VB.NET Code enthalten. <?xml version="1.0" encoding="utf-8" ?> <badge value="<%=cint(rnd(1)*100) %>"/> .csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, "Courier New", courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }   Die Wiederholintervall ist  Minuten, also in dem Fall 30 Minuten. Wenn der Badge Value auf unavailable gesetzt wird erscheint ein kleines rundes Bullet Icon im Tile. Gültige Werte sind laut Doku 30, 60, 360, 720, 1440 (default=1  Tag) Wenn es Neuigkeiten gibt zeigt die Kachel dies dann an. Hier ist die Kachel auch gleich markiert. Damit kann man in der Appbar dann auch das “Live” an und abschalten. Theoretisch sollten man den Update auch per Javascript ausführen können. Die nötige Methode  window.external.msSiteModeRefreshBadge(); funktioniert leider bei mir nicht.