Web- und Software Development

Visual Studio Makro für Shoulda Methoden erstellen

Written By: Mario Priebe - Sep• 13•11

Wenn man Tests mittels sogenannten Shoulda-Methoden (..should_return_a_object_from_type_myobject..) schreibt, kann das Schreiben der Methoden schon recht mühsam mit der Zeit werden.

Idee

Was wäre wenn ich eine Inputbox hätte, die es mir möglich macht, einfach und ohne Unterstriche zu setzen, meine Methode zu beschreiben und der Methodenrumpf nach einem Klick erstellt wird…

Die Idee dazu fand ich dazu bei codekicker, vielen Dank nochmal an dieser Stelle.

Makro erstellen

Um sich die Arbeit zu erleichtern, kann man mittels Makro sich einen Haufen Arbeit und eventuelle Verkrampfungen in den Fingern ersparen.

Um ein Makro zu erstellen ruft man dazu den Makro-Explorer auf, den man unter Tools -> Macros findet.

clip_image001

Dort bekommt man ein Auflistung der bereits gespeicherten Makros. Hier mit einem Rechtsklick auf MyMacros beispielsweise gehen und dann ein neues Modul hinzufügen.

clip_image002

Das Template Modul wählen und das Makro betiteln. Mittels Rechtsklick auf das erstellte Makro dann das Makro editieren.

Hier kann dann das Makro nun programmiert werden. Für unseren Fall implementieren wir folgenden Code:

Imports System Imports EnvDTE Imports EnvDTE80 Imports EnvDTE90 Imports EnvDTE90a Imports EnvDTE100 Imports System.Diagnostics Public Module ShouldaMethodMacro Sub ShouldaMethodMacro() Dim input = InputBox("insert text please").Replace(" ", "_") ActiveDocument.Selection.Text = String.Format("[Test]public void {0}(){1} {{ {1}//arrange{1}{1}//act{1}{1}//assert{1}{1} }}", input, Environment.NewLine) End Sub End Module

(Modulname und Konstruktor muss angepasst werden und speichern nicht vergessen ;)

Tipp: Um jeden Anfangsbuchstaben in dieser Methode groß zu schreiben, verwendet man aus dem Namespace System.Globalization die Methode CultureInfo.CurrentCulture.TextInfo.ToTitleCase:

Dim input As String input = InputBox("insert text please") input = CultureInfo.CurrentCulture.TextInfo.ToTitleCase(input).Replace(" ", "_")

Tastenkombination zuweisen

Nun sollten wir das Makro noch eine Tastenkombination zuweisen. Hierfür geht man unter Tools -> Options -> Environment -> Keyboard und suchen im "Show commands containing" Feld nach den Namen unseres Makros.

Im Feld "Press shortcut keys" dann die Tastenkombination wählen. Wenn möglich eine die noch nicht vorhanden ist. Ich wähle hier Ctrl + Shift + Alt + T und weise meinem Command diesen zu.

clip_image003

So, das war es auch schon. Wenn man nun in der Testklasse sein Shortcut ausführt, bekommt man eine Inputbox in der man seine Methode beschreibt.

clip_image004

Mit einem Klick auf OK, wird daraufhin diese Methode erzeugt. Die natürlich jedem freisteht anzupassen (Bitte die firmeninternen Richtlinien beachten ;)

clip_image005

 

Na dann, wie immer viel Spaß beim entwickeln : )

Selektierte TextBox in Silverlight

Written By: Mario Priebe - Sep• 02•11

Wenn man den Text in einer TextBox  vorselektieren möchte, sobald man dort hineinklickt, so kann man folgendes CodeSnippet dazu verwenden:

myTextBox.GotFocus += (s, arg) => { resultTextBox.SelectAll(); };

Eigene TextBox-Klasse

Oder man macht’s gleich richtig und baut sich eine eigene Klasse, namens “GotFocusTextBox”.

using System.Windows.Controls; namespace MPSoft.Controls { public class GotFocusTextBox : TextBox { public GotFocusTextBox() { this.GotFocus += (s, arg) => { this.SelectAll(); }; } } }

Verwendung:

xmlns:MPControls="clr-namespace:MPSoft.Controls" <MPControls:GotFocusTextBox Text="{Binding any"} />

Selektierten Text in die Zwischenlage kopieren

Nun möchte man vielleicht noch bestimmen, ob man den vorselektierten Text in der Textbox gleich in die Zwischenablage kopieren möchte. Hier erstellt man sich in der GotFocusTextBox-Klasse ein Property namens “CopyFocusedTextToClipBoard” dafür. 

public bool CopyFocusedTextToClipBoard { get; set; }

Nun, um den Text in die Zwischenablage zu bekommen, muss man in Silverlight über einen Umweg gehen. Hier kann man leider nicht die Clipboard-Klasse aus dem Framework verwenden. Die Sicherheitsrichtlinien vom Browser machen hier leider nicht mit. Aber man kann über Umwege dennoch an den Text, wie folgende Implementierung zeigt:

public class ClipBoard { /// <summary> /// Sets the text to clipboard. /// </summary> /// <param name="value">The value.</param> public void SetTextToClipboard(string value) { if (!String.IsNullOrWhiteSpace(value)) { var clipboardData = (ScriptObject)HtmlPage.Window.GetProperty("clipboardData"); if (clipboardData != null) clipboardData.Invoke("setData", "text", value); } } }

(unterstützt nicht jeder Browser, aber beim IE funktioniert das so).

Der Konstruktor der GotFocusTextBox-Klasse sieht demnach wie folgt aus:

public GotFocusTextBox() { this.GotFocus += (s, arg) => { this.SelectAll(); if (CopyFocusedTextToClipBoard) new ClipBoard().SetTextToClipboard(this.Text); }; }

Nun im Xaml noch die Property setzen und bei true wird dann entsprechend der Text in die Zwischenablage kopiert.

<MPControls:GotFocusTextBox CopyFocusedTextToClipBoard="true" Text="{Binding any}" />

fertig.

 

Viel Spaß beim entwickeln : )

UpdateSource-Trigger für Silverlight ComboBox

Written By: Mario Priebe - Aug• 29•11

Im vorherigen Artikel hab ich die Klasse BindingHelper von Thomas vorgestellt. Diese ermöglich ein UpdateSourceTrigger “OnPropertyChanged” an einer TextBox in Silverlight.

Ich habe die Klasse mal für eine ComboBox erweitert.

/// <summary> /// Supports a PropertyChanged-Trigger for DataBindings /// in Silverlight. Works just for TextBoxes /// (C) Thomas Claudius Huber 2009 /// http://www.thomasclaudiushuber.com /// extended from Mario Priebe 2011 for ComboBox SelectedItem /// http://www.biggle.de /// </summary> public class BindingHelper { public static bool GetUpdateSourceOnChange(DependencyObject obj) { return (bool)obj.GetValue(UpdateSourceOnChangeProperty); } public static void SetUpdateSourceOnChange(DependencyObject obj, bool value) { obj.SetValue(UpdateSourceOnChangeProperty, value); } // Using a DependencyProperty as the backing store for … public static readonly DependencyProperty UpdateSourceOnChangeProperty = DependencyProperty.RegisterAttached("UpdateSourceOnChange", typeof(bool), typeof(BindingHelper), new PropertyMetadata(false, OnPropertyChanged)); /// <summary> /// Called when [property changed]. /// </summary> /// <param name="obj">The obj.</param> /// <param name="e">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param> private static void OnPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) { if (obj is ComboBox) { var cb = obj as ComboBox; if ((bool)e.NewValue) cb.SelectionChanged += (cb_SelectionChanged); else cb.SelectionChanged -= (cb_SelectionChanged); } else if (obj is TextBox) { var txt = obj as TextBox; if ((bool)e.NewValue) txt.TextChanged += OnTextChanged; else txt.TextChanged -= OnTextChanged; } } /// <summary> /// Called when [text changed]. /// </summary> /// <param name="sender">The sender.</param> /// <param name="e">The <see cref="System.Windows.Controls.TextChangedEventArgs"/> instance containing the event data.</param> static void OnTextChanged(object sender, TextChangedEventArgs e) { var txt = sender as TextBox; if (txt == null) return; var be = txt.GetBindingExpression(TextBox.TextProperty); if (be != null) be.UpdateSource(); } /// <summary> /// Handles the SelectionChanged event of the combobox control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="System.Windows.Controls.SelectionChangedEventArgs"/> instance containing the event data.</param> static void cb_SelectionChanged(object sender, SelectionChangedEventArgs e) { var cb = sender as ComboBox; if (cb == null) return; var be = cb.GetBindingExpression(ComboBox.SelectedItemProperty); if (be != null) be.UpdateSource(); } }

Im gebundenen ViewModel definiere ich ein Property vom Typ, welcher in der ComboBox verwendet wird.

private TourData tourFilter; public TourData TourFilter { get { return tourFilter; } set { tourFilter = value; OnPropertyChanged("TourFilter"); } }

Verwendet wird das Ganze dann wie folgt:

<ComboBox ItemsSource="{Binding ElementName=TourDataDomainContext, Path=Data}" DisplayMemberPath="TourName" SelectedValuePath="TourId" SelectedItem="{Binding Source={StaticResource DailyStatsViewModel}, Path=TourFilter, Mode=TwoWay}" helper:BindingHelper.UpdateSourceOnChange="True" />

Viel Spaß beim entwickeln : )

OR verknüpftes Filtering in einem DataGrid

Written By: Mario Priebe - Aug• 26•11

Im folgenden Artikel beschreibe ich, wie man in einem DataGrid OR verknüpftes Filtering mit Daten aus einem RIA Sevice nach MVVM implementiert. Da sich keine OR Verknüpfung an einem “normalen” WPF DataGrid “so auf die Schnelle” umsetzen lässt, setze ich ein Telerik DatatGrid ein.

Um in einem DataGrid die Daten aus einem RIA Service-Kontext über der Eingabe in einer TextBox zu filtern, kann man normalerweise den FilterDescriptor einsetzen:

<riaControls:DomainDataSource.FilterDescriptors>     <riaControls:FilterDescriptor PropertyPath="Ort" Operator="Contains" Value="{Binding ElementName=txtOrt, Path=Text}" /> </riaControls:DomainDataSource.FilterDescriptors>

Das funktioniert soweit auch ganz gut, jedoch kann es dazu kommen, dass der Kunde über diese TextBox in mehr als einer Spalte im DataGrid die Daten filtern möchte.

Beispiel: Ich habe eine Spalte "Ort" und eine Spalte "Halle". Nun möchte ich über meiner TextBox entweder in Ort oder Halle filtern und das Ergebnis im Grid anzeigen.

Der o.g. Code zeigt, wie man einen Filter der Liste von FilterDescriptors hinzufügt. Natürlich heißt das auch, dass ich hier mehr als diesen verwenden kann. Nur das Problem ist, das alle Filter innerhalb dieser Liste, lediglich AND verknüpft werden. Das Verwenden eines OR Operators scheint leider (“auf die Schnelle”) nicht möglich.

Wir haben das Glück Komponenten von Telerik einzusetzen. Auch hier ist es möglich am DataGrid (RadGridView) Filter zu verwenden. Hier hat man die Collection "FilterDescriptors" zur Verfügung, in der ich mehrere "CompositeFilterDescriptor" definieren kann. Das Gute daran, hier kann ich über die Property LogicalOperator bestimmen, wie die Filter zueinander stehen.

Das Ganze sieht dann beispielsweise wie folgt aus:

<telerik:RadGridView.FilterDescriptors>     <telerik:CompositeFilterDescriptor LogicalOperator="Or">         <telerik:CompositeFilterDescriptor.FilterDescriptors>             <telerik:FilterDescriptor Member="Ort"                     Operator="Contains"                     Value="hier der suchstring"                     IsCaseSensitive="False" />             <telerik:FilterDescriptor Member="Halle"                     Operator="Contains"                     Value="hier ein weiterer suchstring"                     IsCaseSensitive="False" />         </telerik:CompositeFilterDescriptor.FilterDescriptors>     </telerik:CompositeFilterDescriptor> </telerik:RadGridView.FilterDescriptors>

Nun möchte ich gerne den Wert aus der "Filter"-TextBox in den Filtern verwenden. In etwa so:

<telerik:FilterDescriptor Member="Ort"         Operator="Contains"         Value="{Binding ElementName=txtOrt, Path=Text}"         IsCaseSensitive="False" />

 

Leider funktioniert das so nicht. Telerik sagt hier :

"Binding with the ElementName in the Silverlight GridView is not the best way to go. ElementName uses the logical tree to find the element in the element name binding. Parts of the telerik grid (and the sl data grid), do not participate in this part of the tree, or they have not been created yet at the time of binding (since most of the grid is dynamically generated)"

Jedoch ist es mir möglich über ein ViewModel-Property dieses Problem zu umgehen.

Ich erstelle mir dazu eine ViewModel-Klasse (wenn noch nicht vorhanden) und definiere in dieser die Property "SearchString". (Das ViewModel muss das Interface "INotifyPropertyChanged" implementieren)

private string searchString; public string SearchString {     get     {         return searchString;     }     set     {         searchString = value;         this.OnPropertyChanged("SearchString");     } }

Anschließend referenzieren wir das ViewModel in unserer View:

xmlns:vm="clr-namespace:Namespace.zum.ViewModel"

und in den Page oder Grid Resources instanziieren wir dieses:

<vm:MyViewModel x:Key="MyViewModel" />

Jetzt binden wir TextBox-Text an die Property "SearchString"

<TextBox Text="{Binding Source={StaticResource MyViewModel}, Path=SearchString, Mode=TwoWay}" Name="txtOrt" />

Und dasselbe machen wir in den FilterDescriptoren:

<telerik:FilterDescriptor Member="Ort"     Operator="Contains"     Value="{Binding Source={StaticResource MyViewModel}, Path=SearchString}"     IsCaseSensitive="False" /> <telerik:FilterDescriptor Member="Halle"     Operator="Contains"     Value="{Binding Source={StaticResource MyViewModel}, Path=SearchString}"     IsCaseSensitive="False" />

Funktioniert :-)

Aber Achtung: Wenn man nun in der TextBox Werte die in der Spalte "Ort" und "Halle" vorkommen einträgt, muss man zunächst den Focus der TextBox verlassen, um das Ergebnis zu sehen. Ich möchte aber gerne, das ich das Ergebnis direkt bei der Eingabe bekomme.

In WPF ist es möglich den UpdatSourceTrigger auf PropertyChanged zu setzen. In Silverlight finde ich lediglich default und explizit. Hmmm, was nun. Google angeschmissen und erfahren, das gibt es so nicht.

Auf der Suche nach einer Lösung sogleich beim .NET Rocker gelandet. Thomas stellt eine Klasse bereit, welche das PropertyChanged in Silverlight bereit stellt.

/// <summary> /// Supports a PropertyChanged-Trigger for DataBindings /// in Silverlight. Works just for TextBoxes /// (C) Thomas Claudius Huber 2009 /// http://www.thomasclaudiushuber.com /// </summary> public class BindingHelper {     public static bool GetUpdateSourceOnChange(DependencyObject obj)     {         return (bool)obj.GetValue(UpdateSourceOnChangeProperty);     }     public static void SetUpdateSourceOnChange         (DependencyObject obj, bool value)     {         obj.SetValue(UpdateSourceOnChangeProperty, value);     }     // Using a DependencyProperty as the backing store for …     public static readonly DependencyProperty         UpdateSourceOnChangeProperty = DependencyProperty.RegisterAttached("UpdateSourceOnChange",         typeof(bool), typeof(BindingHelper), new PropertyMetadata(false, OnPropertyChanged));     private static void OnPropertyChanged         (DependencyObject obj,         DependencyPropertyChangedEventArgs e)     {         var txt = obj as TextBox;         if (txt == null)             return;         if ((bool)e.NewValue)         {             txt.TextChanged += OnTextChanged;         }         else         {             txt.TextChanged -= OnTextChanged;         }     }     static void OnTextChanged(object sender,         TextChangedEventArgs e)     {         var txt = sender as TextBox;         if (txt == null)             return;         var be = txt.GetBindingExpression(TextBox.TextProperty);         if (be != null)         {             be.UpdateSource();         }     } }

Wenn wir diese verwenden, so wird nun beim TextChanged-Event wie erwartet das Grid aktualisiert.

<TextBox Text="{Binding Source={StaticResource MyViewModel}, Path=SearchString, Mode=TwoWay}" Name="txtOrt" />

Jetzt passt das.

Viel Spaß beim entwickeln : )