Pages

Showing posts with label Formatting. Show all posts
Showing posts with label Formatting. Show all posts

Monday, 18 August 2014

String Formatting for TimeSpan

By default a TimeSpan is formatted like this hh:mm:ss

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}"

Friday, 2 March 2012

WPF String Formatting

There are a number of ways to use formatted strings and we seem to use nearly all of them in our application. Because the format of the formatting string varies slightly I thought I'd document the different techniques.
Firstly we need some simple properties for the XAML Bindings. Notice that the ToString version needs neither the index (0:) or the curly braces.
public partial class MainWindow : Window
{
   public String FormattedDate
   {
      get { return String.Format("{0:dd MMM yyy}", DateTime.Now); }
   }
   public String DateToString
   {
      get { return DateTime.Now.ToString("dd MMM yyy"); }
   }
   public DateTime RawDateTime
   {
      get { return DateTime.Now; }
   }
   public MainWindow()
   {
      InitializeComponent();
      DataContext = this;
   }
}
The first two TextBlocks bind directly to the pre-formatted properties.
      <TextBlock Text="{Binding FormattedDate}" />
      <TextBlock Text="{Binding DateToString}" />
Or we can bind to the raw data and use StringFormat. We can escape the curly braces in a couple of ways.
      <TextBlock Text="{Binding Path=RawDateTime, StringFormat=\{0:dd MMM yyyy\}}" />
      <TextBlock Text="{Binding Path=RawDateTime, StringFormat={}{0:dd MMM yyyy}}" />
The leading curly braces in the second version can be replaced by constant text that is rendered as part of the formatted string. But it seems a bit "sensitive" so I don't use it.
      <TextBlock Text="{Binding Path=RawDateTime, StringFormat=Today is {0:dd MMM yyyy}}" />
Next up is a Converter for which we need an IValueConverter class:
class DateConverter : IValueConverter
{
   public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
   {
      if (value is DateTime)
      {
         return ((DateTime)value).ToString("dd MMM yyy");
      }
      return value;
   }

   public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
   {
      throw new NotImplementedException();
   }
}
And the corresponding XAML to use the Converter:
        xmlns:local="clr-namespace:WpfWaltz"
...
   <Window.Resources>
      <local:DateConverter x:Key="dateConverter"; />
   </Window.Resources>
...
      <TextBlock Text="{Binding Path=RawDateTime, Converter={StaticResource dateConverter}}" />
Finally, I had assumed that the indexer in the StringFormat was just a necessary evil since it was always 0: and hadn't considered using StringFormat with a MultiBinding like this:
      <TextBlock>
         <TextBlock.Text>
            <MultiBinding StringFormat="{}{0:dd MMM yyyy} {1:hh:mm:ss}">
               <Binding Path="RawDateTime" /> 
               <Binding Path="RawDateTime" />
            </MultiBinding>
         </TextBlock.Text>
      </TextBlock>
The StringFormatting project that has all the code can be downloaded from GoogleCode.

Tuesday, 31 January 2012

DataGridTextColumn Text Alignment (improved)

From Martin: I’ve been using this technique DataGridTextColumn Text Alignment, but I found it had 2 weird effects which I ran by Katharine yesterday.

1. If you clicked on the row just to the left of the value, it wasn’t registering and selecting the row
2. When the row was selected, the value was highlighted weirdly in a dark blue “selected” box

Apparently, that approach was overwriting the usual cell style template and making the textblock behave a little strangely.
With the ElementStyle change she suggested, the 2 issues above went away, so it seems to be the right way to do it.

Courtesy of Katharine, this is the correct way:
<DataGridTextColumn Header="Blah" Binding="{Binding Blah}">
   <DataGridTextColumn.ElementStyle>
      <Style TargetType="{x:Type TextBlock}">
         <Setter Property="TextAlignment" Value="Right" />
      </Style>
   </DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
Alternatively the style can be set up as StaticResource if it needs to be referenced by several DataGrid columns.

<Style x:Key="rightAlignedColumn" TargetType="{x:Type TextBlock}">
   <Setter Property="TextAlignment" Value="Right" />
</Style>

...

<DataGridTextColumn Header="Blah"
                    Binding="{Binding Blah}"
                    ElementStyle="{StaticResource rightAlignedColumn}">
</DataGridTextColumn>

Monday, 22 August 2011

DataGridTextColumn Text Alignment

This technique has been superceded by this one DataGridTextColumn Text Alignment
<DataGridTextColumn.CellStyle>
  <Style>
    <Setter Property="FrameworkElement.HorizontalAlignment" Value="Right" />
  </Style>
</DataGridTextColumn.CellStyle>

Wednesday, 15 July 2009

Databinding and Nullable Types

In this article (Using WPF Binding StringFormat property with nullable types) Karl Shifflett describes how to bind to properties that have a nullable type.

In summary, use TargetNullValue property of the binding like this:
<TextBox
Text="{Binding Path=NumberOfComputers,
TargetNullValue={x:Static sys:String.Empty},
StringFormat=\{0:D\}}" />

TargetNullValue requires .NET 3.5 Framework Service Pack 1. Previously this would have required a IValueConverter class to deal with the Null values.

Friday, 13 March 2009

Forcing OnPropertyChanged to update the UI

Having used my new Func<T, T> friend (see Action<T> and Func<T, TResult>) the bound TextBox refused to show the revised value. Even though OnPropertyChanged("...") had been called the property's "getter" was not called by WPF.

I don't really understand why.

To get round this problem I added a FormattingConverter to the TextBox called DoNothingConverter. Its presence is sufficient to ensure the UI updates.

public class DoNothingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value;
}
}

Action<T> and Func<T, TResult>

I wanted to pass a validation routine to my SetValue method that uses generics. So I used the Action delegate system.
protected void SetValue(T newValue, Action validate)
{
...
validate.Invoke(newValue);
...
}
That worked fine until I wanted to change the value (for formatting purposes). So I moved to using the Func delegate system instead. Since I want the result to be of the same type as the passed argument I used <T, T> instead of <T, TResult>.

protected T SetValue(T newValue, Func validate)
{
...
newValue = validate.Invoke(newValue);
...
}

Sunday, 5 October 2008

C# String Formatting

C# String Formatting

UPDATE: I show alternate ways to use string formats in WPF in this post:WPF String Formatting

This entry has been copied from SteveX (http://blog.stevex.net/index.php/string-formatting-in-csharp/) for my own easy reference.

The text inside the curly braces is {index[,alignment][:formatString]}. If alignment is positive, the text is right-aligned in a field the given number of spaces; if it’s negative, it’s left-aligned.

Strings

There really isn’t any formatting within a string, beyond it’s alignment. Alignment works for any argument being printed in a String.Format call.
Sample Generates
String.Format(”->{1,10}<-”, “Hello”); -> Hello<-
String.Format(”->{1,-10}<-”, “Hello”); ->Hello <-

Numbers

Basic number formatting specifiers:

Specifier Type Format Output (Passed Double 1.42) Output (Passed Int -12400)
c Currency {0:c} $1.42 -$12,400
d Decimal (Whole number) {0:d} System.FormatException -12400
e Scientific {0:e} 1.420000e+000 -1.240000e+004
f Fixed point {0:f} 1.42 -12400.00
g General {0:g} 1.42 -12400
n Number with commas for thousands {0:n} 1.42 -12,400
r Round trippable {0:r} 1.42 System.FormatException
x Hexadecimal {0:x4} System.FormatException cf90

Custom number formatting:

Specifier Type Example Output (Passed Double 1500.42) Note
0 Zero placeholder {0:00.0000} 1500.4200 Pads with zeroes.
# Digit placeholder {0:(#).##} (1500).42
. Decimal point {0:0.0} 1500.4
, Thousand separator {0:0,0} 1,500 Must be between two zeroes.
,. Number scaling {0:0,.} 2 Comma adjacent to Period scales by 1000.
% Percent {0:0%} 150042% Multiplies by 100, adds % sign.
e Exponent placeholder {0:00e+0} 15e+2 Many exponent formats available.

;
Group separator
see below

The group separator is especially useful for formatting currency values which require that negative values be enclosed in parentheses. This currency formatting example at the bottom of this document makes it obvious:

Dates

Note that date formatting is especially dependant on the system’s regional settings; the example strings here are from my local locale.

Specifier Type Example (Passed System.DateTime.Now)
d Short date 10/12/2002
D Long date December 10, 2002
t Short time 10:11 PM
T Long time 10:11:29 PM
f Full date & time December 10, 2002 10:11 PM
F Full date & time (long) December 10, 2002 10:11:29 PM
g Default date & time 10/12/2002 10:11 PM
G Default date & time (long) 10/12/2002 10:11:29 PM
M Month day pattern December 10
r RFC1123 date string Tue, 10 Dec 2002 22:11:29 GMT
s Sortable date string 2002-12-10T22:11:29
u Universal sortable, local time 2002-12-10 22:13:50Z
U Universal sortable, GMT December 11, 2002 3:13:50 AM
Y Year month pattern December, 2002

The ‘U’ specifier seems broken; that string certainly isn’t sortable.

Custom date formatting:

Specifier Type Example Example Output
dd Day {0:dd} 10
ddd Day name {0:ddd} Tue
dddd Full day name {0:dddd} Tuesday
f, ff, … Second fractions {0:fff} 932
gg, … Era {0:gg} A.D.
hh 2 digit hour {0:hh} 10
HH 2 digit hour, 24hr format {0:HH} 22
mm Minute 00-59 {0:mm} 38
MM Month 01-12 {0:MM} 12
MMM Month abbreviation {0:MMM} Dec
MMMM Full month name {0:MMMM} December
ss Seconds 00-59 {0:ss} 46
tt AM or PM {0:tt} PM
yy Year, 2 digits {0:yy} 02
yyyy Year {0:yyyy} 2002
zz Timezone offset, 2 digits {0:zz} -05
zzz Full timezone offset {0:zzz} -05:00
: Separator {0:hh:mm:ss} 10:43:20
/ Separator {0:dd/MM/yyyy} 10/12/2002

Enumerations

Specifier Type
g Default (Flag names if available, otherwise decimal)
f Flags always
d Integer always
x Eight digit hex.

Some Useful Examples

String.Format(”{0:$#,##0.00;($#,##0.00);Zero}”, value);

This will output “$1,240.00? if passed 1243.50. It will output the same format but in parentheses if the number is negative, and will output the string “Zero” if the number is zero.

String.Format(”{0:(###) ###-####}”, 8005551212);

This will output “(800) 555-1212?.