I wanted to format a TimeSpan in XAML and differentiate it from a normal time format, something like this 02h30m
After a bit of fiddling with curly brackets and back slashes I settled on:
"{Binding Path=Duration, StringFormat={}{0:hh}h{0:mm}m}"
One step forward, two steps back
"{Binding Path=Duration, StringFormat={}{0:hh}h{0:mm}m}"
<DataGrid.RowStyle>
<Style TargetType="DataGridRow"
BasedOn="{StaticResource {x:Type DataGridRow}}">
<Style.Triggers>
<DataTrigger Binding="{Binding BeingActioned}"
Value="True">
<Setter Property="FontStyle"
Value="Italic" />
<Setter Property="Foreground"
Value="Silver" />
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
public StudentCourseSearchView()
{
InitializeComponent();
SearchResults.Sorting += new DataGridSortingEventHandler(SearchResults_Sorting);
}
Then in the private SearchResults_Sorting method I determine if it is the StudentID column that is being sorted and instantiate the custom sorting class.
void SearchResults_Sorting(object sender, System.Windows.Controls.DataGridSortingEventArgs e)
{
DataGridColumn column = e.Column;
// I'm only interested in a custom sort for the StudentID column
if (column.SortMemberPath != "StudentID.Number") return;
IComparer comparer = null;
// Prevent the built-in sort from sorting
e.Handled = true;
ListSortDirection direction = (column.SortDirection != ListSortDirection.Ascending) ? ListSortDirection.Ascending : ListSortDirection.Descending;
// Set the sort order on the column
column.SortDirection = direction;
//use a ListCollectionView to do the sort.
ListCollectionView lcv = (ListCollectionView)CollectionViewSource.GetDefaultView(TypedViewModel.Students);
// Instantiate the custom sort class which implements IComparer
comparer = new StudentSearchResultStudentIdSort(direction);
// Apply the sort
lcv.CustomSort = comparer;
}
Finally, the StudentSearchResultStudentIdSort class implements the Compare method using the SortableStringValue extension method.
public class StudentSearchResultStudentIdSort : IComparer
{
ListSortDirection _direction;
public StudentSearchResultStudentIdSort(ListSortDirection direction)
{
_direction = direction;
}
public int Compare(object x, object y)
{
string studentIdX = (x as StudentSearchResult).StudentID.Value;
string studentIdY = (y as StudentSearchResult).StudentID.Value;
if (_direction == ListSortDirection.Ascending)
{
return studentIdX.SortableStringValue().CompareTo(studentIdY.SortableStringValue());
}
else
{
return studentIdY.SortableStringValue().CompareTo(studentIdX.SortableStringValue());
}
}
}
<Rectangle
Stroke="Black">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="StrokeThickness"
Value="1" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Selected}"
Value="True">
<Setter Property="StrokeThickness"
Value="2" />
</DataTrigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
SELECT
TABLE_NAME,
COLUMN_NAME,
DATA_TYPE,
CHARACTER_MAXIMUM_LENGTH,
NUMERIC_PRECISION,
NUMERIC_SCALE,
CHARACTER_SET_NAME
FROM
INFORMATION_SCHEMA.COLUMNS
ORDER BY
COLUMN_NAME
public List<string> SimpleColorList
{
get
{
List<string> colorList = new List<string>();
foreach (PropertyInfo pi in typeof(Colors).GetProperties())
{
colorList.Add(pi.Name);
}
return colorList;
}
}
And bound it to a ComboBox. Using Mode=OneTime means the ColorList is only built once.<ComboBox ItemsSource="{Binding Path=SimpleColorList, Mode=OneTime}"
SelectedValue="{Binding SelectedColor}"
Width="200" />
Adding a ItemTemplate to the ComboBox makes it a little more interesting:<ComboBox ItemsSource="{Binding Path=SimpleColorList, Mode=OneTime}"
SelectedValue="{Binding SelectedColor}"
Width="200">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Width="130"
Text="{Binding}" />
<Border BorderBrush="Black"
BorderThickness="1"
CornerRadius="4"
Margin="0,1,0,1"
Background="{Binding}"
Width="40"
Height="18">
</Border>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
The straight alphabetic sorted list is fine but I wanted to sort by "color", in general terms from lighter to darker. I found some code on the web that would convert the RGB into the HSL color space. (I'm afraid I can't find the code again so my apologies to its author for the lack of attribution). public static class HslValueConverter
{
/// <summary>
/// Converts a WPF RGB color to an HSL color
/// </summary>
/// <param name="rgbColor">The RGB color to convert.</param>
/// <returns>An HSL color object equivalent to the RGB color object passed in.</returns>
public static HslColor RgbToHsl(string name, Color rgbColor)
{
// Initialize result
var hslColor = new HslColor();
hslColor.Name = name;
// Convert RGB values to percentages
double r = (double)rgbColor.R / 255;
var g = (double)rgbColor.G / 255;
var b = (double)rgbColor.B / 255;
var a = (double)rgbColor.A / 255;
// Find min and max RGB values
var min = Math.Min(r, Math.Min(g, b));
var max = Math.Max(r, Math.Max(g, b));
var delta = max - min;
/* If max and min are equal, that means we are dealing with
* a shade of gray. So we set H and S to zero, and L to either
* max or min (it doesn't matter which), and then we exit. */
//Special case: Gray
if (max == min)
{
hslColor.Hue = 0;
hslColor.Saturation = 0;
hslColor.Lightness = max;
return hslColor;
}
/* If we get to this point, we know we don't have a shade of gray. */
// Set L
hslColor.Lightness = (min max) / 2;
// Set S
if (hslColor.Lightness < 0.5)
{
hslColor.Saturation = delta / (max min);
}
else
{
hslColor.Saturation = delta / (2.0 - max - min);
}
// Set H
if (r == max) hslColor.Hue = (g - b) / delta;
if (g == max) hslColor.Hue = 2.0 (b - r) / delta;
if (b == max) hslColor.Hue = 4.0 (r - g) / delta;
hslColor.Hue *= 60;
if (hslColor.Hue < 0) hslColor.Hue = 360;
// Set A
hslColor.Alpha = a;
// Set return value
return hslColor;
}
}
public struct HslColor
{
public string Name { get; set; }
public double Alpha;
public double Hue;
public double Lightness;
public double Saturation;
}
Then I changed the property (Note: the property name has changed to SortedColorList, so the ComboBox Binding will have to be altered) "getter" to sort by Saturation, Hue and Lightness. I also decided to exclude the Transparent color. public List<string> SortedColourList
{
get
{
List<HslColor> colorList = new List<HslColor>();
foreach (PropertyInfo pi in typeof(Colors).GetProperties())
{
Color color = (Color)pi.GetValue(null, null);
// Only select non-Transparent colors
if (color.A != 0) colorList.Add(HslValueConverter.RgbToHsl(pi.Name, color));
}
return colorList.OrderBy(c => c.Saturation)
.OrderBy(c => c.Hue)
.OrderByDescending(c => c.Lightness)
.Select(c => c.Name).ToList<string>();
}
}
The SimpleColorPicker project is available on GoogleCode.