Die Sache mit der Zeit

Windows 8 ist fast fertig. Es sind nur mehr ganz wenige Tage bis zur RTM. Und doch liefert Windows 8 eine seltsame Zeit bzw. Formatierung wenn man Date.Now einer Textbox zuweist. Dabei sind die Setting im Betriebsystem auf Deutsch eingestellt. Der WinRT datetime Typ besitzt keine Culture Informationen und kann das deswegen auch nach Projektion auf .NET DateTime nicht umsetzen. Bei DateTimeOffset wird daraus  Windows.Foundation.DateTime und da fehlt selbst die Zeitzone. Folgenden Code kann man als Workaround betrachten um die Region manuell zu setzen. Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "de-DE" textblock1.Text = Date.Now.ToString("d") Jedenfalls anders als man das aus .NET kennt  (Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US")) Man kann auch die aktuellen Einstellungen auslesen mit Windows.Globalization.DateTimeFormatting.DateTimeFormatter.ShortDate Wie letztendlich formatiert wird, geben die Region Einstellungen vor, Mit diesem untypischen Format sieht das ShortDate dann auch so aus Ähnliche Probleme gibt es auch bei System.Uri und Windows.Uri da die Typen nicht ident sind.

Open Source? Scheissegal

Das EF gibt es ja nun als Open Source, neben der ASP.NET Web API und einigem anderen. Ich frage mich schon seit mindestens 10 Jahren, warum ich den Source Code des Linux Kernels benötigen sollte. Nun ist auch Microsoft voll auf Standard und Open Source. Ich muss jetzt mal eine Lebensbeichte ablegen, ich versteh das alles nicht mehr. Ich werde nie in den Source Code reinschauen und schon gar nicht kompilieren. Mich regt das ganze SDK und Toolkit Source Gedöns ohne Ende auf. Vielleicht gibt es ja auch berufenere wie mich. Jemand der in zwanzig Bibliotheken, jede Zeile kennt. Jemand der in Mikrosekunden zwischen Sprachen umschalten kann und jedwelche Syntax Variante Fehlerfrei mit 250 Anschlägen pro  Minute runtertippt. Jemand der sämtliche .NET Framework Varianten und API Unterschiede, ohne Luft zu holen, runterbeten kann. Ich nicht. Ich scheitere an: lese das und schreibe es in das- kontinuierlich. Dabei mache ich seit 10 Monaten ohnehin nur mehr VB.NET und Windows 8. Aber selbst in dem kleinem Themen Bereich stellt sich dauernd die Frage- WinRT Stream, IBuffer, Byte Array oder unsichtbare Extension Methode. Ich kann das kaum frei runtertippten und muss immer den Samples schauen. Die Doku braucht man ohnehin nicht zu bemühen, nach was soll ich auch suchen. Nur mal Reflection unter 4.5 und WinRT ansehen. Sind ja nur ein paar kleine Unterschiede, aber welche und wann? Und jetzt wende ich mich meinen Schulungsteilnehmern zu. In der Regel habe ich einen deutlichen Wissensvorsprung, sonst würden mein Einsatz auch keine Sinn machen. In einem Kurs ist  ein Event registrieren häufig das höchste der Gefühle. LINQ manchmal, Lambda selten. MVVM nur weil das alle sagen, Verstehen und Anwenden sehr selten. Test Driven, jaaaa wäre schön aber…. Was brauchen 99% der Entwickler? Einfache Werkzeuge Programmierkonzepte die Step by Step zum Erfolg führen Steuerelemente Kunden/Auftraggeber die Zahlen Was brauchen sie nicht Open Source wechselnde Frameworks &APIs Fehlerhafte Controls offene Standards Am Ende soll sich kein Entwickler schlecht oder schlechter fühlen nur weil er diesen Prinzipen nicht folgt oder folgen kann. Du bist gut, Dein Code ist gut- solange er läuft

Windows 8 Tiles Updaten

Tiles, also Kacheln, sind die Visitenkarte einer Anwendung. Anders als ein Menü Icons kann über die WinRT API der Inhalt, also Text und Bild verändert werden. die Tiles haben so sich veränderte Inhalte- IN den METRO Style Guidelines steht, das man auf keinen Fall das für Werbung missbrauchen soll.  Einige tuns aber. In meiner Windows 8 Demo Anwendung wird zunächst die Tile direkt beschrieben.  Mir fällt zwar kein Anwendungszweck ein, aus einer laufenden Anwendung, die unsichtbare Kachel zu aktualisieren, aber es geht. Tiles werden in einem XML Format beschrieben. Dieses wird auch von Windows Push Notification genutzt. Um dieses XML Format zu erzeugen gibt es einen Helperklasse  TileUpdateManager.GetTemplateContent, mit der man aus den vorhandenen Templates auswählen können. Als alter VB Programmierer nehme ich allerdings VB.NET Literals um das XML zu erzeugen. Das mag unter WinRT absurd wirken, aber sobald die Tile Daten von woanders kommen muss das XML ohnehin per Hand zusammen gesetzt werden. Dazu später ein wenig mehr VB Code. Dim text1 = "Hallo" Dim text2 = "Welt" Dim tc As XDocument = <?xml version="1.0"?> <tile> <visual branding="name"> <binding template="TileWideText09"> <text id="1"><%= text1 %></text> <text id="2"><%= text2 %></text> </binding> <binding template="TileSquareText04"> <text id="1"><%= text1 %></text> </binding> </visual> </tile> Dim xml As New XmlDocument xml.LoadXml(tc.ToString) TileUpdateManager.CreateTileUpdaterForApplication().Update(New TileNotification(xml)) Ja, liebe C# Entwickler, den Code kann man so direkt in Visual Studio tippen. In den letzten zwei Zeilen wird aus einem .NET XDocument ein WinRT XMLDocument. Das geht unter reinem .NET mit GetXMLDocument. Die Methode fehlt der WinRT API aber. In der letzten Zeile wird dann die Kachel aktualisiert. Abhängig davon ob sie quadratisch oder doppelt breit ist, mit unterschiedlichen Inhalten aus dem obigen XML Template. Wesentlich näher dran an der Realität ist der dauerhafte Update, auch wenn die Anwendung nicht läuft, mittels Periodicupdate. Davon gibts eigenlich zwei, den StartPeriodicUpdateBatch für eine Liste von 5 und den StartPeriodicUpdate für eine URL. Im folgenden VB Sample wird ein Array mit einem Eintrag erzeugt. TileUpdateManager.CreateTileUpdaterForApplication(). StartPeriodicUpdateBatch({ New Uri("http://localhost:55108/tile1.aspx")}, PeriodicUpdateRecurrence.HalfHour) Das kleinste verfügbare Intervall ist 30 Minuten, siehe auch den Browser Blog Eintrag. In meiner kleinen ASP.NET Website wird wieder mit XML literals das passende Live Tile Template zusammen gesetzt. Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load Dim tc = <tile> <visual branding="name"> <binding template="TileWideText09"> <text id="1"><%= Date.Now.DayOfWeek %></text> <text id="2"><%= Date.Now %></text> </binding> <binding template="TileSquareText04"> <text id="1"><%= Date.Now.DayOfWeek %></text> </binding> </visual> </tile> Response.Write(tc.ToString) End Sub Wenn die Tiles auch automatisch ausgetauscht werden sollen fehlt noch ein Zeile Code, die einmalig aufgerufen werden muss. (öfter geht auch) TileUpdateManager.CreateTileUpdaterForApplication(). EnableNotificationQueue(True) Es können maximal fünf Templates gewechselt werden. Um ein einzelnes Template gezielt auszutauschen kann eine TAG ID verwendet werden. Dieser X-WNS-Tag  kann man neben der Lebensdauer X-WNS-Expires im HTTP Header übertragen. Natürlich kann man das Tile update auch wieder stoppen, per stopperiodicupdate.

ScrollViewer Windows 8

Das Scrollviewer Control aus WinRT ersetzt gleich zwei WPF oder Silverlight Steuerelemente. Mit dem ZoomMode kann das Control nun Scrollviewer und ViewBox in einem und mehr. <ScrollViewer VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Auto" HorizontalAlignment="Left" VerticalAlignment="Top" Height="400" VerticalScrollMode="Disabled" ZoomMode="Disabled"> <StackPanel Orientation="Horizontal" Height="300"> <Image Source="http://www.carspotting.de/userfiles/127/Chevrolet-Chevrolet-Camaro-2008_508med.jpg" Height="300"/> <Image Source="http://www.carspotting.de/userfiles/123/Chevrolet-Corvette_2888med.JPG" Height="300"/> <Image Source="http://www.carspotting.de/userfiles/127/Chevrolet-Chevrolet-Camaro-2008_508med.jpg" Height="300"/> <Image Source="http://www.carspotting.de/userfiles/24/Chevrolet-Camaro-2008_3053med.JPG" Height="300"/> <Image Source="http://www.carspotting.de/userfiles/259/Chevrolet-Camaro-_4103med.jpg" Height="300"/> </StackPanel> </ScrollViewer> In der Verwendung sieht das dann so aus. Besonders praktisch ist das sowohl im Portrait oder Landscape Modus alles passt. Wenn der Benutzer mit der Maus oder dem Mausrad navigiert, erscheint ein Scrollbar. Bei der Touch Bedienung nicht. Wenn nun das Attribut Zoommode auf enabled im XAML deklariert wird, kann der Windows 8 Benutzer per STRG Mausrad oder mit der Zwei Finger Geste Zoomen und rollen. Eine Neuerung sind die Snappoints. Damit wird beim Scrollen soweit animiert, damit das komplette Bild/Frame sichtbar ist. Mit HorizontalSnapPointsType="Mandatory" und Breite des Scrollviewers ident mit Bildbreite. ohne Snappoint Die passend Windows 8 Schulung für XMMAL VB.NET und C#

Flipview und Template Selector

Grundsätzlich ist das Windows 8 FlipView Control ein Datensteuerelement. Man bindet es an eine Liste und der Benutzer kann per Flip Geste rechts links oder oben unten weiter blättern. Geht natürlich auch mit der Maus. In der Regel zeigt man nur einen Datensatz an. Wenn man so will das Forms Control. Klar kann man auch mit der Maus navigieren. Allerdings kann man auch verschiedene Sichten verwenden,die sich im XAML von WinRT als DataTemplates wieder spiegeln. Ob man damit einen Formular Wizard bauen sollte, bezweifle ich aktuell noch. Zunächst das WinRT FlipView Control in der XAML Deklaration <FlipView x:Name="flipview1" HorizontalAlignment="Left" Margin="335,23,0,0" VerticalAlignment="Top" BorderBrush="white" BorderThickness="1"> <FlipView.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Horizontal"/> </ItemsPanelTemplate> </FlipView.ItemsPanel> </FlipView> Dann die beiden Templates im Resources Bereich der XAML Page <DataTemplate x:Key="ImageTemplate"> <Grid> <Image Width="480" Height="270" Source="Assets/acid.png" Stretch="UniformToFill"/> <Border Background="#A5000000" Height="80" VerticalAlignment="Bottom"> <TextBlock Text="Bild" FontFamily="Segoe UI" FontSize="26.667" Foreground="#CCFFFFFF" /> </Border> </Grid> </DataTemplate> <DataTemplate x:Key="ContactTemplate"> <ContentPresenter Content="{Binding}"/> </DataTemplate> Damit wird generell im ContactTemplate die Deklaration aus dem FlipView Bereich übernommen, da der ContentPresenter nur als Platzhalter dient. Dann braucht man Logik. Und die Logik kommt in eine Klasse, hier TempSelector.  Um dieser Klasse die Daten zur Verfügung zu stellen muss man noch ein wenig VB.NET Code tippen und eine Eigenschaft zwischenschalten die Pubic ist, hier ICH. Public Shared ich As _14Scroll ... Public Sub New() InitializeComponent() ich = Me flipview1.ItemTemplateSelector = New TempSelector() flipview1.ItemsSource = {"eins", "zwei"} End Sub Der Templateselector und die Daten wurden zugwiesen so das nun endgültig das VB.NET Beispiel dran ist. Die Klasse erbt von DataTemplateSelector. Public Class TempSelector Inherits DataTemplateSelector Protected Overrides Function SelectTemplateCore(item As Object, container As DependencyObject) As DataTemplate Dim iDT As DataTemplate = _14Scroll.ich.Resources("ImageTemplate") Dim cDT As DataTemplate = _14Scroll.ich.Resources("ContactTemplate") If item = "eins" Then Return iDT Else Return cDT End If End Function End Class

Mehrseitige Bilder wie TIFF und GIF

Irgendwo hab ich gelesen das System.Drawing obsolet sei. Jedenfalls gabs dort eine SelectActiveFrame Methode mit der man ganz einfach ein mehrseitiges Bild in einem Image Control anzeigen kann. Was unter .NET 4 ein Einzeiler ist, braucht in Windows 8 und WinRt ein wenig mehr VB.NET Code Dim pageCount As Integer = 0 Dim f As StorageFile Private Async Function Button_Click_1(sender As Object, e As RoutedEventArgs) As Task If f Is Nothing Then Dim fo As New FileOpenPicker() 'Manifest fileopnepicker fo.SuggestedStartLocation = PickerLocationId.PicturesLibrary fo.ViewMode = PickerViewMode.Thumbnail fo.FileTypeFilter.Clear() fo.FileTypeFilter.Add(".tif") f = Await fo.PickSingleFileAsync() End If Dim fileStream As IRandomAccessStream = Await f.OpenAsync(Windows.Storage.FileAccessMode.Read) Dim imgDecode = Await BitmapDecoder.CreateAsync(BitmapDecoder.TiffDecoderId, fileStream) Dim b1 As BitmapFrame = Await imgDecode.GetFrameAsync(pageCount) Dim pData = Await b1.GetPixelDataAsync Dim ims = New InMemoryRandomAccessStream() Dim imgEncode = Await BitmapEncoder.CreateAsync(BitmapEncoder.TiffEncoderId, ims) imgEncode.SetPixelData( imgDecode.BitmapPixelFormat, imgDecode.BitmapAlphaMode, imgDecode.PixelWidth, imgDecode.PixelHeight, imgDecode.DpiX, imgDecode.DpiY, pData.DetachPixelData() ) Await imgEncode.FlushAsync ims.Seek(0) Dim bi As New BitmapImage() bi.SetSource(ims) bild1.Source = bi pageCount += 1 End Function End Class

Bilder laden und anzeigen als Liste

Manche Dinge sind so einfach, das man nicht darüber nachdenkt bis man sie braucht. In .NET wäre das auflisten aller Bilder ungefähr s zu erledigen. Dim files As String() = Directory.GetFiles("c:\temp",".jpg") .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 und seine WinRT API können aber ungleich mehr und Strom sparen soll man dabei auch noch, weil klassische Festplatten durchaus Stromfresser sind. Vorteil Nummer eins, man kann in den Query Optionen mehrere Dateiendungen und eine Sortierung mitgeben. Wenn eine Abweichende von Default angegeben wird, versucht WinRT den Indexer zu verwenden. Das kann man auch erzwingen per IndexerOption.UseIndexerWhenAvailable. Die Verzeichnistiefe ist per Standard Deep und wird hier per Shallow auf eine Ebene reduziert. Dim ft = {".jpg", ".png"} Dim qo = New QueryOptions(CommonFileQuery.DefaultQuery, ft) qo.FolderDepth = FolderDepth.Shallow Dim itemQuery = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(qo) Dim fi = New FileInformationFactory(itemQuery, ThumbnailMode.DocumentsView) Dim FileListe = Await fi.GetFilesAsync() Liste1.ItemsSource = FileListe In der zweiten Hälfte des Beispiels wird nicht einfach per GetFilesAsync alles geholt, sondern eine Query abgesetzt. Wenn man die Folder haben will, kommt GetFolderquery zum Einsatz. Für Bilder inkl. Unterverzeichnisse nimmt man CreateItemQuery. Beim Binden an eine Liste zeigt letzterer Fall einfach Thumbnails der Subdirectorys an. im XAML wird dann gebunden und per Converter in den passenden Typ (hier BitmapImage) umgewandelt <Image Source="{Binding Path=Thumbnail, Converter={StaticResource thumbnailConverter}}" Die Microsoft Windows 8 Entwickler haben an alles gedacht. Wenn die Liste lange ist wird so was natürlich sehr langsam. Dann kommt der virtualisierte Datei Vektor zum Einsatz. Das Listview Control verwendet intern ohnehin das VirtualizingStackPanel verwendet. In meinem Test war das untere VB.NET Beispiel ein vielfaches schneller. Dim itemQuery = KnownFolders.PicturesLibrary.CreateFileQueryWithOptions(qo) Dim fi = New FileInformationFactory(itemQuery, ThumbnailMode.DocumentsView) Dim FileListe = Await fi.GetFilesAsync() Liste1.ItemsSource = fi.GetVirtualizedFilesVector Zusätzlich kann man auch ein oder mehrere sortier Kriterien über SortEntry einfügen. Auch die Suche in den Metadaten von Dateien ist möglich (PropertyName) Ich empfehle das Beispiel DataSourceAdapter aus dem WinRT SDK.

Windows 8 MessageBox was ein MessageDialog ist

Ja ich weis ich bin bekannt für meine Kenntnisse der deutschen Sprache. Übrigens ein Grund warum ich VB.NET nehme. Eine MessageBox kennt man in Windows 8 und WinRT nicht, nicht einmal einen modalen Dialog. Optisch kommt allerdings der MessageDialog knapp heran. Leider kann man nicht per XAML den Dialog definieren. Der Text wird über das Content Attribut oder bei Initialisierung gesetzt. Der Titel mit der gleichnamigen Eigenschaft.  Der MessageDialog muss per Code mit Elementen gefüllt werden. Möglich sind hier nur Buttons die per UICommand erstellt werden. Die Delegate Funktion kann auch inline als Lambda Prozedur angelegt werden. Darin können dann Werte gesetzt werden und so später im Code die Benutzer Entscheidung verwertet werden. Dim msg As MessageDialog = New MessageDialog(storedFoto.Name) msg.Commands.Add(New UICommand("Wahl A", Sub(command) wert1 = True End Sub)) msg.Commands.Add(New UICommand("Wahl B", Wahlb())) msg.Commands.Add(New UICommand("Wahl C", Nothing, "C")) msg.DefaultCommandIndex = 0 msg.CancelCommandIndex = 1 'ESC Dim ret = Await msg.ShowAsync() .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 einfachere Lösung, mit Rückgabe, ist Variante C, die zur Laufzeit die Auswahl als Eigenschaft ID ausgibt. Dies bezeichnet Microsoft als CommandID. Im VB.NET Beispiel sieht man auch noch wie man den Abbruch per ESC Taste einem Button zuweist und welcher Button auf Return reagieren soll und damit vorbelegt wird.

Windows 8 Standard Web Cam Dialog

Wer ein Foto oder Video in Windows 8 machen möchte, hat die Qual der Wahl. Entweder man erstellt Oberfläche und Code selber oder man nimmt einen Standard Dialog aus WinRT. Der folgende Code setzt voraus das die Anwendung die Capability Webcam, einzustellen im appxmanifest, besitzt. Dim dialog = New CameraCaptureUI() storedFoto = Await dialog.CaptureFileAsync(CameraCaptureUIMode.Photo) Dim msg As MessageDialog = New MessageDialog(storedFoto.Name) Await msg.ShowAsync() .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 Bilder werden automatisch im Anwendungsverzeichnis abgelegt. C:\Users\pre\AppData\Local\Packages\20d2d3c9-68cc-415e-b173-ca571910a20e_89gf582k2a27c\TempState Auch der Standardsettings Dialog lässt sich mit ein wenig VB.NET Code öffnen. Die statische Klasse CameaOptionsUi funktioniert aber nur mir einem laufenden mediacapture. Private mc As MediaCapture .. mc = New MediaCapture() Await mc.InitializeAsync(mcs) .... If mc IsNot Nothing Then CameraOptionsUI.Show(mc) End If .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 Device View States

Eines der größeren Probleme von Windows 8 WinRT, sehe ich bei den unterschiedlichen Ansichten, ein und der selben Seite. Für Snapped, Full, Filled, Portrait und Landscape. Jedes Mal eine andere Sicht innerhalb des selben XAML Files. Da liegt der Schluss nahe, das es Unterstützung im Werkzeug geben muss um die visual states zu verwalten. Expression Blend Device Dialog. Wie die Option Enable State Recording vermuten lässt können auch die Animationen in den Visual States erzeugt und verwaltet werden. Auch in Visual Studio 2012 ist diese Option für Windows 8 METRO style vorhanden. Sie finden den im Menüpunkt DESIGN- Device Window. Auch die verschiedenen Auflösungen lassen sich im Designer simulieren. Der beiliegende Simulator kann über die rechte Toolbar das Gerät um 90 Grad drehen. Im Code wird die bevorzugte Displayausrichtung über Klassen aus dem Namensraum Windows.Graphics.Display gesetzt oder gelöscht (none) DisplayProperties.AutoRotationPreferences = DisplayOrientations.Portrait .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; }