Reputation: 282805
I have a column defined like this:
<DataGridTextColumn Binding="{Binding Path=FileSizeBytes, Mode=OneWay}" Header="Size" IsReadOnly="True" />
But instead of displaying the file size as a big number, I'd like to display units, but still have it sort by the actual FileSizeBytes
. Is there some way I can run it through a function or something before displaying it?
@Igor:
Works great.
http://img200.imageshack.us/img200/4717/imageget.jpg
[ValueConversion(typeof(long), typeof(string))]
class FileSizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string[] units = { "B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" };
double size = (long)value;
int unit = 0;
while (size >= 1024)
{
size /= 1024;
++unit;
}
return String.Format("{0:0.#} {1}", size, units[unit]);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Upvotes: 6
Views: 5106
Reputation: 1072
You can try using StringFormat in your binding expression if you're using .NET 3.5SP1 or later. See this post on Lester's WPF Blog or this post at Vince Sibal's blog for some syntax examples. The addition of StringFormat to bindings will eliminate most needs for Value Converters and conveniently keep your formatting with your markup rather than off in another class somewhere. It's certainly a lot less typing, too.
Maybe something like this will work:
<DataGridTextColumn
Binding="{Binding Path=FileSizeBytes, Mode=OneWay, StringFormat='\{0:N0\} bytes'}"
Header="Size" IsReadOnly="True" />
I'm not sure if clicking on the header to sort the items will sort them as strings or as the underlying data type, though, so depending on what your formatting expression looks like, you may or may not get the desired sorting behavior.
Upvotes: 4
Reputation: 76500
Binding to a function is possible in WPF, but it's generally painful. In this case a more elegant approach would be to create another property which returns a formatted string and bind to that.
class FileInfo {
public int FileSizeBytes {get;set;}
public int FileSizeFormatted {
get{
//Using general number format, will put commas between thousands in most locales.
return FileSizeBytes.ToString("G");
}
}
}
In XAML, bind to FileSizeFormatted
:
<DataGridTextColumn Binding="{Binding Path=FileSizeBytes, Mode=OneWay}" Header="Size" IsReadOnly="True" />
EDIT Alternative solution, thanks to Charlie for pointing this out.
You could write your own value converter by implementing IValueConverter
.
[ValueConversion(typeof(int), typeof(string))]
public class IntConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
//needs more sanity checks to make sure the value is really int
//and that targetType is string
return ((int)value).ToString("G");
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
//not implemented for now
throw new NotImplementedException();
}
}
Then in XAML:
<UserControl.Resources>
<src:DateConverter x:Key="intConverter"/>
</UserControl.Resources>
...
<DataGridTextColumn Binding="{Binding Path=FileSizeBytes, Mode=OneWay, Converter={StaticResource intConverter}}" Header="Size" IsReadOnly="True" />
Upvotes: 2