Strings Sortieren wie im Windows-Explorer

7. März 2015 Keine Kommentare

Dieser erkennt, wenn Zahlen vorhanden sind und sortiert Dateinamen so, wie es Menschen erwarten. Z.B. wird aus den Dateien

  • A1B.txt
  • A10B.txt
  • A2B.txt

die sortierte Folge

  1. A1B.txt
  2. A2B.txt
  3. A10B.txt

Im Gegensatz zur lexikographischen Sortierung, welche die 10 vor die 2 setzen würde. Gesucht wurde als eine Methode zwei Strings miteinander zu vergleichen, und Zahlen als Zahlen zu verwenden. Dazu müssen beide Strings von vorne durchlaufen werden und jeweils in Zahlenteile und Text/Symbol-Teile unterteilt werden, welche dann jeweils miteinander verglichen werden können.

Dazu habe ich folgende Klasse implementiert, die von IComparer<string> erbt und so wiederverwendbar ist:

public class NumericStringComparer1 : IComparer<string> { private static char DecimalSeparator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator[0]; public NumericStringComparer1() { SortDirection = ListSortDirection.Ascending; } public string PropertyName { get; set; } public ListSortDirection SortDirection { get; set; } /// <summary> /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other. /// </summary> /// <param name="x">The first object to compare.</param> /// <param name="y">The second object to compare.</param> /// <returns> /// A signed integer that indicates the relative values of <paramref name="x" /> and <paramref name="y" />, as shown in the following table.Value Meaning Less than zero<paramref name="x" /> is less than <paramref name="y" />.Zero<paramref name="x" /> equals <paramref name="y" />.Greater than zero<paramref name="x" /> is greater than <paramref name="y" />. /// </returns> public int Compare(string x, string y) { int result = 0; if (x == null) result = -1; else if (y == null) result = 1; else { int i = 0, j = 0; double dx, dy; string sx = null, sy = null; while (i < x.Length && j < y.Length) { sx = GetStringPart(x, i, out i); sy = GetStringPart(y, j, out j); if (sx == null && sy == null) { // We only get the numbers, if we need to dx = GetNumericPart(x, i, out i); dy = GetNumericPart(y, j, out j); result = dx.CompareTo(dy); // For Numbers as Strings, which are equal, the string-length is also relevant if (result == 0) result = i.CompareTo(j); } else if (sx == null) result = -1; else if (sy == null) result = 1; else { result = sx.CompareTo(sy); } i = Math.Min(i + 1, x.Length); j = Math.Min(j + 1, x.Length); if (result != 0) break; } } if (result != 0 && this.SortDirection != ListSortDirection.Ascending) result *= -1; return result; } /// <summary> /// Gets the numeric value from the string starting at the given startIndex. /// The endIndex is set to the last character that is numeric /// If no numeric value is found, Double.NaN is returned. /// </summary> /// <param name="stringVal">The string value.</param> /// <param name="startIndex">The start index.</param> /// <param name="endIndex">The end index.</param> /// <returns></returns> internal static double GetNumericPart(string stringVal, int startIndex, out int endIndex) { double ret = Double.NaN; StringBuilder sb = new StringBuilder(); bool decimalAdded = false; endIndex = startIndex; for (int i = startIndex; i < stringVal.Length; i++) { if (char.IsNumber(stringVal[i])) { sb.Append(stringVal[i]); } else if (decimalAdded == false && stringVal[i] == DecimalSeparator) { decimalAdded = true; sb.Append(stringVal[i]); } else { break; } endIndex = i; } if (sb.Length > 0) ret = double.Parse(sb.ToString()); return ret; } /// <summary> /// Gets the string part from the given string starting at the given index. /// If nos string-part is found (meaning there is a number at the given startIndes), null is returned /// and the endIndex is set to the startIndex. /// /// </summary> /// <param name="stringVal">The string value.</param> /// <param name="startIndex">The start index.</param> /// <param name="endIndex">The end index.</param> /// <returns></returns> internal static string GetStringPart(string stringVal, int startIndex, out int endIndex) { endIndex = startIndex; bool stringFound = false; for (int i = startIndex; i < stringVal.Length; i++) { char c = stringVal[i]; if (char.IsDigit(stringVal[i]) == false) { endIndex = i; stringFound = true; } else { break; } } if (stringFound) { return stringVal.Substring(startIndex, endIndex - startIndex + 1); } else return null; } }

KategorienProgrammierung Tags: ,

CurrentPrincipal in einer Unity-WPF-Anwendung setzen

10. Oktober 2014 Keine Kommentare

Bei der Untersuchung von Claims Authentifizierung mit WPF ​innerhalb einer mehrschichtigen Anwendung bin ich auf ein Problem gestoßen, den aktuellen (Windows-) Benutzer zu setzen.

Die Anwendung verwendet Unity und somit auch einen entsprechenden Bootstrapper, welcher von UnityBootstrapper erbt. Ich wollte also innerhalb von InializeShell im Bootstrapper auch den aktuellen Benutzer setzen bzw. Claims für den aktuellen Benutzer hinzufügen.

Laut mehreren Orten im Web kann dies ja per Thread.CurrentPrincipal erfolgen. Es ist allerdings so, dass scheinbar mehrere Threads erstellt werden und so der CurrentPrincipal nicht erhalten bleibt. Eine Lösung ist, den Benutzer für die aktuelle Domäne der Anwendung zu setzen – also mit AppDomain.CurrentDomain.SetThreadPrincipal(principal).

Ich hoffe das kann dem einen oder anderen helfen.

KategorienProgrammierung Tags: , , ,

Eigene Sortierung von DataGrids in WPF

28. Mai 2014 Keine Kommentare

Ich war auf der Suche nach einer Möglichkeit, für einzelne Spalten in einem WPF-DataGrid eine eigene Sortierung zu verwenden. Ausgehend von der schönen Lösung bei Stackoverflow habe ich eine weitere angehängte Eigenschaft implementiert, mit deren Hlfe man festlegen kann, welche Eigenschaft der einzelnen Elemente zur Sortierung verwendet wird.

Das Schöne an dieser Variante ist die einfache Verwendung. Es muss nur im DataGrid definiert werden, dass CustomSort erlaubt ist und bei einzelnen Spalten muss noch gesagt werden, welcher Comparer verwendet werden soll (In diesem Fall eine Implemetierung von ICustomSorter.

<DataGrid ItemsSource="{Binding MyItems}" AutoGenerateColumns="False" local:CustomSortBehaviour.AllowCustomSort="True"> <DataGrid.Resources> <local:MyComparer x:Key="myCustomComparer"/> </DataGrid.Resources> <DataGrid.Columns> <DataGridTextColumn Header="MyHeader" local:CustomSortBehaviour.CustomSorter="{StaticResource myCustomComparer}" local:CustomSortBehaviour.CustomSortPropertyName="MySortingProperty" /> </DataGrid.Columns> </DataGrid>

Hier der Code für das Attached Behaviour:

public class CustomSortBehaviour { #region CustomSorter-Property public static readonly DependencyProperty CustomSorterProperty = DependencyProperty.RegisterAttached("CustomSorter", typeof(ICustomSorter), typeof(CustomSortBehaviour)); public static ICustomSorter GetCustomSorter(DataGridColumn gridColumn) { return (ICustomSorter)gridColumn.GetValue(CustomSorterProperty); } public static void SetCustomSorter(DataGridColumn gridColumn, ICustomSorter value) { gridColumn.SetValue(CustomSorterProperty, value); } #endregion #region AllowCustomSort-Property public static readonly DependencyProperty AllowCustomSortProperty = DependencyProperty.RegisterAttached("AllowCustomSort", typeof(bool), typeof(CustomSortBehaviour), new UIPropertyMetadata(false, OnAllowCustomSortChanged)); public static bool GetAllowCustomSort(DataGrid grid) { return (bool)grid.GetValue(AllowCustomSortProperty); } public static void SetAllowCustomSort(DataGrid grid, bool value) { grid.SetValue(AllowCustomSortProperty, value); } #endregion #region CustomSortPropertyName - Property public static readonly DependencyProperty CustomSortPropertyNameProperty = DependencyProperty.RegisterAttached("CustomSortPropertyName", typeof(string), typeof(CustomSortBehaviour)); public static string GetCustomSortPropertyName(DataGridColumn gridColumn) { return (string)gridColumn.GetValue(CustomSortPropertyNameProperty); } public static void SetCustomSortPropertyName(DataGridColumn gridColumn, string value) { gridColumn.SetValue(CustomSortPropertyNameProperty, value); } #endregion private static void OnAllowCustomSortChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var existing = d as DataGrid; if (existing == null) return; var oldAllow = (bool)e.OldValue; var newAllow = (bool)e.NewValue; if (!oldAllow && newAllow) { existing.Sorting += HandleCustomSorting; } else { existing.Sorting -= HandleCustomSorting; } } private static void HandleCustomSorting(object sender, DataGridSortingEventArgs e) { var dataGrid = sender as DataGrid; if (dataGrid == null || GetAllowCustomSort(dataGrid) == false) return; var listColView = dataGrid.ItemsSource as ListCollectionView; if (listColView == null) throw new Exception("The DataGrid's ItemsSource property must be of type ListCollectionView"); // Sanity check var sorter = GetCustomSorter(e.Column); if (sorter == null) return; var propertyName = GetCustomSortPropertyName(e.Column); // prevent the default sorter from working e.Handled = true; var direction = (e.Column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending; e.Column.SortDirection = sorter.SortDirection = direction; // Set Property to use if (propertyName != null) { sorter.PropertyName = propertyName; } else { DataGridBoundColumn boundColumn = e.Column as DataGridBoundColumn; if (boundColumn != null) { Binding binding = (Binding)boundColumn.Binding; sorter.PropertyName = binding.Path.Path; } } listColView.CustomSort = sorter; } } public interface ICustomSorter : IComparer { ListSortDirection SortDirection { get; set; } string PropertyName { get; set; } }

KategorienProgrammierung Tags: ,

SSDT in Visual Studio 2012 integrieren

7. August 2013 Keine Kommentare

Heute war es soweit: Ich wollte den (fast) endgültigen Umstieg auf Visual Studio 2012 einleiten. Dafür fehlte mir nur SSDT (a.k.a. SQL Server Data Tools, a.k.a. Business Intelligence Developer Studio). Da ich schon Visual Studio 2012 installiert hatte, wollte ich natürlich nur die entsprechenden Komponenten nachinstallieren (ohne lokalen SQL Server). Nach langem Suchen und misslungenen Versuchen habe ich’s gefunden: Durch die Installation des eigenständigen SSDT Pakets konnte ich meine alten Berichts-Projekte (also .rptproj) wieder öffnen.

Überreden von Visual Studio zur Zusammenarbeit mit nicht installiertem Office

1. Juli 2013 Keine Kommentare

Heute musste ich ein älteres Office-Addin Projekt (in diesem Fall Excel 2003) von mir in Visual Studio öffnen um es zu bearbeiten. Nach einem Klick auf Debuggen, meldete Visual Studio prompt, dass das Debuggen Vorgang nicht möglich sei, da die entsprechende Office-Version fehlte (Installiert ist Office 2013).

Nach ein wenig Suchen konnte habe ich festgestellt, das die Office-Version welche verwendet werden soll einfach nur in der Projektdatei fest eingetragen ist (bzw. der entspr. Registry-Schlüssel). Um dies zu ändern sind ein paar wenige Schritte nötig (auf eigene Gefahr, da dies ganz bestimmt nicht offiziell unterstützt ist!):

  1. Projektdatei öffnen (.csproj bei C#-Projekten) öffnen
  2. Nach dieser Zeile suchen:
    DebugInfoExeName=”#Software\Microsoft\Office\11.0\Excel\InstallRoot\Path#excel.exe”
  3. Hier muss dann einfach nur die Versionsnummer durch die ersetzt werden, die aktuell ist (bei 2013 wäre das 15, für eine Übersicht, siehe hier)
  4. Projektdatei speichern
  5. Projekt öffnen und Happy Coding!

MDT mit WDS nutzen

4. April 2013 Keine Kommentare

Die Windows Deployment Services sind schon eine feine Sache. Man kann Installationen von Rechnern per Image wunderbar an Clients verteilen und hat so nach wenig Zeit ein frisch installiertes System. Wenn man etwas mehr will, bietet sich das Microsoft Deployment Toolkit an.

Die beiden Dienste schreien natürlich geradezu danach, zusammen verwendet werden. An sich sollte dies relativ sein: Image im WDS bereitstellen, Tasks im MDT konfigurieren, MDT-Image als Startimage in den WDS importieren und alles ist wunderbar. Meine erste Erfahrung war leider, dass der Client per PXE bootet und das MDT-Image startet. Das einzige Ergebnis war allerdings, dass der Client nach ein paar Sekunden nachdem er kurz den Desktop-Hintergrund gezeigt hat ohne irgendeine Fehlermeldung neu gestartet ist.

Nach langer Suche bin ich nun zur Ursache des Problems vorgedrungen: Ich hatte das WAIK schon vor längerer Zeit installiert – genau genommen vor dem SP 1 für Windows 7.  Jetzt muss man wissen, dass das MDT die boot.wim aus dem WAIK verwendet, um ein Startabbild zu erzeugen. Da ich noch eine ältere Version des WAIK hatte, hat MDT die boot.wim von Windows 7 ohne SP 1 verwendet (In WDS an der Version 7.1.6000 erkennbar) . Nach langem Suchen und ständigem Übersehen, des Unterschieds im WDS im Vergleich zu meinen anderen Startimages (man ist manchmal schon extrem blind…) bin ich auf folgenden Post gestoßen.

Also WAIK aktualisiert und plötzlich geht’s! Die richtige Version ist die 7.1.6001 und wird auch so im WDS angezeigt.

Hinweis: Um das WAIK zu aktualisieren, braucht man diese ISO. Um die Aktualisierung zu installieren, braucht man nur den Inhalt der ISO in das Installationsverzeichnis von WAIK zu kopieren. Bei Standardpfaden also in das Verzeichnis “C:\Program Files\Windows AIK\Tools\PETools” (dabei natürlich alles Überschreiben).

Kaum war das geschehen, hat plötzlich alles wie von Zauberhand funktioniert.

KategorienWindows Tags: , ,

Literaturverzeichniss und Verweise mit Word 2007 / 2010

12. April 2010 3 Kommentare

Da ich gerade begonnen habe meine Diplomarbeit zu schreiben komme ich auch nicht um das leidige Thema Formatierung von Literaturverzeichnissen rum. In der Informatik ist der IEEE-Style dafür der quasi-Standard. Normalerweise sagt einem dann jeder “Nimm Latex der kann’s”, aber es geht auch mit Word extrem einfach (und meiner Meinung nach komfortabler).

Mehr…

Lenovo Thinkvantage Toolbox Icon unter Windows 7 entfernen

3. Dezember 2009 4 Kommentare

Heute habe ich mal wieder System Update laufen lassen, wobei eine neue Version der Lenovo Thinkvantage Toolbox installiert wurde. Nach der Installation habe ich mich über das tolle neue Icon auf meiner Startleiste gewundert und direkt gesucht, wie man es denn weg bekommt (Ja, so groß war die Freude)…

Mehr…

KategorienAllgemein Tags: ,

Windows 7: Hier ist der alte Hardware hinzufügen Dialog hin…

22. November 2009 Keine Kommentare

Seit Virtual PC nutze ich den MS Loopbackadapter um eine Netzwerkverbindung zwischen meinem Rechner und einer virtuellen Maschine herzustellen. Da gibt es leider keine eingebaute Möglichkeit, aber dank des Loopbackadapters ist das kein Problem. Jetzt nutze ich Windows 7 und musste mich auf die Suche nach dem alten Hardware hinzufügen Dialog machen – aber wer suchet de findet!

Mehr…

KategorienWindows, Windows 7 Tags: ,

Fehler in der Fensterbehandlung von WPF

2. Oktober 2009 Keine Kommentare

Gerade habe ich einen kleinen Fehler in WPF entdeckt. Scheinbar ist es so, dass Fensterinstanzen bei der Instanziierung schon zur WindowCollection hinzugefügt werden und nicht erst wenn Sie geöffnet werden. Dadurch muss man Sie erst entfernen, bevor man das Programm beenden kann, da es ansonsten denkt, dass noch ein Fenster offen ist.

Mehr…

KategorienProgrammierung Tags: ,