Archiv

Artikel Tagged ‘.Net’

Strings Sortieren wie im Windows-Explorer

7. März 2015 Keine Kommentare

Der Windows Explorer sortiert Dateinamen seit den letzten Windows-Versionen so, wie es Menschen erwarten und nicht so, wie es Programmierer tun – er beendet also die Ära der Dateinamen die mit 01, 02, usw. geendet haben. Er macht dies möglich indem er erkennt, wenn Zahlen vorhanden sind und Dateinamen so sortiert, 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: ,

Ü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!