So geht es manchmal:
- Da arbeitet man 4, 8, 12 oder 16 Stunden an einem Modul.
- Alles sieht gut aus, nicht mehr lange und wir können einchecken.
- Du änderst noch dies und das, steckst noch mal 2 Stunden Arbeit rein weil noch was optimiert werden soll und auf einmal merkst Du dass Du Dich verrannt hast. Die letzten 2 Stunden Arbeit hattest Du irgendwie das Gehirn nicht eingeschaltet, x-Änderungen gemacht, die nun alle Sch…sind.
- Undo ist nicht mehr, weil Du schon andere Projekte offen hattest bzw. einmal VS abgeraucht ist.
- Du hast Bockmist gebaut und jetzt willst Du auf den Stand von vor 4 Stunden zurück, oder den von gestern Abend.
- Ein Shelveset hast Du im TFS nicht angelegt. Das machst Du nur wenn Du ins Wochenende gehst, oder Deinen Kollegen was weiterreichen musst.
Was nun?
Als VA-X Benutzer (VisualAssist X http://www.wholetomato.com) hat man tatsächlich noch ein Backup!
Und zwar nicht nur eines, sondern ein paar.
In meinem History Ordner von VA-X
C:\Users\\AppData\Local\Microsoft\VisualStudio\10.0\Extensions\Whole Tomato Software\Visual Assist X\version>\Data\vs10\history\
werde ich fündig…

Ufff…
Ich habe dieses Backup mittlerweile schon so oft verwendet, dass ich dazu übergangen bin diesen Ordner umzulegen an eine Stelle an die ich schneller dran komme. Das geht über den Registry Schlüssel HKCU\Software\Whole Tomato\UserDataDir.
Wer mehr dazu wissen will findet hier weitere Infos:
http://www.wholetomato.com/forum/topic.asp?TOPIC_ID=6865
Copyright © 2010 Martin Richter
Dieser Feed ist nur für den persönlichen, nicht gewerblichen Gebrauch bestimmt. Eine Verwendung dieses Feeds bzw. der hier veröffentlichten Beiträge auf anderen Webseiten bedarf der ausdrücklichen Genehmigung des Autors.
(Digital Fingerprint: bdafe67664ea5aacaab71f8c0a581adf)
Der Schweizer MVP Christian Moser hat eine umfassende Website zum Thema Windows Presentation Foundation (WPF) für Einsteiger zusammengestellt. Wer in einer Woche den Einstieg machen will, sollte sich insbesondere der Eintrag Learn WPF in one Week anschauen.

Das umfassende Online-Werk eignet sich auch für Entwickler, die schon länger in anderen Themenbereichen unterwegs waren, zur schnellen und knackigen Wissensauffrischung. Auch Einsteiger in die Programmierung für Windows Phone finden hier klare, einfach verständliche Worte und Zusammenhänge, hieß doch die Oberflächen-Basistechnologie in ihrem Entwicklungsstadium als WPF-Untermenge noch WPF/E ("Windows Presentation Foundation for Everywhere").
Links:
Was bisher geschah: TDD im Flow – Teil 1 Test #2: Ein Worker entnimmt aus mehreren Queues Der dritte Test in meiner Planung bleibt sinnig. Er führt zu Änderungen am Produktionscode. Den Testcode zu zeigen, lohnt nicht. Er entspricht der Skizze im Bild. Aber hier der Produktionscode: internal class NotifyingMultiQueue{ List>> _queues = new List<
Nach ca. 10 Monaten im Release Candidate (RC) Status ist System Center Advisor (ehemals Codename Microsoft Atlanta) seit gestern öffentlich verfügbar. Eine Information dazu findet sich im Microsoft Server and Cloud Platform Blog und im CSS SQL Server Engineers Blog. Der auf Windows Azure gehostete Cloud Service ermöglicht proaktives Server Configuration Management für SQL Server [...]
Im letzten Teil meiner Serie zu Knockout.js beschäftigen wir uns mit dem Mapping Plugin. In einigen der letzten Beispiele wurde mit JSON gearbeitet und so Daten simuliert, die vom Server stammen. Damit diese an eine View gebunden wurde, musste das ViewModel (welches Observables enthält) manuell aufgefüllt werden. Das Mapping Plugin bietet hier eine Unterstützung, die in diesem Anwendungsfall sehr viel Aufwand spart. Dieser Artikel zeigt wie es funktioniert.
Voraussetzungen
Damit das Mapping-Plugin verwendet werden kann, muss es via github bezogen werden. Hier der Direktlink zur aktuellsten Version. Das Script ist dann natürlich entsprechend einzubinden.
Grundlagen / Wiederholung
In den letzten Teilen dieser Serie wurden alle ViewModels manuell erstellt und sahen - mehr oder weniger - so aus:
var MyViewModel = function() {
this.title = ko.observable();
this.author = ko.observable();
this.pages = ko.observable();
}
Die Daten wurden dann so gesetzt:
myViewModel.title('Ein Buch');
myViewModel.author('Ein Autor');
myViewModel.pages(500);
So musste das für jede Variable vorgenommen werden. Ein recht großer Aufwand, vor allem, wenn die Struktur komplexer wird.
Knockout.mapping kann diesen Aufwand für uns stark minimieren.
Beispiel
Nun aber zu einem Beispiel an Hand dessen wir uns ansehen, wie knockout.mapping funktioniert. Dazu benötigen wir eine View, die mit einer Schaltfläche ausgestattet ist. Bei einem Klick darauf werden JSON-Daten vom Server geladen und an die View gebunden. Hier vorerst die View:
<div id="control">
<input type="button" data-bind="click: loadData" value="Load data"></input>
</div>
<div id="data">
<h2>Loaded Data</h2>
<p>
<span data-bind="text: userName"></span><br/>
<span data-bind="text: tweetCount"></span>
</p>
</div>
Soweit noch nichts Neues, aber wenden wir uns der JavaScript-Seite zu.
$(function(){
var OverallViewModel = function() {
this.loadData = function() {
$.getJSON('http://dl.dropbox.com/u/30654117/Demos/JavaScript/Knockout.js/Mapping/data/data.json', function(data) {
var viewModel = ko.mapping.fromJS(data);
ko.applyBindings(viewModel, document.getElementById('data'));
});
};
}
var firstViewModel = new OverallViewModel();
ko.applyBindings(firstViewModel, document.getElementById('control'));
});
Hier wird ein ViewModel namens OverallViewModel definiert und an das DIV mit der Id control gebunden. Darin enthalten ist die Funktion loadData, welche bei einem Klick auf die Schaltfläche ausgelöst wird.
Darin wird im ersten Schritt das JSON vom Server bezogen. Im Anschluss wird ko.mapping.fromJS mit den Daten als Parameter aufgerufen. Das Ergebnis ist ein ViewModel, welches die entsprechenden Eigenschaften, welche im JSON definiert sind (als Observables) besitzt.
Der Vollständigkeit halber hier das JSON:
{
"userName": "Norbert Eder",
"tweetCount": 10000
}
Das resultierende ViewModel wird nun via ko.applyBindings an das DIV mit der Id data gebunden und die Daten kommen sofort zur Anzeige. Das einfache Beispiel ist fertig. Aber das Plugin kann noch mehr.
Ins Mapping eingreifen
Meine erste Frage - und das wird vermutlich jedem so gehen, war:
So ein einfaches ViewModel ist ganz nett, aber was, wenn ich Funktionen, berechnete Eigenschaften etc. benötige?
Aber auch hierfür gibt es eine entsprechende Lösung.
Erweitern wir obiges Beispiel ein wenig. Zu den rudimentären Tweet-Informationen sollen nun einzelne Tweets hinzugefügt und angezeigt werden. Jeder Eintrag soll mit einer Schaltfläche bestückt werden, mit deren Hilfe ein Retweet durchgeführt werden kann. Auch das sollte nun recht einfach gehen, haben wir uns doch bereits mit Templates beschäftigt. Die Herausforderung ist jedoch, die Tweets mit einem eigenen ViewModel zu versehen, welches eine entsprechende Funktion retweet anbietet, um eben diese Aktion ausführen zu können.
Zuerst aber ein Blick auf die Daten, zwecks Übersicht der durchgeführten Änderungen:
{
"userName": "Norbert Eder",
"tweetCount": 10000,
"tweets": [
{
"content": "Ein Testtweet der mit Sicherheit 140 Zeichen nicht überschreitet."
},
{
"content": "Knockout.js ist schon eine sehr feine Sache!"
},
{
"content": "Bin schon gespannt auf Backbone.js. Soll auch nett sein."
}
]
}
Nun muss die View umgebaut und mit einem Template versehen werden, auch noch nicht spektakulär:
<div id="control2">
<input type="button" data-bind="click: loadData" value="Load data"></input>
</div>
<div id="data2">
<h2>Loaded Data</h2>
<p>
<span data-bind="text: userName"></span><br/>
<span data-bind="text: tweetCount"></span>
</p>
<div data-bind="template: { name: 'tweet-template', foreach: tweets }"></div>
</div>
<script type="text/html" id="tweet-template">
<p data-bind="text: content"/>
<input type="button" value="Retweet" data-bind="click: retweet"/></input>
</script>
Interessant ist das ViewModel, oder besser gesagt, sind die beiden ViewModels, da für die Anzeige eines einzelnen Tweets ein weiteres ViewModel (nennen wir es TweetViewModel) definiert werden muss. Immerhin möchten wir eine Funktion unterbringen. Darin sehen wir nun auch bereits den ersten Trick:
var TweetViewModel = function(data) {
ko.mapping.fromJS(data, {}, this);
this.retweet = function() {
alert("retweeted");
};
}
Die Funktion retweet ist einfach und zeigt hier lediglich einen Alert an. Zumindest kann so die Funktionalität getestet werden. Interessanter ist, dass das ViewModel Daten als Parameter übergeben bekommt und diese via ko.mapping.fromJS auf sich selbst appliziert. Damit werden für die in den Daten enthaltenen Eigenschaften korrespondierende Observables am ViewModel erzeugt. Erster Teil erledigt.
Nun muss noch ein Weg gefunden werden, eine Instanz von TweetViewModel pro Item zu erzeugen. Dazu muss das Haupt-ViewModel ein wenig adaptiert werden.
Hinweis: Dieses wurde mit einem anderen Namen als im einfachen Beispiel zu sehen, da es im gleichen Beispiel zu finden ist, Download siehe weiter unten.
Sehen wir es uns an:
var AnotherViewModel = function() {
this.loadData = function() {
$.getJSON('http://dl.dropbox.com/u/30654117/Demos/JavaScript/Knockout.js/Mapping/data/data2.json', function(data) {
var viewModel = ko.mapping.fromJS(data, mapping);
ko.applyBindings(viewModel, document.getElementById('data2'));
});
};
}
Abgesehen davon, dass die Daten nun von einer anderen Quelle bezogen werden, sticht diese Änderung ins Auge:
var viewModel = ko.mapping.fromJS(data, mapping);
Es wird ein neuer Parameter übergeben. Eine Konfiguration für das Mapping.
var mapping = {
'tweets': {
create: function(options) {
return new TweetViewModel(options.data);
}
}
}
Darin wird ein create-Callback definiert, der sich auf das Array tweets bezieht (siehe Daten weiter oben) und für alle Einträge aus dem tweets-Array (siehe Daten) aufgerufen. Als Parameter werden options übergeben. Diese enthalten:
- data: Ein JavaScript-Objekt mit den Daten
- parent: Das Eltern-Objekt oder das Array zu dem die Daten gehören
Schlussendlich definiert die Zeile
return new TweetViewModel(options.data);
dass eine neue Instanz von TweetViewModel instanziiert werden soll. Die Daten werden als Parameter übergeben und innerhalb des ViewModel weiterverarbeitet (siehe weiter oben).
Das wunderschön gestaltete Ergebnis:

Fazit
Diese beiden Beispiele zeigen sehr schön, dass man sich mit Hilfe des Mapping Plugins jede Menge Schreibarbeit - und damit durchaus auch Fehler - sparen kann. Durch das mögliche Eingreifen in die Erstellung der ViewModels sind alle notwendigen Szenarien vorstellbar und keine Grenzen gesetzt.
Download / Showcase
Getestet werden können die beschriebenen Beispiele hier. Gleich untenstehend der Download des gesamten Paketes:
Feedback
Gerne nehme ich jegliches Feedback zu diesem Beitrag, als auch zur gesamten Serie zu Knockout.js entgegen. Hat dir die Serie geholfen? Hast du Anmerkungen, Anregungen? Fehlt ein Teil, sprich, wurde ein wichtiges Thema nicht besprochen? Teile es mir doch einfach mit und verfasse gleich einen Kommentar.
Mit einem Überblick über die Arbeiten an den ALM-Möglichkeiten der kommenden Entwicklungsumgebung Visual Studio 11 gibt Microsofts Entwicklerabteilungschef Sivaramakrishnan "Soma" Somasegar in seinem Blog einen umfassend bebilderten Einblick in die Visual Studio 11 Developer Preview, und verspricht frühe Nachricht zum Stand des aktuellen Fortschritts an unseren Werkzeugen von morgen. In dem angebotenen Überblick stehen die mit der Preview-Version schon heute evaluierbaren Aspekte Team-Zusammenarbeit, agile Prozesse, Projektmanagement und Feedbackmechanismen, ergänzt um hilfreiche Ressourcen der vergangenen Wochen und Monate zur näheren Einarbeitung in einzelne Details.
PLUS: Soeben wurde das aktuellste englische Visual Studio 11 Developer Preview Training Kit veröffentlicht.
Um eine einfache Trainingsmöglichkeit für den Team Foundation Server 2010 zu bieten werden im msdn Virtual Lab 3 Szenarien zum Team Foundation Server (engl.) als hands-on Lab angeboten. Die virtuellen Labs erfordern keinen großen Download sondern nur en Active-X control im Browser.
Wie bekommt man Daten aus einem lokalen SQL Server in SQL Azure? Mit Tools, wie dem SQL Azure Migration Wizard oder mit dem nigel-nagel-neuen SQL Azure Import/Export Service.

Wie das Windows Azure MSDN Blog verkündet, ging das SQL Azure Import/Export Service heute in den Produktivbetrieb. Der Name ist etwas sperrig – aber beschreibt genau die Funktion.
Announcing SQL Azure Import/Export Service Now in Production
Einige Highlights des Services:
- Verbesserte Leistung
- Verbesserte Connectivity
- Selektiver Export (Neu)
- Fortschritts-Anzeige (Neu)
- Das Service ist nun von CTP in Produktion
- Neue sample EXE
Weitere Details und Beispiel-Videos gibt es im Blog von DAC Guy:
SQL Azure Import/Export Service has hit Production
Das SQL Azure Import/Export Service steht allen Windows Azure Kunden kostenfrei zur Verfügung.
In 10 kostenlosen, deutschsprachigen Hands-on-Lab-Handbüchern für SharePoint 2010 werden die ersten Schritte zur SharePoint 2010-Entwicklung beschrieben. Jedes Lab ist dabei für die Sprachen C# und Visual Basic verfügbar.
Zum Download Center
Das eigentlich für 24.02.2012 mit Steffen geplante Event zu F# wird verschoben.
Einen neuen Termin geben wir an derselben
Stelle bekannt und auf der Startseite der .NET User Group Leipzig dotnet-leipzig.de.
Team Foundation Service ist der TFS in der Cloud. Keine Installation, Ideal für verteilte kleine Teams, ob Scrum, MSF Agile oder CMMI es ist Euer Prozess Ihr könnt wählen. Freunden könnt Ihr ganz einfach Zugriff via Windows Live ID geben und schon geht’s los.
Jetzt unter http://tfspreview.com mit folgendem Code anmelden : “TfsDecUpdate”
Viel Spassss
Steht Flow-Design eigentlich im Gegensatz zu Test-Driven Design? Nein. Zwar bin ich überzeugt, dass TDD viel weniger wichtig ist, als viele Vertreter agiler Softwareentwicklung glauben, aber deshalb hat TDD doch seinen Platz. An einem Beispiel möchte ich das demonstrieren. Vor einiger Zeit habe ich Gedanken zu einer Flow Execution Engine geäußert. Die habe ich inzwischen angefangen zu bauen. Ihr
Heute möchte ich zeigen, wie man sich einen einfachen Style-Switcher mittels jQuery in einem MVC / Razor Projekt einbauen kann.

Den Basis-Style setzen wir zu Beginn, wenn die Seite zum ersten Mal geöffnet wird. Wichtig ist, das wir eine Id setzen, um in unserer jQuery Funktion dieses Element wieder zu finden.
<link id="jQ" href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/base/jquery-ui.css")" rel="Stylesheet" type="text/css" />
Um zwischen den Styles zu wechseln, erstellen wir eine Html-ComboBox mit den Namen der einzelnen Styles:
<select id="styles">
<option value="base">base</option>
<option value="black-tie">black-tie</option>
<option value="blitzer">blitzer</option>
<option value="cupertino">cupertino</option>
<option value="dark-hive">dark-hive</option>
<option value="dot-luv">dot-luv</option>
<option value="eggplant">eggplant</option>
<option value="excite-bike">excite-bike</option>
<option value="flick">flick</option>
<option value="hot-sneaks">hot-sneaks</option>
<option value="humanity">humanity</option>
<option value="le-frog">le-frog</option>
<option value="mint-choc">mint-chocolate</option>
<option value="overcast">overcast</option>
<option value="pepper-grinder">pepper-grinder</option>
<option value="redmond">redmond</option>
<option value="smoothness">smoothness</option>
<option value="south-street">south-street</option>
<option value="start">start</option>
<option value="sunny">sunny</option>
<option value="swanky-purse">swanky-purse</option>
<option value="trontastic">trontastic</option>
<option value="ui-darkness">ui-darkness</option>
<option value="ui-lightness">ui-lightness</option>
<option value="vader">vader</option>
</select>
Als Script verwenden wir folgenden Codeabschnitt. Die Funktion setzt den gewählten Style und ruft eine Methode im Controller auf, der ggfls. den Style in den Usereinstellungen speichern kann.
<script>
$(document).ready(function ()
$('#styles').change(function () {
var style = $(this).val();
$("#jQ").attr("href", "http://ajax.googleapis.com/ajax/libs/jqueryui/1.8/themes/" + style + "/jquery-ui.css");
$.post('@Url.Action("SaveStyle", "Home")', { style: style }, function () {
return false;
});
});
});
</script>
Im Controller dann kann der Name des gewählten Styles weiter verarbeitet werden:
public void SaveStyle(string style)
{
//save style
}
Viel Spaß beim entwickeln : )
Dieser Beitrag stammt von Mario Priebe.

Ähnliche Beiträge
Heute bin ich wieder mal (jedes mal wenn ich das Management Studio neu installiere) über ein bereits seit langem bekanntes Problem gestolpert.
Nachdem ich einer vorhandenen Tabelle einige Felder über den Table Designer hinzugefügt habe und diese Änderungen speichern wollte, habe ich folgende Meldung erhalten:

Verzeihung liebes Management Studio, Nein ich möchte nicht dass du die Tabelle löschst und sie mit der neuen Struktur Neu erstellst.
Da mir aber zu diesem Zeitpunkt wieder mal entfallen war, dass ich dieses Problem früher schon hatte, habe ich mich kurzerhand an meine Timeline in Twitter gewandt.
Dort habe ich prompt, wie fast immer, die richtige Antwort erhalten.
Danke dafür an Sebastian und Norbert, bei den beiden Funktioniert das Gedächtnis wohl noch besser als bei mir.
Und damit ich in Zukunft selbst daran denke, und auch Uwe nachschauen kann wie man das Problem lösen kann, habe ich diesen Beitrag geschrieben.
Und hier nun die Lösung des Problems:
Man kann in den Optionen einstellen, ob das Management Studio diese Vorgehensweise “Warnung ausgeben und das Drop/Create Szenario” anstelle einer “Änderung der Tabellenstruktur”, verwenden soll (Siehe nachfolgende Abbildung).

Vielleicht hilft das hier nicht nur mir und Uwe
Ein Überblick über die kommenden .NET-Konferenzen in Deutschland im Jahr 2012.
C++ ist noch lange nicht tod! Microsoft hat in den letzten Jahren wieder mehr Resourcen in diesen Bereich investiert. Um wieder mehr auf diesen Bereich aufmerksam zu machen bzw. sich mal weider zu Informieren, was es alles gibt, solltest Du unbedingt auf einen der C++ Days gehen. Das beste dabei ist, das Ganze ist kostenlos! Also, gleich Anmelden:
2.2.2012 14:00- 18:00 Berlin (ausgebucht)
- 7.2.2012 14:00- 18:00 Bad Homburg (noch freie Plätze!)
- 13.2.2012 14:00- 18:00 Karlsruhe (noch freie Plätze!)
5.3.2012 14:00- 18:00 Köln (ausgebucht)
Mehr Infos unter:
http://blogs.msdn.com/b/cbinder/archive/2011/12/29/c-entwickler-uptodate-microsoft-c-day-2012.aspx
In meinem Neujahrspost habe ich schon angedeutet, dass ich dieses Jahr wohl viel unterwegs sein werde und so steht jetzt fest, dass ich im Mai auf der .Net Dev Con zu MS Test sprechen werde.

Dort werde ich unter dem Titel: “MS Test - der missverstandene Stiefbruder” im Grunde das auseinander nehmen, was ich die letzten zwei Jahre im Umgang mit der Visual Studio Test Integration und weitestgehend freien Alternativen erlebt habe. Genauer geht es mir dabei um die Lücke zwischen Unit Tests mit NUnit, Moq und Resharper, gegenüber den System- und Integrationstests mit Visual Studio, samt Pex und Moles.
Ich habe dabei nicht vor auf irgend welchen Designschwächen von was auch immer welchen Tools herum zu hacken. Viel mehr geht es mir darum die unterschiedlichen Sichtweisen gegenüber zu stellen die hinter den einzelnen Frameworks stehen, deren wichtigste Features zu erläutern und die Gründe zu nennen warum sie verwendet werden. Ob sie ihre Arbeit gut machen muss dann jeder selbst entscheiden.
Ich hoffe also auf eine rege Teilnahme und freue mich auf jeden den ich am 15. Mai willkommen heißen darf.
In Jetzt Knockout.js lernen: Observables erweitern der Serie zu Knockout.js wurde gezeigt, wie Observables erweitert werden können. Als Beispiel diente eine Validierung auf Basis von einzelnen Observables. Nun hängen diese in der Regel aber in einem Formular zusammen, wodurch eine Gesamtvalidierung und eine damit verbundene Freischaltung von Schaltflächen bzw. weiteren Interaktionsmöglichkeiten einhergeht. Dieser Beitrag greift das Validierungsbeispiel auf und erweitert es im Rahmen dieser Anforderung.
Ausgangspunkt
Den Ausgangspunkt dieses Beitrags bietet dieses Beispiel, zu dem Florian einen sinnvollen Erweiterungswunsch hinterlassen hat:
Interessant wäre noch ein Save-Button, der nur aktiv ist, wenn es keine Fehler auf dem gesamten Formular mehr gibt, sowie ein Cancel- bzw. Undo-Button der bei eventuellen Fehlern, die Werte zurücksetzt auf den ursprünglichen Wert.
Aus meiner Sicht handelt es sich hierbei um eine gängige Anforderung, die ich natürlich aufgegriffen habe. Wer den Hintergrund des Beispiels erfahren möchte, kann dies hier vollständig nachlesen. Als Zusammenfassung: Das Beispiel zeigt, wie ein eigener Extender für Observables unter Knockout.js geschrieben werden kann, um einzelne Observables zu validieren. Als Validierung wurde eine Erweiterung hinsichtlich Pflichtfelder implementiert. Eine Gesamtvalidierung war nicht vorgesehen.
Gesamtvalidierung
Aktuell wird jedes Observable einzeln validiert. Entsprechend der Anforderung sollen nun zwei Schaltflächen für das Speichern, als auch das Zurückstellen auf die zuletzt gültigen Werte (im Fehlerfalle) hinzugekommen. Damit diese auch entsprechend freigeschalten sind, muss das ViewModel bekannt geben, in welchem Status es sich gesamt befindet. Dieser ergibt sich aus den Status jedes einzelnen Observables. Dies können wir uns berechnen lassen (ko.computed) und nennen wir hasErrors.
self.hasErrors = ko.computed(function()
{
return self.title.hasError() ||
self.author.hasError() ||
self.pages.hasError();
});
Hinweis: self ist eine Referenz auf this und wurde im Vergleich zum originalen Beispiel hinzugefügt, um innerhalb der anonymen Funktion auf die Eigenschaften und Funktionen des ViewModels zugreifen zu können.
Damit könnten nun auch bereits die beiden neuen Schaltflächen gesteuert werden:
<input type="button" value="Save" data-bind="disable: hasErrors"></input>
<input type="button" value="Undo" data-bind="enable: hasErrors"></input>
Auf die Speichern-Schaltfläche möchte ich an dieser Stelle nicht weiter eingehen, viel spannender ist hier der Undo-Button. Dieser soll ja - im Fehlerfalle - die zuletzt gültigen Werte hinterlegen. Dies bedeutet, dass diese erfasst werden müssen.
Nun ist es so, dass die einzelnen Eingabefelder eine value-Bindung besitzen und das Durchschreiben der Werte via valueUpdate auf den Wert afterkeydown gesetzt wurde. Das ViewModel wird also in Echtzeit aktualisiert. Darauf können wir also nicht aufbauen, da der Benutzer beispielsweise per Backspace alle Zeichen von “Beispiel” löschen könnte, durch die Echtzeit-Aktualisierung würde der zuletzt valide Wert durch “B” repräsentiert. Worauf wir uns jedoch hängen können ist das Verlassen des Fokus, also dem OnBlur-Ereignis.
Dazu brauchen wir eine Funktion, die uns den beim Verlassen vorhandenen - gültigen - Wert übernimmt:
self.updateLastValidValue = function(observable) {
if (!observable.hasError()) {
observable.lastValidValue(observable());
}
};
Als Parameter wird das an das Eingabefeld gebundene Observable übergeben. So kann auch auf die durch den Extender geschriebenen Eigenschaften (siehe hasError) zugegriffen werden. Läuft die Validierung ohne Fehler, dann ist ein entsprechender Wert vorhanden und kann übernommen werden. Hierzu wird eine neue Eigenschaft namens lastValidValue mit dem Wert gesetzt.
Damit dies nun bei allen Eingabefeldern berücksichtigt wird, ist deren Bindung zu aktualisieren, hier an Hand der Titel-Eingabe:
<input data-bind='value: title, valueUpdate: "afterkeydown", event: { blur: function() { updateLastValidValue(title); }}' />
So werden die Werte nun weiterhin in Echtzeit übernommen und zusätzlich beim Verlassen des Feldes die Funktion updateLastValidValue des ViewModels aufgerufen. Das ist allerdings noch nicht alles, denn ein etwaiger initial gesetzter Wert würde nicht berücksichtig werden. An dieser Stelle bietet es sich an, den bereits existierenden Extender (requireField) zu erweitern:
ko.extenders.requiredField = function(target, message) {
target.hasError = ko.observable();
target.validationMessage = ko.observable();
target.lastValidValue = ko.observable();
function validate(newValue) {
target.hasError(newValue ? false : true);
target.validationMessage(newValue ? "" : message || "* required");
}
function setInitialValueIfValid() {
if (!target.hasError()) {
target.lastValidValue(target());
}
}
validate(target());
setInitialValueIfValid();
target.subscribe(validate);
return target;
};
Neu ist die Funktion setInitialValueIfValid(). Diese wird beim Erstellen von requiredField durchlaufen (siehe den Aufruf direkt nach der initialen Validierung) und, wenn ein Wert vorhanden ist, lastValidValue mit dem entsprechenden Wert beschrieben.
Zu guter Letzt fehlt noch das tatsächliche Zurückschreiben der Werte, wenn die Undo-Schaltfläche geklickt wird. Dazu benötigen wir eine Funktion, nennen wir sie resetToValidValues:
self.resetToValidValues = function() {
self.title(self.title.lastValidValue());
self.author(self.author.lastValidValue());
self.pages(self.pages.lastValidValue());
};
Außerdem muss die click-Bindung auf der Schaltfläche gesetzt werden:
<input type="button" value="Undo" data-bind="enable: hasErrors, click: resetToValidValues"></input>
Fertig ist die Gesamtvalidierung des Formulars, inklusive der Möglichkeit, die zuletzt gültigen Werte zurück zu schreiben.
Ergebnis
An dieser Stelle nun zwei Screenshots, die die Implementierung visuell veranschaulichen. Der erste Screenshot zeigt das gesamte Formular ohne Validierungsfehler:

Und hier nun im Fehlerfalle:

Download / Showcase
Wie immer gibt es auch dieses Beispiel vollständig für eigene Tests. Unter http://jsfiddle.net/VgvuK/ findet sich die entsprechende Spielwiese. Wer möchte, kann sich dies auch unterhalb ansehen, sollte hierbei allerdings auf die Nutzung eines IE verzichten:
Fazit
Die notwendigen Erweiterungen der “Feld-Validierung” konnte in meinen Augen mit sehr wenig Aufwand auf eine Gesamtvalidierung erweitert werden, wodurch für den Benutzer sehr schnell ersichtlich ist, welche Aktionen zur Verfügung stellen. Ein äußerst sauberer Weg, durch die mögliche Auftrennung.
Feedback
Gerne nehme ich - wie immer - konstruktive Kritik, Anregungen und Anmerkungen entgegen. Einfach einen kurzen Kommentar hinterlassen, es findet sich sicherlich die eine oder andere Verbesserungsmöglichkeit.
Soo, nachdem ich die Überschrift mit all den hippen Begriffen vollgepackt habe die es in Sachen Web momentan so gibt möchte ich euch kurz erklären worum es in diesem Artikel gehen soll.
Ihr werdet sicher mitbekommen haben, dass HTML5 auf dem Vormarsch ist und auch wenn es immer noch nicht offiziell fertig ist findet man immer häufiger den HTML5-Header <!DOCTYPE html> auf diversen Seiten.
Eins der meiner Meinung nach nützlichsten Features die wir mit HTML5 erhalten sind die neuen Input-Typen. Denn bisher hatten wir keine Wahl und mussten type="text" benutzen!
Die Eingabefelder vom Typ Text konnten natürlich alles enthalten, sind aber nicht gerade Benutzerfreundlich wenn man etwas komplexere Daten eingeben soll wie zum Beispiel ein Datum.
In HTML5 wurden deswegen spezifische Typen für solche immer wieder einkehrende Eingaben eingeführt. Eine von ihnen ist "Date":
Birthday: <input type="date" name="bday" />
Der große Vorteil hier ist, dass die Browser nun eine eigene Implementierung für diesen Typ von Eingabefeldern machen können. Falls der Browser nun also diesen neuen Eingabe Typ unterstützt blendet er automatisch einem (mehr oder weniger) schönen Date-Picker ein in dem man bequem das Datum wählen kann:

Ansicht des neuen Typen in verschiedenen Browsern
Ein weiterer großer Vorteil ist, dass auch die mobilen Browser wie z.B. auf dem iPhone diese neuen Elemente ebenfalls unterstützen und einen passenden Picker einblenden:

Date-Picker auf dem iPhone
Wie ihr in dem ersten Screenshot sehen könnt unterstützt der FireFox (zumindest zum Zeitpunkt des Screenshots) den neuen Typ nicht. In diesem Fall blendet er ein gewöhnliches Textfeld ein wo wir nun gezwungen sind das Datum auf die gewohnte (unbequeme) Weise einzutragen.
Übrigens, ich glaube ich muss euch nicht sagen warum der Internet Explorer hier nicht mit aufgeführt ist oder?
Und genau das ist das Problem um das es in diesem Blogpost geht. Wenn wir die tollen neuen Elemente nutzen gehen wir das Risiko ein, dass Nutzer die einen älteren Browser nutzen nicht in den Genuss einer bequemen Eingabe kommen.

jQuery Date-Picker
Wir könnten auch komplett auf das HTML5 Element verzichten und direkt alles mit jQuery machen, welches auch einen Date-Picker bietet, aber das ist auch nicht das gelbe vom Ei, denn dann wären die Mobilen Nutzer in Nachteil, da dort die Browser diese Elemente für gewöhnlich unterstützen und eine gewohnte Oberfläche für die Eingabe bieten.
Die Optimale Lösung ist offensichtlich eine Mischung beider Welten. Wenn der Benutzer die Seite mit einem kompatiblen Browser aufruft soll der Date-Picker des Browsers genutzt werden, wenn der Browser aber veraltet ist soll stattdessen der jQuery Fallback greifen und der JavaScript Date-Picker (siehe links) genutzt werden.
Zu unserem Glück macht uns ASP.NET MVC die Umsetzung dieses Plans sehr leicht, da es an den nötigen Stellen sehr einfach erweitert werden kann.
Schauen wir uns mal ein kleines Beispiel an. Wir haben eine Klasse Person, und wollen auch dessen Geburtstag speichern, hier wollen wir die tolle neue Funktionalität nutzen. Implementieren wir das Ganze jedoch erstmal wie gewohnt:
public class Person
{
public int ID { get; set; }
public String Name { get; set; }
public DateTime Geburtstag { get; set; }
}
Wenn wir die Model-Klasse haben können wir das MVC Scaffolding nutzen um für uns eine View zum Erstellen neuer Personen anzulegen:

MVC Scaffolding kommt uns zu Hilfe
Schauen wir uns nun doch mal an was da für uns tolles generiert wurde:
<div class="editor-field">
@Html.EditorFor(model => model.Geburtstag)
@Html.ValidationMessageFor(model => model.Geburtstag)
</div>
Auffällig hierbei ist, dass hier @Html.EditorFor genutzt wird und nicht etwa @Html.TextBoxFor! Der Hintergrund ist der, dass MVC bei der Generierung versuchen wird ein passendes Element für den angegebenen Datentyp (DateTime) zu bestimmen, aber da MVC standardmäßig nur den Typ Text kennt wird hier eigentlich immer ein Input-Element vom Typ text generiert.
Hier kommt uns die Erweiterbarkeit von MVC zu gute, denn wir können ganz einfach NuGet nutzen um uns die nötige Funktionalität zu verschaffen. Wir installieren das Packet MvcHtml5Templates.

Installation über NuGet

Neue Templates
Nachdem NuGet alles erledigt hat werdet ihr feststellen, dass ihr ein paar neue Dateien in eurem Views/Shared Verzeichnis habt (siehe Bild links). Und wie ihr schon an dem Namen erkennen könnt sind das die neuen Input-Typen aus HTML5.
Diese Templates machen im Grunde nichts anderes als die MVC-Eigenen zu überschreiben und versehen die mit dem passendem Type-Attribut.
Hier sind euch keine Grenzen gesetzt ihr könnt natürlich auch eire eigenen Templates definieren mit euren eigenen Typen. (Auch wenn man das eher selten benötigt)
Das Tolle: mehr müssen wir nicht machen. MVC wird nun zur Laufzeit statt einem Text ein Date Input-Element für uns anlegen. Beachtet, dass das nur geht wenn wir in der View die allgemeine Funktion @Html.EditorFor nutzen und keinen Spezifischen Typ angeben.
Schauen wir mal in den Sourcecode der zur Laufzeit für das Geburtstagsfeld generiert wird:
<input class="text-box single-line"
data-val="true"
data-val-required="Das Feld 'Geburtstag' ist erforderlich"
id="Geburtstag"
name="Geburtstag"
type="datetime"
value=""; />
Wie ihr seht wird nun der Korrekte Typ, nämlich datetime verwendet. Wenn wir die Seite nun also mit einem kompatiblen Browser aufrufen können wir ganz bequem das Datum wählen.
Leider ist der Opera Browser momentan wohl der einzige Desktop Browser der uns hier einen Benutzerfreundlichen Dialog einblendet (siehe erstes Bild oben), somit müssen wir dafür sorgen, dass die Benutzer mit anderen oder alten Browsern nicht benachteiligt werden.

JavaScript Magie
Auch für diese Problematik bringt MVC bereits alles mit was nötig ist sie zu lösen. Werfen wir doch mal einen Blick in unseren Scripts Ordner finden wir alle nötigen jQuery und jQuery UI Skripte, dazu kommt noch die modernizr Bibliothek die uns auch zu Gute kommen wird.
Die jQuery Bibliotheken liefern und die nötige Funktionalität die wir benötigen um diesen hübschen Date-Picker einzublenden.
Die modernizr Bibliothek dagegen hilft uns herauszufinden ob der Browser, den der Benutzer momentan verwendet die gewünschten HTML5 Features unterstützt.
Nun müssen wir also bei unseren Formularen prüfen, ob der Browser die nötigen Funktionen kennt und bei Bedarf die jQuery Klassen einbinden. Das Ganze ist dank der Einfachheit von Modernizr und jQuery ziemlich simpel.
Wechseln wir zu unserer View und fügen die Referenzen auf die nötigen Skripte und CSS Dateien ein:
<script src="@Url.Content("~/Scripts/jquery-1.5.1.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-1.7.js")" type="text/javascript"></script>
<link href="@Url.Content("~/Content/themes/base/jquery.ui.all.css")" rel="stylesheet" type="text/css" />
Unten in der View fügen wir nun eine Modernizr Abfrage ein und prüfen, ob der Browser das DateTime-Feld unterstützt, und wenn nicht lassen wir jQuery das Ganze für uns regeln:
<script type="text/javascript">
$(function () {
if (!Modernizr.inputtypes.date) {
$("input[type='datetime']").datepicker();
}
});
</script>
Mit dem Befehl $("input[type='datetime']").datepicker(); werden alle Input-Felder vom Typ DateTime durch den jQuery Picker ersetzt. Coole Sache!

Unser Browser kennt den Typ nicht
Kleiner Hinweis am Rande: Das ist nicht wirklich best practice da die Skripte auch geladen werden wenn sie gar nicht benötigt werden (der Browser kennt das HTML5 Feld) ich habe das hier der Einfachheit geopfert.
Und das wars auch schon! Alle Browser die das Feld unterstützen blenden nun ihren Picker ein, ansonsten wird der jQuery Fallback genutzt.
Wir müssen uns auch nicht mit irgendwelchen UserAgent-Prüfungen herumschlagen und lassen das alles Modernizr erledigen, der das intern über JavaScript prüft und somit immer aktuell ist. Das heißt dass wenn irgendwann der IE plötzlich die neuen Typen kennt werden diese auch funktionieren!
Genauso könnt ihr auch bei all den anderen neuen HTML5 Typen vorgehen und euren Usern ein bestmögliches Bedienerlebnis bereiten


Die Windows Azure Produktgruppe hat bekanntgegeben, dass ab sofort der SQL Azure Import/Export Service allgemein produktiv verfügbar ist. Mit diesem Dienst ist es sehr leicht möglich, bestehende lokal betriebene Datenbanken leicht in die Cloud auf SQL Azure zu bringen und existierende SQL Azure und SQL Server Datenbanken problemlos im Windows Azure Blob Storage zu archivieren.
Mit der Produktivsetzung des SQL Azure Import/Export Service werden auch einige Verbesserungen am Dienst verfügbar:
Der Dienst steht kostenlos zur Verfügung. Es fallen lediglich die üblichen Kosten für Datentransfer und Blob Storage (für die Ablage der archivierten Datenbanken) an.
Weitere Informationen
GoingNative ist eine 2-tägige Konferenz in Redmond vom 2.-3.2. zu C++ im Kontext der C++ Renaissance. Sprecher sind unter anderem Bjarne Stroustrup (C++ Creator, Texas A&M), Herb Sutter (ISO C++ Chair, Microsoft). Die Session STL11 vom MS Standard Library Maintainer Stephan T. Lavavej (STL) zeigt zum Beispiel die Evolution von Class Templates aus Boost nach C++11.
Agenda
Es gibt einen Live Stream zu den Sessions am 2.2, 3.2. (allerdings mit CET -9 Stunden) und als Recording jeweils in <24h danach.
http://channel9.msdn.com/Events/GoingNative/GoingNative-2012
Twitter Bootstrap, ein UI-Toolkit für Web-Applikationen von Twitter, erscheint (wie bereits berichtet) demnächst in der Version 2.0.
Der offizielle Release ist am 31. Januar, allerdings beginnt jetzt laut Mark Otto (einer der Hauptentwickler von Twitter Bootstrap) die intensive Test-Phase. Das heisst, das es nun offiziel auch die 2.0 Dokumentation online gibt.
Im Vergleich zur aktuellen Version 1.4 gibt es einige neue Controls, Javascripts und das Template wurde auch für Mobile Geräte und Tablets angepasst (Stichwort “Responsive Design”).
Wer mehr erfahren will – Mark Ottos Post “Bootstrap 2 ready for testing and feedback”
Am Mittwoch, den 25.01.2012 findet das nächste Treffen der .NET Developer Group Braunschweig
statt. Lars und
ich halten einen Vortrag über Windows 8 und was es Wichtiges aus Entwicklersicht gibt.
Seit September 2011 gibt es die erste Preview Version von Windows 8 und die Beta steht
bereits vor der Tür. So langsam wird es Zeit sich mit den Neuerungen und Änderungen
des neuen Betriebsystems zu beschäftigen. Mit der neuen Metro-Welt für Tablet-PCs
führt Microsoft auch einen neuen App Store ein. Wie auch schon beim Window Phone 7
ergibt sich hier für Entwickler ein neuer Markt für die Vertreibung der eigenen Software.
Lars Keller und Karim El Jed machen einen kleinen Rundflug durch die neue Windows
8 Welt und zeigen wie man eine Metro-Applikation mit der Windows Runtime entwickeln
kann.
Weitere Infos unter: http://www.dotnet-braunschweig.de
Im folgenden eine kleine Übersicht von Google API jQuery UI 1.7.2 Designs, so vorbereitet, dass diese schnell im Header eines Razor Layouts eingefügt werden können.
base
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/base/jquery-ui.css")" rel="Stylesheet" type="text/css" />

black-tie
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/black-tie/jquery-ui.css")" rel="Stylesheet" type="text/css" />

blitzer
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/blitzer/jquery-ui.css")" rel="Stylesheet" type="text/css" />

cupertino
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/cupertino/jquery-ui.css")" rel="Stylesheet" type="text/css" />

dark-hive
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/dark-hive/jquery-ui.css")" rel="Stylesheet" type="text/css" />

dot-luv
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/dot-luv/jquery-ui.css")" rel="Stylesheet" type="text/css" />

eggplant
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/eggplant/jquery-ui.css")" rel="Stylesheet" type="text/css" />

excite-bike
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/excite-bike/jquery-ui.css")" rel="Stylesheet" type="text/css" />

flick
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/flick/jquery-ui.css")" rel="Stylesheet" type="text/css" />

hot-sneaks
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/hot-sneaks/jquery-ui.css")" rel="Stylesheet" type="text/css" />

humanity
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/humanity/jquery-ui.css")" rel="Stylesheet" type="text/css" />

le-frog
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/le-frog/jquery-ui.css")" rel="Stylesheet" type="text/css" />

mint-chocolate
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/mint-choc/jquery-ui.css")" rel="Stylesheet" type="text/css" />

overcast
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/overcast/jquery-ui.css")" rel="Stylesheet" type="text/css" />

pepper-grinder
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/pepper-grinder/jquery-ui.css")" rel="Stylesheet" type="text/css" />

redmond
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/redmond/jquery-ui.css")" rel="Stylesheet" type="text/css" />

smoothness
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/smoothness/jquery-ui.css")" rel="Stylesheet" type="text/css" />

south-street
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/south-street/jquery-ui.css")" rel="Stylesheet" type="text/css" />

start
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/start/jquery-ui.css")" rel="Stylesheet" type="text/css" />

sunny
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/sunny/jquery-ui.css")" rel="Stylesheet" type="text/css" />

swanky-purse
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/swanky-purse/jquery-ui.css")" rel="Stylesheet" type="text/css" />

trontastic
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/trontastic/jquery-ui.css")" rel="Stylesheet" type="text/css" />

ui-darkness
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/ui-darkness/jquery-ui.css")" rel="Stylesheet" type="text/css" />

ui-lightness
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/ui-lightness/jquery-ui.css")" rel="Stylesheet" type="text/css" />

vader
<link href="@Url.Content("http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/themes/vader/jquery-ui.css")" rel="Stylesheet" type="text/css" />

Dieser Beitrag stammt von Mario Priebe.

Ähnliche Beiträge
Im vergangenen Teil der Serie zu Knockout.js wurden benutzerdefinierte Bindungen behandelt. Dieser Teil beschäftigt sich nun damit, wie Observables auf eigene Bedürfnisse hin erweitert werden können. Knockout.js unterstützt das Lesen und Schreiben von Werten, als auch Benachrichtigungen bei Wertänderungen. In zahlreichen Fällen (zum Beispiel Validierung) möchte man dieses Verhalten jedoch erweitern. Dieser Beitrag zeigt, wie dies umgesetzt werden kann.
Extender zur Validierung erstellen
Knockout.js bietet mit Extendern eine Erweiterungsmöglichkeit an, sich an Observables zu hängen. Das nachfolgende Beispiel erweitert Observables durch die Möglichkeit einer Validierung.
Im ersten Schritt erstellen wir ein einfaches ViewModel, welches an eine View gebunden wird. Hier das ViewModel:
function BookViewModel(title, author, pages) {
this.title = ko.observable(title).extend({ requiredField: "Please enter a title" });
this.author = ko.observable(author).extend({ requiredField: "Please enter an author" });
this.pages = ko.observable(pages).extend({ requiredField: "" });
}
var viewModel = new BookViewModel("Windows Presentation Foundation 4.5 - Einführung und Praxis", "Norbert Eder", 400);
ko.applyBindings(viewModel);
Und passend dazu die View:
<div id="bookform">
<h3>Title</h3>
<p>
<input data-bind='value: title' />
</p>
<h3>Author</h3>
<p>
<input data-bind='value: author' />
</p>
<h3>Pages</h3>
<p>
<input data-bind='value: pages' />
</p>
</div>
Soweit nichts Neues. Nun möchten wir aber, dass eingegebene Werte validiert werden. In diesem Fall beschränke ich mich darauf, Felder als benötigt zu definieren. Ein Extender wird daher benötigt. Um einen Extender zu schreiben muss ko.extenders um eine Funktion erweitert werden, nennen wir sie requiredField. Diese enthält als Parameter das Ziel der Erweiterung und eine Nachricht (die für die Anzeige einer Meldung verwendet werden kann).
ko.extenders.requiredField = function(target, message) {
target.hasError = ko.observable();
target.validationMessage = ko.observable();
function validate(newValue) {
target.hasError(newValue ? false : true);
target.validationMessage(newValue ? "" : message || "* required");
}
validate(target());
target.subscribe(validate);
return target;
};
Der Extender hängt dem Ziel zwei Eigenschaften hasError und validationMessage um. Die erste beschreibt, ob ein Validierungsfehler vorliegt, die zweite, welcher Fehler dies genau ist. Zusätzlich wird eine Funktion validate benötigt, welche die Validierung schlussendlich durchführt. Dies ist in diesem Beispiel recht einfach. So muss lediglich auf Vorhandensein eines Wertes geprüft werden. Ist dies nicht der Fall wird die über den Parameter message übergegebene Meldung angezeigt - wurde keine eigene Meldung übergeben, wird ein Fallback auf eine Standardmeldung verwendet.
Via validate(target()); wird eine initiale Validierung durchgeführt. Per target.subscribe(validate); wird ein Abonnement hinsichtlich Wertänderungen hinzugefügt, damit auch in diesen Fällen die Validierung durchgeführt wird.
Anschließend wird das ursprüngliche Observable zurück geliefert. Das war alles, was auf JavaScript-Seite zu implementieren war. Nun ist noch die View anzupassen, um etwaige Meldungen sichtbar zu machen.
<div id="bookform">
<h3>Title</h3>
<p data-bind="css: { error: title.hasError }">
<input data-bind='value: title, valueUpdate: "afterkeydown"' />
<span data-bind='visible: title.hasError, text: title.validationMessage'> </span>
</p>
<h3>Author</h3>
<p data-bind="css: { error: author.hasError }">
<input data-bind='value: author, valueUpdate: "afterkeydown"' />
<span data-bind='visible: author.hasError, text: author.validationMessage'> </span>
</p>
<h3>Pages</h3>
<p data-bind="css: { error: pages.hasError }">
<input data-bind='value: pages, valueUpdate: "afterkeydown"' />
<span data-bind='visible: pages.hasError, text: pages.validationMessage'> </span>
</p>
</div>
Für die Erweiterung der View wurden insgesamt drei Schritte unternommen:
- Für die input-Felder wird die Aktualisierung des Wertes im ViewModel durch valueUpdate auf afterkeydown geändert, damit jede Änderung sofort im ViewModel wirksam wird und so schon zur Eingabe eine Validierung stattfinden kann.
- Es wird ein span-Element eingeführt. Dieses enthält eine visible-Bindung auf die Eigenschaft hasError - ist also nur sichtbar, wenn ein Validierungsfehler aufgetreten ist. Der anzuzeigende Wert des span-Elementes wird auf die validationMessage gebunden.
- Die umschließenden p-Tags wurden um eine css-Bindung erweitert. Dadurch wird bei Vorhandensein eines Validierungsfehlers eine CSS-Klasse error auf das Element gehängt, wodurch ein Styling einfach gemacht wird. So sieht es aus:
Download / Showcase
Das Beispiel kann unter http://jsfiddle.net/zHFEU/ getestet und bezogen werden. Untenstehend findet sich das JSFiddle in eingebundener Form, benötigt aber einen Browser ungleich IE.
Fazit
Dieses Beispiel hat gezeigt, wie einfach es möglich ist, ein Verhalten á la Validierung per Knockout.js und Extender zu realisieren. Auch hier gilt wieder, dass die Zuständigkeiten sauber getrennt sind und einfach gewartet werden.
Wie werden fast&fluid Client Anwendungen, welche das User Interface nicht blockieren, in .NET erstellt? Wie kann in server-seitigen Anwendungen mit Worker Threads das Ausschöpfen des Thread Pools verhindert werden? Der Webcast gibt eine Übersicht zur asynchronen Programmierung mittels C# async/await, sowie dem Task-based Asynchronous Pattern in .NET. Topics sind:
- Synchrone Programmierung: Client UI-und server-seitige Problemstellungen
- Asynchrone Programmiermodelle in .NET
- async/await in .NET 4.5
- Erstellung eines async/await Beispiels
- Tips und Caveats
- Asynchrone Programmierung relativ zu paralleler Programmierung
- Dataflow Unterstützung in .NET
Anmeldung: http://www.katapult.tv/
Am 26.01.2012 um 18:00 Uhr findet das nächste Treffen der .NET Usergroup Karlsruhe (XING-Gruppe der .NET Usergroup Karlsruhe) statt.
Thema
Know your Tools
Details zum Thema
Alternativen zu den klassischen Tools
Im Zentrum steht natürlich VisualStudio 2010
- was bringt VS 2012?
- welche Alternativen gibt es ?
- Add-Ons, die im Allgemeinen verwendet werden
- Zielplattformen, die mit .NET bedient werden können (WP7, iPhone, Android, Metro, Linux, OSX)
- UI-Technologien und Tools (WPF, Silverlight, Metro, GTK, Android, ...)
- Offene Diskussion, es besteht auch die Möglichkeit ,dass jeder seine Tools vorstellen kann
Über den Sprecher
Frank Pfattheicher, Alexander Zeiter und möglichst viele aus der Usergroup 
Teilnahme
Bitte meldet Euch wieder via XING an, die Location ist wieder DJK-Ost:
DJK-Ost
Friedrichstaler Allee 52
76131 Karlsruhe
Anfahrt: http://www.djk-ost.de/html/vereinsheim.html
Es freut mich heute einige neue How-To-Guides unserer Video-Reihe Ihr-Unternehmen-in-der-Cloud.de vorstellen zu dürfen.
Zielgerichtet in einzelnen Video-Kapiteln, oder kompakt in 1 ½ Stunden, erfahren Sie, wie man Windows Intune, Office 365 und Dynamics CRM Online von der ersten Sekunde an nutzen kann. Es wird gezeigt, wie man sich für die kostenlosen und unverbindlichen Testversionen registriert, die ersten Schritte mit dem jeweiligen Produkten und Services macht und von der Testversion in ein Produktivsystem übergeht. Verschaffen Sie sich so einen umfangreichen Überblick über die Cloud-Angebote von Microsoft für kleine und mittelständische Unternehmen.
Aufgegliedert nach verschiedenen Produkten & Kapiteln... [... mehr in diesem Blogeintrag auf Giza-Blog.de]
This post is powered by
www.Giza-Blog.de |
©
Copyright 2006-2011 Kay Giza. All rights reserved.
Legal
Den Projektfortschritt zu Überwachen ist eine der wichtigsten Aufgaben eines Projektmanagers. Die Messung des Projektfortschritts findet dabei auf Basis von eingetragenen Arbeitszeiten für die definierten Aufgaben statt. Im TFS werden Aufgaben in Form von Work Items abgebildet und verwaltet. Doch wie kommen die Daten in die Work Items? Geht man nach der Produktdokumentation, öffnet man die Aufgabe zum Beispiel im Visual Studio und trägt dort die Zeiten ein – das sollte natürlich am besten nach jeder abgeschlossenen Aufgabe passieren. Leider passiert es allzu häufig, dass dies schlichtweg vergessen wird. Kennzahlen und Bericht, wie zum Beispiel der Burn-Down-Report für den Projektfortschritt lassen sich dann nur unvollständig generieren und werden nutzlos. Für Entwicklungsaufgaben gibt es nun Abhilfe…
Eine der Stärken von TFS ist die Verknüpfung von Code mit Aufgaben um eine lückenlose Nachverfolgbarkeit (Traceability) zwischen Code-Änderungen und den Aufgaben und Anforderungen zu ermöglichen. Die Funktion zur Verknüpfung zwischen Code und Arbeitsaufgabe ist beim Team Explorer (Visual Studio) bereits im Standard-Eincheckdialog integriert:

Bild 1: Eintragen eines Kommentares

Bild 2: Verknüpfung mit einem Work Item
Als Entwickler ist die zuvor beschriebene Verknüpfung zwischen Eincheckvorgang und Work Item optional. Dies kann mithilfe einer Checkin-Policy in einen verpflichtenden Schritt umgestellt werden. Als Projektmanager haben Sie damit schon einen Teil ihrer Anforderungen, und zwar die Nachverfolgbarkeit von Code und Aufgaben erfüllt. Ein wichtiger Punkt hat uns als AIT an dieser Stelle aber in der Vergangenheit in eigenen Entwicklungsprojekten gefehlt. Die Problemstellung war, dass die Work Items der Entwickler auch realistische Fortschrittsdaten (geleistete Arbeit, verbleibende Arbeit, aktuelle Schätzung des Gesamtaufwandes) widerspiegeln sollten. Ohne Erweiterungen haben Sie als Projektmanager nur die Möglichkeit ihre Entwickler per Arbeitsanweisung zu bitten, die Felder geleistete Arbeit und verbleibende Arbeit in den Work Items täglich auszufüllen.
Diese Methodik hat in der Praxis aber mehre Probleme:
- der Entwickler hat einen hohen manuellen Aufwand, weil er alle verknüpften Work Items nochmals im Visual Studio öffnen muss,
- der Entwickler vergisst die Erfassung, weil im Projektstress die tägliche Erfassung von geleisteter Arbeit im Projektstress untergeht oder
- der Entwickler ignoriert die Arbeitsanweisung, weil Nebentätigkeiten wie die Erfassung von Arbeitszeiten eine niedrige persönliche Priorität haben.
Wir haben nach einer Lösung gesucht und den CheckinTimeTracker entwickelt. Der CheckinTimeTracker ermöglicht die Erfassung von Arbeitszeiten während des Checkin-Vorganges ohne Störung des Entwickler-Arbeitsprozesses. Im Rahmen des neuen AIT TeamSystemPro Productivity Tools Releases haben wir jetzt den CheckInTracker als Checkin Policy für Sie kostenlos freigeben (Download siehe [1]).
Im Folgenden lernen Sie die notwendigen Schritte zum Einrichten und zur Nutzung der CheckinTracker Policy kennen.
Einrichtung durch den Team Projekt Administrator
1. Hinzufügen der Checkin-Policy über Team Projekt –> Source Control –> Checkin-Policies

2. Öffnen der Konfiguration über Edit (Achtung: Beim Hinzufügen der Policy öffnet sich der Dialog automatisch.)

3. Zuordnen von Work Item Feldern zu den Checkin-Policy Feldern (Completed Work, Remaining Work und Original Estimate).
Hinweis: Die Working Hours haben keine direkte Entsprechung in den Work Items, weil Sie nur die geleistete Arbeit widerspiegeln. Der erfasste Wert wird durch die Checkin-Policy mit den Work Item Feldern verrechnet.
4. Zuordnen von beschreibenden Beschriftungen zu den Checkin-Policy-Feldern (Completed Work, Remaining Work, Original Estimate und Working Hours).
5. Auswählen der zu aktualisierenden Work Item Typen
6. Optional: Auswählen von zusätzlich anzuzeigenden Work Item Felder aus.
Hinweis: Wir unterstützen aktuell nur die Feldtypen String, Integer und Double Work Item.
7. Speichern der Einstellungen durch Drücken von OK.
Nutzung als Entwickler beim Einchecken
1. Öffnen es Eincheckdialoges (Pending Changes )

2. Auswählen von Work Items

3. Erfassen der Arbeitsstunden für alle ausgewählten Arbeitsaufgaben in der Spalte Working Hours.
Hinweis: Nach Eingabe der Working Hours werden die Felder Remaining Work und Completed Work automatisch berechnet. Für die Berechnung verwenden wir folgende Formeln.
Remaining Work = Original Remaining Work – Working Hours
Completed Work = Original Completed Work + Working Hours

4. Optional: Anpassen der verbleibenden Arbeitsbudgets, wenn die Werte aufgrund von veränderten Rahmenbedingungen nicht mehr stimmig sind.

5. Optional: Anpassen der Werte der Zusatzfelder, wenn die Werte aufgrund von veränderten Rahmenbedingungen nicht mehrstimmig sind oder die Aufgabe dies erforderlich macht (z.B. Kollege soll ein Review der Ergebnisse durchführen).

6. Bestätigen der Eingaben mit “OK”
Ergebnis:

Fazit:
Mit dem neuen CheckinTimeTracker aus unseren AIT TeamSystemPro Productivity Tools können Sie als Entwickler ohne Störung Ihres Arbeitsflusses sehr einfach Ihre Arbeitsstunden auf Work Items verbuchen und damit Ihrem Projektmanager das Leben signifikant vereinfachen. Als Projektmanager bekommen Sie nun täglich aktuelle Daten zum Projektfortschritt und können damit proaktiv und erfolgreich ihre Projekte managen.
Links und weiterführende Informationen
[1]: AIT TeamSystemPro Productivity Tools: http://visualstudiogallery.msdn.microsoft.com/d127b275-b7b6-4504-b01e-58b4a010ff53
AIT TeamSystemPro Productivity Tools jetzt mit CheckinTimeTracker Checkin-Policy is a post from: AIT Blog
In den vorangegangenen drei Teilen der Serie über Knockout.js haben wir uns mit einer generellen Einführung, einer beispielhaften Bindung an Formulare und Listen als auch der Verwendung von Vorlagen beschäftigt. Dieser Teil beschäftigt sich nun mit der Möglichkeit der benutzerdefinierten Bindungen. So wird gezeigt, wann diese sinnvoll verwendet werden und wie sie denn überhaupt funktionieren.
Bindungen im Allgemeinen
Vordefinierte Bindungen stehen für die unterschiedlichsten Bereiche zur Verfügung. Abgedeckt werden Bindungen um Text und Darstellung zu kontrollieren (z.B. visible, text, html, css usw.), um mit Formular-Feldern zu arbeiten (submit, checked, value, etc.) und zur Ablaufsteuerung (foreach, if, ifnot, with).
Eine vollständige Übersicht findet sich in den Slides des ersten Teils.
Trotz der mannigfaltigen Möglichkeiten, besteht der Bedarf an eigenen Bindungen, gerade dann, wenn Werteänderungen spezielle Auswirkungen auf DOM Elemente haben sollen. Äußerst hilfreich sind benutzerdefinierte Bindungen auch dann, wenn Steuerelemente von Drittanbietern zum Einsatz kommen und so spezielle oder zusätzliche Attribute/Verhalten bedient werden sollen.
Grundlagen
Eine benutzerdefinierte Bindung wird durch folgenden Code erstellt, wobei myBindingName mit dem tatsächlichen Namen der Bindung zu ersetzen ist:
ko.bindingHandlers.myBindingName = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
},
update: function(element, valueAccessor, allBindingsAccessor, viewModel) {
}
};
Von Bedeutung sind die beiden Funktionen init und update:
- init: Wird nur beim ersten Auswerten der Bindung aufgerufen. Ideal für die Initialisierung, beispielsweise dem Setzen von Eventhandlern.
- update: Hier kann darauf reagiert werden, wenn sich gebundene Werte verändern. Der Aufruf erfolgt, wenn die Bindung das erste Mal angewandt wird und bei jeder Änderung des gebundenen Wertes. Sollte verwendet werden, um DOM Elemente auf Basis des geänderten Wertes zu manipulieren/aktualisieren.
Beide Funktionen sind mit insgesamt vier Parametern ausgestattet:
- element: Das DOM Element der Bindung. Dadurch ist es nicht notwendig, dem Element eine Id oder Ähnliches zuzuweisen.
- valueAccessor: Dahinter verbirgt sich eine Funktion, die Zugriff zum gebundenen Wert gibt. Die Funktion liefert ein Observable zurück, nicht den tatsächlichen Wert. Wird ein Ausdruck für die Bindung verwendet, dann wird dieser zurück geliefert.
- allBindingsAccessor: Eine Funktion, die alle Bindungen auf dieses DOM Element zurück liefert.
- viewModel: Das ViewModel-Objekt, das via ko.applyBindings gebunden wurde. Bei einer verschachtelten Bindung wird das gebundene Datenelement zurückgeliefert.
Doch sehen wir uns ein einfaches Beispiel an.
Beispiel
Sehen wir uns die benutzerdefinierte Bindung anhand eines kleinen Beispiels an. Es soll ein Eingabeformular geben. Hier können sowohl der Vor- als auch der Nachname erfasst werden. Die Daten werden darüber als Zusammenfassung angezeigt. Wird ein Wert in der Eingabe verändert, soll die Wertänderung in der Zusammenfassung mit Hilfe eines Fade-In-Effekts angezeigt werden.
Dazu ist ein einfaches ViewModel notwendig, welches an das User Interface gebunden wird:
var viewModel = {
firstName: ko.observable("Norbert"),
lastName: ko.observable("Eder")
};
ko.applyBindings(viewModel);
Die View selbst sieht dann so aus:
<div class="overview">
<h2>First name</h2>
<span data-bind="text: firstName"></span>
<h2>Last name</h2>
<span data-bind="text: lastName"></span>
</div>
<div class="data">
<h2>First name</h2>
<input data-bind="value: firstName" /><br/>
<h2>Last name</h2>
<input data-bind="value: lastName" /><br/>
</div>
Soweit noch nichts aufregendes. Bei einer Werteveränderung, wird dies sofort in der Zusammenfassung nachgezogen. Nun soll aber noch der Einblendeffekt hinzukommen. Dazu erstellen wir eine benutzerdefinierte Bindung, die auf eine Wertänderung reagiert (update-Funktion).
ko.bindingHandlers.fade= {
update: function(element, valueAccessor) {
$(element).hide().fadeIn(1000);
}
};
Da lediglich auf die Wertänderung, ohne Berücksichtigung des tatsächlichen Wertes, reagiert werden muss, ist die update-Funktion ausreichend. Auch muss auf den Wert selbst nicht zugegriffen werden. Stattdessen wird lediglich das Element versteckt und via fadeIn eingeblendet.
Hinweis: Müsste auf den Wert reagiert werden, kann dieser via ko.utils.unwrapObservable(valueAccessor()) bezogen werden.
Damit dies auch tatsächlich funktioniert, muss die benutzerdefinierte Bindung noch in der View gesetzt werden:
<div class="overview">
<h2>First name</h2>
<span data-bind="text: firstName, fade: firstName"></span>
<h2>Last name</h2>
<span data-bind="text: lastName, fade: lastName"></span>
</div>
<div class="data">
<h2>First name</h2>
<input data-bind="value: firstName" /><br/>
<h2>Last name</h2>
<input data-bind="value: lastName" /><br/>
</div>
Fertig ist eine einfache Verwendung.
Download / Showcase
Das Bespiel kann unter http://jsfiddle.net/SvAHr/ getestet und bezogen werden. Oder aber auch untenstehend (IE streikt hier leider):
Fazit
Auch wenn das gezeigte Beispiel ein sehr einfaches ist, können benutzerdefinierte Bindungen sehr gut eingesetzt werden, um komplexe Verhalten zu steuern. Der Vorteil liegt darin, dass so wiederverwendbare Bindungen erstellt werden können. Im einfachsten Fall können darüber Standardverhalten/-einstellungen gesetzt werden, die jedoch nur an einer einzelnen Stelle zu implementieren/verwalten sind.
Als ich eben den Post zu AddFieldAsXml geschrieben habe, habe ich die Beispiele in einer Konsolenanwendung nachgestellt. Das Beispiel ist nicht sehr kompliziert und doch überraschte mich SharePoint direkt in der ersten Zeile mit einer FileNotFoundException.
Laut MSDN wird eine FileNotFoundException im SPSite-Konstruktor dann geworfen, wenn die SiteCollection unter der Url nicht gefunden werden konnte. Die Url ließ sich jedoch problemlos im Browser aufrufen - sowohl unter localhost als auch unter dem Rechnernamen wurde die SiteCollection gefunden. Nächster Versuch war also, die Credentials explizit mitzugeben, auch wenn das eigentlich nicht das Problem sein dürfte, weil sowohl Browser als auch Visual Studio mit dem gleichen Nutzeraccount verwendet wurden.
Was die MSDN-Seite verschweigt: Diese Exception wird auch dann geworfen, wenn das Platform-Target nicht stimmt. Konsolenanwendungen haben in der Visual Studio Vorlage grundsätzlich erst einmal das Platform Target x86; SharePoint aber ist eine 64-bit-Anwendung, daher muss das Platform Target der Konsolenanwendung auch x64 sein, damit es funktioniert. Also die Einstellung eben geändert, neu kompiliert und schon klappt es auch von der Konsole.
Möchte man programmatisch Felder zu einer SharePoint-Liste hinzufügen, so stolpert man früher oder später über die Methoden Add bzw. AddFieldAsXml von SPFieldCollection. In vielen Fällen führen beide Methoden zum Ziel, wobei AddFieldAsXml den Vorteil hat, dass man recht einfach das Schema-Xml aus bereits bestehenden Listen übernehmen kann, es auch recht komplexe Szenarien zulässt und dann in meinen Augen besser lesbar ist als Quellcode, der über mehrere Zeilen oder Klassen verteilt versucht, ein Objekt vom Typ SPField zu erzeugen und mit den passenden Eigenschaften zu bestücken.
Damit möchte ich auch schon zum Problem kommen: Ein Objekt vom Typ SPField verfügt über zwei Eigenschaften, die in diesem Kontext eine Rolle spielen: Title stellt in der Liste den Spaltennamen dar, den der Nutzer zu Gesicht bekommt. Dieser kann natürlich auch Leerzeichen oder Sonderzeichen beinhalten. Im Gegensatz dazu ist InternalName die interne Referenz auf die Spalte und als solche ist sie in der jeweiligen Liste eindeutig und readonly. Zudem werden hier sämtliche Sonderzeichen maskiert dargestellt. Um es mal plastisch darzustellen im folgenden das Beispiel aus der MSDN.
using (var site = new SPSite("http://localhost/"))
{
using (var web = site.RootWeb)
{
var list = web.Lists["Shared Documents"];
var displayName = "My Custom Field";
var staticName = "MyStaticName";
var strInternalName = list.Fields.Add(displayName, SPFieldType.Text, false);
var field = list.Fields.GetFieldByInternalName(strInternalName);
field.StaticName = staticName;
field.Update();
}
}
In diesem Fall ist Title nun wie erwartet My Custom Field und der Internal Name My_x0020_Custom_x0020_Field.
Wie bereits angedeutet, geht das ganze auch mit AddFieldAsXml:
using (var site = new SPSite("http://localhost/"))
{
using (var web = site.RootWeb)
{
var list = web.Lists["Shared Documents"];
var fieldXml = "<Field Type=\"Text\" MaxLength=\"50\" DisplayName=\"My Custom Field\" ID=\"15C0D97B-3735-4AD9-8FE2-8A01E5B43486\" StaticName=\"MyStaticName\" Name=\"MyInternalName\" />";
var strInternalName = list.Fields.AddFieldAsXml(fieldXml);
var field = list.Fields.GetFieldByInternalName(strInternalName);
field.Update();
}
}
Was bei der Ausführung nun auffällt: Obwohl ich im XML einen Internal Name vorgegeben habe, wird dieser nicht verwendet, sondern weiterhin auf Basis des Display Name ein Internal Name generiert. Das ist insofern schlecht, da wie beschrieben der Internal Name ja der Identifier der Spalte ist und deshalb auch in CAML-Queries verwendet wird.
Aber mal unabhängig davon gibt es natürlich auch hier mehrere Arten das Problem zu lösen: Erster Ansatz und einer der auch häufig verwendet wird, ist, im Aufruf von AddFieldAsXml DisplayName und InternalName auf den künftigen InternalName zu setzen und im Nachhinein den DisplayName programmatisch auf den eigentlichen Wert zu setzen. Diesen Ansatz verfolgen zum Beispiel Bil und auch Torsten.
Wenn man sich die Blogbeiträge so anschaut und sieht, dass dieses Verhalten schon mindestens seit 2005 besteht, so stellt sich hier doch die Frage nach Bug oder Feature und warum es dafür keine saubere Lösung gibt. Gibt es nicht? Doch, die gibt es, aber man muss drauf kommen.
AddFieldAsXml hat noch eine zweite Überladung und hier liegt der Schlüssel zum Erfolg:
using (var site = new SPSite("http://localhost/"))
{
using (var web = site.RootWeb)
{
var list = web.Lists["Shared Documents"];
var fieldXml = "<Field Type=\"Text\" MaxLength=\"50\" DisplayName=\"My Custom Field\" ID=\"15C0D97B-3735-4AD9-8FE2-8A01E5B43486\" StaticName=\"MyStaticName\" Name=\"MyInternalName\" />";
var strInternalName = list.Fields.AddFieldAsXml(fieldXml, true, SPAddFieldOptions.AddFieldInternalNameHint);
var field = list.Fields.GetFieldByInternalName(strInternalName);
field.Update();
}
}
Verwendet man hier explizit die Option AddFieldInternalNameHint, nur dann wird der im Xml angegebene interne Name auch verwendet.

Ich hätte erwartet, dass man das auch direkt anhand des Schema-XMLs hätte ermitteln können - à la: Attribut Name vorhanden, dann verwenden, sonst anhand des DisplayNames. Aber so ist es leider nicht. Nehmen wir es also als Sicherheits-Feature hin, dass man die Vergabe des Internal Names explizit noch einmal mit setzen der Option AddFieldInternalNameHint bestätigen muss.
Karim und ich haben zusammen eine Reihe von kleinen Filmen (1-6 Teile) aufgenommen, wo wir über die Windows 8 Developer Preview und das passende Samsung Slate von der //BUILD/ Konferenz sprechen.
Wir geben einen Überblick über die neuen Funktionalitäten und zeigen warum Windows 8 ein interessantes Tablett Betriebssystem werden kann. Metro-Oberfläche ist in aller Munde! Teil 5 & 6 zeigen wie einfach man für diese programmieren kann.
Link: www.windows-8-preview.de
Feedback zu den Filmen ist willkommen! :-)
Wer mit WPF zu tun hat, kennt das: Damit die Benutzeroberfläche richtig aktualisiert wird, müssen die ViewModels INotifyPropertyChanged implementieren und das große Problem dabei ist, dass die PropertyChangedEventArgs den Namen der Eigenschaft als String erwarten. Wenn man sich konzentriert, ist das initial auch nicht allzu schwierig. Problematisch kann es dann werden, wenn man Refactorings durchführt und dann vergisst, den String anzupassen.
using System.ComponentModel;
public class MainViewModel : INotifyPropertyChanged
{
private string zahl1;
public event PropertyChangedEventHandler PropertyChanged;
public string Zahl1
{
get
{
return this.zahl1;
}
set
{
this.zahl1 = value;
this.RaisePropertyChanged("Zahl1");
}
}
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Dieses Problem kann man sehr elegant mit AOP umgehen - zum Beispiel mit einem Aspekt mit PostSharp. Diese Lösung finde ich persönlich sehr elegant, jedoch kann oder möchte man dieses Produkt nicht immer einsetzen. Deshalb möchte ich hier kurz auf eine andere Alternative hinweisen.
In vielen WPF-Projekten wird Prism eingesetzt, um Composite Applications zu erzeugen. Prism selbst bringt ein Basisobjekt mit, das man für die ViewModels verwenden kann - NotificationObject. Vorteil dieses Basisobjekts ist es, dass dieses bereits den Event und die Methode RaisePropertyChanged zum Aufruf beinhaltet. Damit würde das oben angebrachte Beispiel dann wie folgt aussehen:
using Microsoft.Practices.Prism.ViewModel;
public class MainViewModel : NotificationObject
{
private string zahl1;
public string Zahl1
{
get
{
return this.zahl1;
}
set
{
this.zahl1 = value;
this.RaisePropertyChanged("Zahl1");
}
}
}
Damit ist das ViewModel schon etwas entschlackt, aber das Problem mit den Magic Strings besteht noch immer. NotificationObject bietet noch eine weitere Überladung für RaisePropertyChanged, in der man eine Expression übergeben kann, aus der der Name der Property dann extrahiert wird.
using Microsoft.Practices.Prism.ViewModel;
public class MainViewModel : NotificationObject
{
private int zahl2;
public int Zahl2
{
get
{
return this.zahl2;
}
set
{
this.zahl2 = value;
this.RaisePropertyChanged(() => this.Zahl2);
}
}
}
Wenn man mal hinter die Kulissen schaut, dann sieht man, dass die Magie von der Methode PropertySupport.ExtractPropertyName übernommen wird. Lässt man nun hier mal alles weg, was mit Parameterprüfung zu tun hat, dann reduziert es sich auf einen Zweizeiler, der Gebrauch vom Namespace System.Linq.Expressions macht.
private void RaisePropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
var propertyName = ExtractPropertyName(propertyExpression);
this.RaisePropertyChanged(propertyName);
}
private static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
{
var memberExpression = propertyExpression.Body as MemberExpression;
return memberExpression.Member.Name;
}
Man könnte wenn man wollte also diese Methode auch ohne eine Referenz auf Prism implementieren. System.Linq.Expressions selbst verwendet intern (wie sollte es anders sein) Reflection - und wie immer, so ist die Ermittlung von Werten zur Laufzeit langsamer als das Kompilieren von Konstanten, so wie es mit dem Hinterlegen des Namens der Eigenschaft als String oder mit PostSharp passieren würde. In meinen Augen ist der Geschwindigkeitsnachteil aber in den meisten Fällen vernachlässigbar klein.
Das AIT Team System Pro Team einen Skype Provider für VS 2010 auf Codeplex bereit gestellt. Über die Team Member Funktion kann man als Team Explorer Nutzer sehr einfach andere Instant Messaging Programme einbinden. Die Einbindung umfasst dabei nicht nur die Kommunikation über Textnachrichten, sondern in Abhängigkeit vom Client auch über Voice over IP (VOIP) oder Video. Also mal schnell jemanden aus dem Team “anpingen” ohne Visual Studio verlassen zu müssen.

Alle Details gibt’s auf dem AIT Blog.
Die DNUG Braunschweig trifft sich am 25.01.2012 um 19:00 im Restaurant Zucker (Tagungsraum). Dieses Mal halten Karim und ich einen Vortrag zur Windows 8 Entwicklung.
Abstract:
Seit September 2011 gibt es die erste Preview Version von Windows 8 und die Beta steht bereits vor der Tür. So langsam wird es Zeit sich mit den Neuerungen und Änderungen des neuen Betriebsystems zu beschäftigen. Mit der neuen Metro-Welt für Tablet-PCs führt Microsoft auch einen neuen App Store ein. Wie auch schon beim Window Phone 7 ergibt sich hier für Entwickler ein neuer Markt für die Vertreibung der eigenen Software.
Lars Keller und Karim El Jed machen einen kleinen Rundflug durch die neue Windows 8 Welt und zeigen wie man eine Metro-Applikation mit der Windows Runtime entwickeln kann.
Wie immer ist die Veranstaltung kostenlos! Weitere Informationen zur DNUG Braunschweig können hier gefunden werden: http://www.dotnet-braunschweig.de
Eine der wichtigsten Gründe Prism einzusetzen ist die Möglichkeit die GUI in Regionen aufzuteilen. Jede dieser Regionen wird als Content-, Items- o.ä. Control repräsentiert und kann dann zur Laufzeit mit 1 (ContentControl) bis n (Items- oder TabControl) UserControls bestückt werden. Auf diese Weise wird die eigentliche Benutzeroberfläche lose gekoppelt und kann ohne größeren Aufwand an neue Anforderungen angepasst werden.
Ein immer wiederkehrendes Problem stellt für mich dabei die Nutzung des ItemsControl dar. Jenes zeigt die einzelnen Views in Form einer Liste an, was unter Umständen etwas langweilig und verwirrend wirken kann. Aus diesem Grund bietet es sich an, jene Views durch Überschriften oder Abgrenzungen voneinander zu trennen, aber wie kriegt man das hin? Kann man es evtl. auch ohne Prism verwenden? Und vor allem wie schafft man es mit möglichst wenig Aufwand?
Erst einmal völlig statisch…
Bevor wir uns Prism zu wenden, noch einmal kurz beschrieben was wir eigentlich tun wollen. Das grundlegende Ziel ist zunächst das Anzeigen mehrerer Controls in einer Liste. Dies kann wie folgt geschehen, wobei der Einfachheit halber Rechtecke statt UserControls verwendet wurden.
Der Vorteil eines ItemsControls gegenüber z.B. einer ListBox ist hierbei vor allem, dass keine Selektierung statt findet sobald eines der enthalten UserControls den Fokus erhält, denn für den Anwender darf nicht ersichtlich sein, dass es sich tatsächlich um verschiedene Bestandteile handelt.
Wie man im obigen Bild schon sieht, ist das etwas verwirrend da alle Controls dicht an dicht aufgereiht sind und somit die logische Gruppierung fehlt. Dies wird um so schlimmer je mehr Inhalt sie haben. Also wäre es doch besser sie untereinander abzugrenzen.
Dazu kann man entweder vor jedem Control einen entsprechend gestylten TextBlock einfügen oder wie folgt das HeaderedContentControl nutzen. Letzteres hat den Vorteil, dass man die Einträge im Xaml besser unterscheiden und vor allem leichter stylen kann.
…dann etwas dynamisch…
Im dynamischen Fall werden die anzuzeigenden Controls nicht mehr fest angegeben, sondern aus einer Datenquelle bezogen. Hier ist die Abgrenzung über einen allgemeinen Style noch wichtiger als im statischen Fall, denn in diesem hat man wesentlich geringeren Einfluss darauf wie die geladenen Controls intern aufgebaut sind.
<ItemsControl ItemsSource="{Binding ControlSource}" />
Nun hat man zunächst aber ein weiteres Problem. Denn man muss irgend wie die Überschrift ermitteln die zuvor noch statisch angegeben werden konnten. Hier bietet sich die Nutzung eines Interfaces an, dass genau jene Property vorschreibt auf die der Header sich dann bindet.
public interface IHeaderedView
{
string Header { get; }
}
Als nächstes müssen wir nun noch das Aussehen unseres Headers festlegen. Diesmal verwende ich kein HeaderedContentControl sondern überschreibe das ControlTemplate des ContentControls eines jeden Items. Sieht man sich den Code genauer an, merkt man sehr schnell, dass ich jetzt genau das tue, was ich im statischen Fall zu vermeiden versucht habe.
<Style x:Key="ItemHeaderStyle">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ContentControl}">
<StackPanel>
<TextBlock Text="{Binding Path=Header,
RelativeSource={RelativeSource TemplatedParent}}" />
<ContentPresenter />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Der ContentPresenter spiegelt hierbei das tatsächlich hinzugefügte Element wieder, welches von einem StackPanel umgeben und dessen Header von einem TextBlock dargestellt wird. Der Text des TextBlocks ergibt sich nun aus der von IHeaderedView vorgeschriebenen Property. Damit diese Bindung funktioniert muss das Interface jedoch im CodeBehind der View umgesetzt werden.
Damit es aber nun wirklich wie gewünscht aussieht, muss der Style noch verwendet werden. Hierzu ist dieser dem ItemContainerStyle des ItemsControl zuzuweisen.
…zuletzt dann auch mit Prism.
Tja, das war eigentlich schon alles. Um Prism hier ins Spiel zu bringen muss man nun eigentlich nur das Binding auf die Liste der Controls entfernen und das ItemControl als Region bekannt machen. Wie so etwas aussehen kann, zeigt der folgende Code und das entsprechende Bild.
<ItemsControl Prism:RegionManager.RegionName="DiagramRegion"
ItemContainerStyle="{StaticResource ItemHeaderStyle}"/>

Wen es interessiert: Als Ausgangspunkt für den Style im Bild wurde das Shiny Red Theme verwendet.
Weitere Infos
- WPF Content Model in der MSDN
- sehr gute Beschreibung zu Items Containern & Co.
In meiner Serie zu Knockout.js habe ich bereits gezeigt, was diese Bibliothek leisten kann. Nichts liegt also näher, als das eigene Blog ein wenig auf Vordermann zu bekommen. Stein des Anstoßes ist die Suche, die bei BlogEngine.NET sehr altertümlich wirkt und in meinem neuen Design daher bis dato nicht berücksichtigt wurde. Mit Knockout.js sollte eine verbesserte Variante schnell implementiert sein. Da die Suchergebnisse durch diverse Bots auch nicht indiziert werden müssen, kann Knockout.js bedenkenlos eingesetzt werden. Hier mein Erfahrungsbericht.
Kontext
Um den Kontext für diejenigen zu setzen, die sich weder mit der BlogEngine.NET beschäftigt haben, noch dies wollen, ein kurze Einführung wie die Zusammensetzung hinsichtlich der Suche ist und was alles benötigt wird. Wenn Erfahrungen mit eben diesem System bestehen, kann dieser Abschnitt übersprungen werden.
Die Suche der BlogEngine.NET besteht aus folgenden Teilen:
- Klasse Search, welche die Suche übernimmt und Ergebnisse zurück liefert.
- Ein Steuerelement SearchBox, welches ein Input-Feld, eine Schaltfläche und noch ein paar andere Dinge rendert. Das Input-Feld selbst reagiert auf ein Enter und leitet eigentlich nur auf die Suchseite weiter.
- Ein Widget, welches das Steuerelement verwendet
- Eine Suchseite (search.aspx), welche ebenfalls das angesprochene Steuerelement verwendet.
Das ist grob alles. Um das gesamte User Interface der Suche ersetzen zu können, ist es also notwendig, ein einzelnes Steuerelement (zum Einbinden im Header etc.), ein Widget und eine dedizierte Suchseite anzubieten.
Suche via AJAX ermöglichen
Die Suchfunktionalität besteht bereits (durch die Klasse Search). Diese sollte soweit bestehen bleiben, um auch mit zukünftigen Versionen kompatibel zu sein.
Was allerdings fehlt, ist ein Service, um dies nutzen zu können. Damit das Ergebnis am Client gut verarbeitet werden kann, liefert dieses JSON zurück. Zusätzlich werden auf Einstellungen des Blogsystems zurück gegriffen, um die Beschreibungen der Posts für die Ergebnisliste entsprechend zu kürzen. Der Code hierfür kann man sich dann im Download-Package ansehen.
Knockout.js ViewModel
Damit in der UI die notwendigen Bindungen gesetzt werden können, ist ein entsprechendes ViewModel notwendig, nennen wir es FastSearchViewModel. Welche Möglichkeiten soll es bieten?
- Es muss der Suchbegriff abgefasst werden.
- Der Benutzer kann wählen, ob Kommentare durchsucht werden sollen. Diese Information ist ebenfalls durch das ViewModel zu halten.
- Die AJAX-Suche muss unter Angabe des Suchbegriffs durchgeführt werden.
- Eine Liste der Ergebnisse ist zur Verfügung zu stellen.
- Der Benutzer möchte Rückmeldungen á la “Keine Suchergebnisse” erhalten. Diese müssen angeboten werden.
- Die Suche kann theoretisch dreimal auf einer Seite vorkommen (Suchfeld, Widget und Suchseite). Dieser Umstand ist zu berücksichtigen (wichtig für die Datenbindung auf einen bestimmten Bereich, da sonst alle Suchmöglichkeiten mit derselben Bindung ausgestattet werden).
- Schlussendlich bedarf es noch Kleinigkeiten, wie das Zurücksetzen des Eingabefeldes bei einem Klick bzw. dem Auslösen der Suche bei Betätigen der Enter-Taste.
All diese Dinge wurden im nachfolgenden ViewModel berücksichtigt.
var FastSearchViewModel = function (containerElement) {
var self = this;
self.container = containerElement;
self.searchValue = ko.observable('');
self.includeComments = ko.observable(false);
self.foundItems = ko.observableArray();
self.showNoItemsFound = ko.observable(false);
self.hasSearchValue = ko.computed(function () { return self.searchValue().length > 0; }, self);
self.hasResult = ko.observable(false);
self.notExecuted = ko.observable(true);
self.search = function () {
var dto = { "searchTerm": self.searchValue(), "includeComments": self.includeComments() };
$.ajax({
url: "uri/to/searchmethod",
data: JSON.stringify(dto),
type: "POST",
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (result) {
self.foundItems = ko.observableArray();
if (result.d.Posts && result.d.Posts.length > 0) {
for (var i = 0; i < result.d.Posts.length; i++) {
self.foundItems().push(new ResultViewModel(result.d.Posts[i].Title, result.d.Posts[i].Content, result.d.Posts[i].Link));
}
self.showNoItemsFound(false);
self.hasResult(true);
} else {
self.showNoItemsFound(true);
self.hasResult(false);
}
self.notExecuted(false);
ko.applyBindings(self, self.container);
}
});
};
self.inputChanged = function (sender, event) {
if (event.keyCode === 13 && self.hasSearchValue()) {
this.search();
}
};
self.clear = function (sender, event) {
self.searchValue('');
self.hasResult(false);
self.showNoItemsFound(true);
self.notExecuted(true);
};
self.closePopup = function () {
self.hasResult(false);
};
};
Zu beachten ist, dass die Ergebnisse aus dem AJAX-Aufruf in ein weiteres ViewModel geschrieben werden (ResultViewModel). Dies ist notwendig, damit die Bindungen greifen, die in einem Template für die einzelnen Einträge festgelegt sind. Hier der entsprechende Code:
var ResultViewModel = function (postTitle, postContent, postLink) {
this.title = ko.observable(postTitle);
this.content = ko.observable(postContent);
this.link = ko.observable(postLink);
};
User-Interface
Das User-Interface muss nun die für die Suche notwendigen Elemente zur Verfügung stellen und die Bindungen festlegen. Hier das auf die Suchseite vereinfachte Markup:
<div id="fastsearchpage">
<input id="fastsearchbox" data-bind="value: searchValue, valueUpdate: 'afterkeydown', event: { keyup: inputChanged, click: clear }" placeholder="Suche" onkeypress="return noenter()"></input>
<input id="fastsearchcomment" type="checkbox" data-bind="checked: includeComments">Kommentare inkludieren</input>
<input type="button" id="fastsearchbutton" data-bind="click: search, enable: hasSearchValue" value="Durchsuchen"></input>
<div id="fastsearchresult" data-bind="template: { name: 'searchresult-template', foreach: foundItems }">
</div>
<div id="nofastsearchresult" data-bind="visible: showNoItemsFound">
<p>
Keine Suchergebnisse.
</p>
</div>
<div id="nofastsearchexecuted" data-bind="visible: notExecuted">
<p>
Geben Sie einen Suchbegriff ein und drücken Sie Enter oder die Suchen-Schaltfläche.
</p>
</div>
<script type="text/html" id="searchresult-template">
<div class="searchitem">
<a data-bind="attr: { href: link, title: title }">
<span>
<strong data-bind="text: title"></strong>
</span>
</a>
<div data-bind="text: content"/>
</div>
</script>
<script type="text/javascript">
function noenter() {
return !(window.event && window.event.keyCode == 13);
}
</script>
</div>
Der Großteil der darin verwendeten Bindungen bringen zur Knockout.js-Serie nichts Neues. Interessant ist das Eingabefeld selbst:
<input id="fastsearchbox"
data-bind="value: searchValue, valueUpdate: 'afterkeydown', event: { keyup: inputChanged, click: clear }"
placeholder="Suche" onkeypress="return noenter()">
</input>
Dieses selbst hat eine Bindung auf searchValue um den eingegebenen Suchbegriff in das ViewModel durchzuschreiben. Ebenfalls wird das Ereignis OnKeyUp abonniert um auf das Betätigen der Enter-Taste hin zu prüfen, da in diesem Fall die Suche ausgelöst werden sollte. Beim Standardverhalten der value-Bindung ist es nun aber so, dass zum Zeitpunkt des Auslösens des Ereignisses (und des Verarbeitens in der Funktion des ViewModels) der eingegebene Wert noch nicht bekannt ist. Das liegt daran, dass die value-Bindung erst beim Verlassen des Fokus die Bindung selbst aktualisiert.
Um aber derartige Anforderungen abzudecken, steht der zusätzliche Parameter valueUpdate bereit. Darüber lässt sich konfigurieren, zu welchem Zeitpunkt die gebundene Eigenschaft des ViewModels aktualisiert werden soll. Folgende Möglichkeiten stehen zur Verfügung:
- change: Das ist die Standardeinstellung und aktualisiert das ViewModel wenn der Fokus zu einem anderen Element geht.
- keyup: Die Aktualisierung findet beim Loslassen des Tastendrucks statt.
- keypress: Das ViewModel wird aktualisiert, wenn eine Taste gedrückt wurde. Hier ist anzumerken, dass dieses Ereignis mehrfach ausgelöst wird, wenn der Benutzer auf der Taste bleibt.
- afterkeydown: Hier wird das ViewModel aktualisiert, sobald der Benutzer beginnt, ein Zeichen einzugeben. Diese Auswahl eignet sich bestens dazu, wenn das ViewModel in Echtzeit aktualisiert werden soll.
Stellen wir also valueUpdate auf afterkeydown, steht beim Auslösen der Suche der durch den Benutzer eingegebene Suchbegriff bereit.
Hinweis: Zusätzlich wird beim OnKeyPress noch die Funktion noenter() aufgerufen. Diese dient lediglich dazu, ein Submit eines eventuell vorhandenen Formulars zu verhindern.
Binden des ViewModels
Damit die Bindungen angezogen werden, muss ko.applyBindings aufgerufen werden. Wie bereits oben angesprochen, kann die Suche selbst mehrfach auf der Seite vorkommen. Damit nicht alle an dasselbe ViewModel gebunden werden (und dadurch anspringen, sobald es in einem der Felder eine Aktion gibt), sollte überprüft werden, wie viele Vorkommen vorhanden sind und an jedes eine eigene Instanz des ViewModels gebunden werden.
Das Ergebnis
Für die Verwendung unter BlogEngine.NET ist nun nicht mehr viel zu tun und weniger relevant, da lediglich Widget-Control (als Host) implementiert werden muss etc. Dies kann sich der interessierte Leser im Download genauer ansehen.
Das Resultat kann sich jeder hier im Blog ansehen, da alle Suchmöglichkeiten auf dieser Implementierung beruhen.
So sieht die Einbindung des Suchfeldes aus:

Und hier noch die Suchseite:

Download / BlogEngine.NET Package
Der Download ist über die BlogEngine.NET Gallery verfügbar. Darin befindet sich die Erweiterung zur BlogEngine.NET selbst, als auch die gesamte Implementierung. Wer diese Erweiterung für sein Blogsystem basierend auf BlogEngine.NET einsetzen möchte, findet weitere Informationen unter http://devtyr.com/fastsearch.html.
Feedback
Wie immer freue ich mich über jegliches Feedback. Sowohl zum Code selbst, als auch grundsätzlich zur Funktionalität der BlogEngine.NET-Erweiterung.
Im November 2011 habe ich meinen Vortrag “LINQ to SharePoint Best Practices” auch in Luzern auf den CollaborationDays gehalten. Diesmal wurde der Vortrag allerdings noch auf Video aufgenommen und ist nun Online kostenlos anschaubar.
Wer also etwas tiefer in die LINQ to SharePoint API eintauchen möchte, kann sich dadurch ein paar Eindrücke verschaffen. Unter http://mediasite.lm-ag.de/mediasite/SilverlightPlayer/Default_2.aspx?peid=7989650d2a294ec7abe22a2630a8482c ist das Video zu finden.
Viel Spaß damit.
Wer mit RavenDB arbeitet kommt automatisch zu einem sehr mächtigen Mittel: Den Indexen. Der Hauptfokus des Posts liegt hierbei auf dem Unit-Testen von RavenDB. Unit-Testing in Datenbank-Projekten ist mehr als anstrengend und zeitfressend. RavenDB lässt sich allerdings recht einfach in einen “Test” Modus versetzen, sodass die Funktionalität erhalten bleibt.
Achtung: Streng genommen darf ein Unit-Test auch keine Datenbank berühren, da man damit mehr als die eigentliche Test-Einheit testet. Richtiger wäre Integrationstests, allerdings redet die halbe Softwarewelt generell von Unit-Tests. Daher belassen wir es mal bei der unschärfe.
Der Code selbst ist unter Mithilfe von Daniel Lang entwickelt, als er mir bei einem RavenDB Index geholfen hat.
Was ist ein RavenDB Index?
Ein RavenDB Index kann man sich als gespeicherte Abfrage vorstellen, welche von RavenDB im Hintergrund ausgeführt wird und das entsprechende Result zwischenspeichert. Über RavenDB Indexe können Abfragen über mehrere Dokumente gemacht werden und über Map/Reduce die Ergebnismenge angepasst werden.
Bsp:
Ein ganz simpler Index sieht so aus:
public class SearchIndex : AbstractIndexCreationTask<Term>
{
public SearchIndex()
{
Map = terms => from term in terms
select new { term.Title };
Index(x => x.Title, FieldIndexing.Analyzed);
}
}
Was das ganze überhaupt macht, ist hier gut erklärt. Wenn ich den Code anwenden möchte:
Session.Query<Term, SearchIndex>().Where(x => x.Title.StartsWith(searchTerm)).ToList();
Unit-Test dazu (gemacht mit xUnit)
public abstract class RavenTest
{
protected IDocumentStore GetDatabase()
{
var documentStore = new EmbeddableDocumentStore
{
RunInMemory = true
};
documentStore.Initialize();
return documentStore;
}
}
public class SearchIndexTest : RavenTest
{
[Fact]
public void TitleContainsSearch()
{
using (var documentStore = GetDatabase())
{
IndexCreation.CreateIndexes(typeof(UserActivityFeedIndex).Assembly, documentStore);
using (var documentSession = documentStore.OpenSession())
{
documentSession.Store(new Term
{
Title = "RavenDB",
});
documentSession.Store(new Term
{
Title = "ASP.NET MVC",
});
documentSession.Store(new Term
{
Title = "Twitter Bootstrap",
});
documentSession.SaveChanges();
var result = documentSession.Query<Term, SearchIndex>().Where(x => x.Title.StartsWith("Boot"))
.Customize(x => x.WaitForNonStaleResults())
.ToList();
Assert.Equal(1, result.Count);
Assert.Equal("Twitter Bootstrap", result[0].Title);
}
}
}
}
Die RavenTest Klasse erstellt die Connection zur RavenDB “embedded” Datenbank. Über “RunInMemory” wird dies auch nur im Arbeitsspeicher gehalten. Vorteil: Sehr schnell und kein Cleanup nach dem Test.
In der Testmethode wird erst der Index angelegt (über Reflection wird die Assembly durchsucht) und dann werden Testdaten in diese DB abgespeichert. Am Ende erfolgt die Abfrage und das Ergebnis wird überprüft. Bei der Abfrage wird noch ein WaitForNonStaleResults dazugehangen um auch die gerade eben gespeicherten Daten mit abzufragen (RavenDB speichert die Index-Ergebnisse zwischen, sodass es zu einer kurzen Verzögerung kommen kann. Ist im Unit-Testing allerdings ungünstig.)
Sehr einfach und wesentlich eleganter als bei einer klassischen DB.
Immer wieder mal lese ich von Problemen bei der Installation oder mit Installationen von VS-2010. Ich hatte den Fall noch nie, aber es gibt scheinbar immer wieder Installationen in denen der Wurm drin ist.
VS-2010 installiert eine Unmenge an Komponenten und Programmteilen. Die meisten können einfach deinstalliert/gelöscht werden. Aber es bleiben immer noch ein Haufen DLLs/ActiveX Controls und Registry Einträge und Verweise auf Verzeichnisse. Und leider gibt es für die Visual Studio 2010 keinen richtigen Uninstaller, der auch alle Artefakte löscht und deshalb kann ich gleich zu Anfang sagen, dass man außer der Deinstallation wirklich wenig tun kann.
Eine Reparaturinstallation sollte man aber bei einer bestehenden Installation in jedem Fall einmal, bevor man zu härteren Maßnahmen greift. Die wirkt oft schon Wunder.
Im Netz gibt es das folgende Utility: Visual Studio 2010 Uninstall Utility:
http://archive.msdn.microsoft.com/vs2010uninstall
Das Tool hat drei Operationsweisen die man in dem obigen Link erklärt findet.
Das Tool funktioniert eigentlich ohne Probleme aber benutzt auch nur auf dem MSI Uninstall Prozess auf, und dabei berücksichtigt es nicht einmal Servicepacks. Sollte man also das SP1 von Visual Studio 2010 installiert haben, dann muss man dieses vorher selber entfernen.
Das dieses Tool macht aber auch nur die Arbeit halb. Das erkennt man mit einem schnellen Blick in Registry. Der Ast HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\10.0 wird durch die Deinstallation nicht entfernt und man muß in diesem Fall manuell Hand anlegen.
Wenn man also persistente Probleme mit der VS-2010 Installation hat, die man nicht über die Setup-Logs oder mit Tipps aus den Foren lösen kann, dann bleibt nach meiner Meinung nur der harte Weg den Rechner neu aufzusetzen. Bisher bin ich davon verschont geblieben und meine Installation haben immer von Rechnerwechsel zu Rechnerwechsel gehalten.
Copyright © 2010 Martin Richter
Dieser Feed ist nur für den persönlichen, nicht gewerblichen Gebrauch bestimmt. Eine Verwendung dieses Feeds bzw. der hier veröffentlichten Beiträge auf anderen Webseiten bedarf der ausdrücklichen Genehmigung des Autors.
(Digital Fingerprint: bdafe67664ea5aacaab71f8c0a581adf)
Microsoft ist als Aussteller auf den Software Quality Days in Wien vom 18. bis 19. Jänner vertreten.


Die Konferenz mit ca. 300 Teilnehmern fokussiert auf den Bereich Software Testing und Qualitätsmanagement. Wir zeigen auf dem Ausstellungstand Visual Studio Test Professional, Microsoft Test Manager, den Team Foundation Server und informieren über unser ALM Angebot in dem auch Testing ein integraler Bestandteil ist.
Im Vortrag “Exploratives Testen – Kreativität vs. Produktivität”, gehalten von Andreas Pollak (Microsoft) und Martin Klonk (Anecon) haben wir Visual Studio Test Professional vorgestellt bzw. einen Ausblick auf die nächste Version von Visual Studio Test Professional gegeben. Ca. 40 Personen haben sich in der Solution Provider Vortragsreihe der Konferenz über “Wie funktioniert exploratives Testen?” sowie die Herausforderungen und Vorteile informiert. In der Praxis führt exploratives Testen zu höherer Produktivität, die Möglichkeit den Testfokus auch auf andere Stellen im Projekt zu richten und bietet eine gute Skalierbarkeit (von kleinen bis zu großen Projekten, mit weniger oder mehr Budget). Ein wichtiges Erfolgskriterium ist aber die Protokollierung des Testverlaufes mit einem geeigneten Tool. Mit Visual Studio Test Professional steht ein Werkzeug zur Verfügung welches die Testschritte sehr umfangreich dokumentiert bzw. aufzeichnet (Video, screenshots, Systemzustände, snapshot einer virtuellen Umgebung) und diese auch zur weiteren Verarbeitung zur Verfügung stellt. So kann aus einem explorativen Test auch ein formaler Testplan abgeleitet werden oder auch ein coded UI Test erstellt werden. Ein Entwickler kann durch die Aufzeichnung einen Fehler leichter nachvollziehen und diesen durch Überleitung in einen formalen Testfall auch in der Zukunft vermeiden.
Weitere Informationen zu Visual Studio Test Professional finden Sie unserer österreichischen Visual Studio Webseite.
Am Montag den 17.1. fand das erste Windows Phone Pizza Tour Event des Jahres an der TU Graz statt. Mike Alexander gab unterstützt von Andreas Neuhold und Martin Cores vom U-Crew Team sowie Peter Prantner eine Übersicht zum Development auf dem Windows Phone. Mit 74 Teilnehmern musste eine Pizza-und Cola Bestellung wesentlich ausgeweitet werden.
Höhepunkt war die Verlosung eines brandneuen Nokia Lumia 800 Smartphones, natürlich über eine Windows Phone Verlosungs App.
Wir danken allen Teilnehmern und gratulieren der Gewinnerin. Die nächsten Termine zur Pizza Tour werden im Blog angekündigt.




Links
Windows Phone 7.5 = SDK 7.1 Download
Kostenlose AppHub Registrierung um Apps in den Windows Phone Marketplace zu stellen:
https://www.dreamspark.com/
Das Semester neigt sich dem Ende, die Vorlesungen sind fast alle gehalten und so ist jetzt eine gute Möglichkeit den Studenten ein wenig Wissen abseits des eigentlichen Lehrplans zu vermitteln.
Dank Professor Nestler hatte ich auf diese Weise wieder die Möglichkeit einen Spezialvorlesung an der HTW-Dresden zu halten. Nach dem ich mich beim letzten Mal im Juni vor allem der agilen Entwicklung und dem automatisierten Test gewidmet hatte, ging es dieses Mal um Pattern, Clean Code und Prism.
Für alle Anwesenden muss ich an der Stelle zugeben, dass die Vorlesung zunächst eigentlich zu MVVM und Prism geplant war und im Rahmen einer Lehrveranstalltung zum Thema WPF stattfinden sollte. Im Verlauf der letzten Wochen hat sich dies dann aber in eine etwas andere Richtung verschoben, so das Patterns allgemein und Clean Code mehr ins Zentrum gerückt sind.
Ziel war es daher auch nicht mehr so sehr, einen Einblick in die Praxis mit genanntem Framework und Patten zu geben, sondern eine Art Wegweiser zu errichten der Ideen geben sollte wie bestimmte Probleme umgangen werden können bzw. warum diese überhaupt erst auftauchen.
Für mich persönlich war es erneut eine Herausforderung die Informationen möglichst interessant und verständlich aufzubereiten. Immerhin sprechen wir hier über Themen deren Umfang und Komplexität auch so manchen erfahrenen Entwickler vor Probleme stellt.
Dass man dabei nicht alle Sachen die in diesem Rahmen gesagt und gezeigt wurden gänzlich in Form von Folien verfügbar machen kann, ist sicher einleuchtend. Ich hoffe aber das möglichst viele der Informationen noch immer in der Präsentation enthalten sind, zumal ich eine ganze Reihe von Bildern entfernen musste weil ich mich nicht sicher bin ob die von mir erworbenen Lizenzen eine Veröffentlichung im Internet einschließen.
Viel Spaß also mit den Folien und viel Erfolg bei zukünftigen Projekten.
Links
- Gute Übersicht zu den Pattern der
GOF
Zum aktuellen Themenspezial in der Ausgabe 2/2012 der dotnetpro trägt das AIT CloudPro TEAM mit zwei Artikeln bei.
In die Cloud und wieder zurück
Anwendungen unterliegen im Laufe ihres Lebenszyklus einer Reihe von sich verändernden Rahmenbedingungen. Neben fachlichen Anforderungen zählen hierzu auch eine Weiterentwicklung der Technologien, rechtliche Aspekte oder auch veränderte Benutzerzahlen. Dies kann dazu führen, dass Anwendungen, die als Einzelplatzanwendung konzipiert wurden, in die Cloud verschoben werden müssen und möglicherweise aus anderen Gründen auch wieder zurück. Wie man eine Anwendung aufbaut, die flexibel in unterschiedlichsten Szenarien installiert werden kann und was dabei beachtet werden muss lesen ab Seite 22.
Persistenz unter Microsoft Azure: Himmelweite Unterschiede
Bei der Entwicklung von Software muss sich bereits in der Planungsphase die Frage gestellt werden, woher die Anwendung ihre Information erhält und wohin sie diese speichert. In klassischen Anwendungen ist das in aller Regel eine Datenbank oder auch einzelne Dateien auf dem Dateisystem. Der Zugriff ist einfach, und hier kann sich der Entwickler ruhig “austoben”, denn dieser Speicherplatz stellt schon lange keine limitierende Ressource mehr dar. Wird aber einmal der Entschluss gefasst, die Software in die Cloud zu verlegen, muss sich die Architektur an die neuen Gegebenheiten anpassen um diese optimal zu nutzen. Es stehen unterschiedliche Technologien zur Verfügung. Diese unterscheiden sich unter anderem in der Skalierbarkeit, dem zur Verfügung stehenden Speicherplatz, Latenzzeiten sowie den Kosten. Paul Rack geht ab Seite 28 auf die Details ein und zeigt welche Technologie sich für welche Szenarien anbietet.
dotnetpro Themenspezial “Microsoft Azure schrittweise nutzen” is a post from: AIT Blog
Manchmal will man eine WindowsForms Anwendung auch über die Konsole aufrufen und dann möchte man natürlich auch Ausgaben der Anwendung in dem Konsolenfesnter sehen. Doch leider ist das nicht so einfach, da alle Aufrufe über die Console-Klasse werden ignoriert.
Doch warum ist das so? Das ist ganz einfach und liegt daran, dass das Konsolenfenster, das unser Programm startet zu einem anderem Prozess gehört, nämlich cmd.exe und somit nicht verbunden ist.
Was wir also tun müssen ist, unsere Ausgabe an das Konsolenfenster zu heften, das unsere Anwendung gestartet hat. Dafür kann man die Win32-Methode AttachConsole nutzen.
Diese ist sehr einfach aufgebaut und sollte vor der ersten Ausgabe an die Konsole aufgerufen werden:
Definition des Win32-Aufrufs: (Beispiele in VB.NET)
<DllImport("kernel32.dll")> _
Private Shared Function AttachConsole(dwProcessId As Integer) As Boolean
End Function
Um die Ausgabe an den Prozess zu geben der uns aufgerufen hat (Parent-Process) kann man hier als Wert -1 übergeben:
Private Const ATTACH_PARENT_PROCESS As Integer = -1
Was nun noch bleibt ist es die Methode aufzurufen, zum Beispiel in der Main-Methode der Applikation:
Private Sub frmMain_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
AttachConsole(ATTACH_PARENT_PROCESS)
//[...]
Das wars auch schon, nun werden eure Ausgaben an das Konsolenfester umgeleitet. Um die Konsole wieder von eurem Prozess zu lösen kann die paremeterlose Methode FreeConsole genutzt werden.
Es lohnt sich hier natürlich dann auch eine Unterscheidung zu machen wie die App gestartet wurde, also ob über Konsole oder über Doppelklick wie immer. Wenn sie über die Konsole gestartet wird, sollte man vielleicht die Oberfläche nicht mehr anzeigen. Das kann man dann zB. daran erkennen, dass Environment.GetCommandLineArgs.Length > 1 ist.
Probleme
Leider gibts ein paar Nebeneffekte die nicht unbeachtlich sind.
Ein Problem ist, dass Konsolenfuntionen die die Ausgabe von Programmen umleiten zB. so: "myapp.exe -test >test.txt" nicht funktionieren, die Ausgabe erfolgt weiterhin auf der Konsole. Hierfür konnte ich leider keine Lösung finden, wenn euch was einfällt einfach kommentieren.
Das zweite ist nur eine Schönheitssache, nämlich wenn das Programm beendet wird wird erst nicht die gewohnte Konsoleneingabezeile angezeigt, sondern nur der blinkende Cursor, so dass es aussieht als ob das Programm noch laufen würde, was es aber nicht mehr tut. (Wie gesagt Schönheitssache) Das kann man zB. dadurch umgehen, dass man vor dem Beenden des Programms noch ein [Enter] auf der Konsole ausgibt.
Weitere Infos und auch eine kleine Diskussion gibts hier und hier.

Falls jemand der Blog-Leser auf der Suche nach einer neuen Arbeitsstelle in der Region St. Gallen(Schweiz) ist, habe ich hier eine Stellenausschreibung.
Es geht um die Weiterentwicklung einer webbasierten Business Software(CRM,ERP,PPS,Fibu,...) welche als SaaS aus der Cloud aber auch im Lizenzkauf angeboten wird.
myfactory Schweiz sucht .NET Entwickler/in


RadioactivityCounter for Android - REAL working Geiger counter + 10Sv/h Image