First time poster here but have been reading SO for ages and finally have run into a question that I've not been able to answer.
I've got a ListView hosting a GridView with multiple columns. One displays a price and another displays a currency code (CAD, USD, GBP, etc). This is all pulled out of SQL server using Entity Framework so the GridView is databound to a IEnumerable which stores the result of my query. The currency code is stored in a separate table with a localization string (en-US, en-GB) which (in a WinForms version of this app) was previously used in String.Format() to localize the currency to display the appropriate currency format and symbol.
The problem I have is in XAML binding the ConverterCulture of the Price binding to the Currency.LocalizedCultureName to get it to format correctly. Here's my current XAML:
<ListView Grid.Column="0" Name="pricingListingListView" ItemsSource="{Binding Source={StaticResource pricesByYear}}">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<GroupBox Header="{Binding Name}" Margin="0,0,0,10">
<ItemsPresenter/>
</GroupBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</ListView.GroupStyle>
<ListView.View>
<GridView>
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Date" DisplayMemberBinding="{Binding Source.Date}" Width="60" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Price" DisplayMemberBinding="{Binding Price, StringFormat='{}{0:C}', ConverterCulture={Binding Currency.LocalizedCultureName}}" Width="60" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Currency" DisplayMemberBinding="{Binding Currency.Code}" Width="60" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Unit" DisplayMemberBinding="{Binding Unit.Name}" Width="60" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Source" DisplayMemberBinding="{Binding Source.Name}" Width="125" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Project" DisplayMemberBinding="{Binding Project.Description}" Width="125" />
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Plant Type" DisplayMemberBinding="{Binding Project.Plant.Name}" Width="100" />
</GridView>
</ListView.View>
</ListView>
PricesByYear is simply a CollectionViewSource which pulls the IEnumerable out of a DP in my code behind. The data is pulled out correctly, just not formatted.
This compiles fine, but generates a XamlParseException when I load the window containing it: A 'Binding' cannot be set on the 'ConverterCulture' property of type 'Binding'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
The line generating the error is: <GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Price" DisplayMemberBinding="{Binding Price, StringFormat='{}{0:C}', ConverterCulture={Binding Currency.LocalizedCultureName}}" Width="60" />
The short-form objective is to display the price, but format it according to the culture name stored as a string value. Each row in the gridview could potentially be different. As it seems I cannot bind within a binding, is there an alternative way I could go about this?
Answer
Multibinding did the trick, here's the working XAML:
<local:LocalizeCurrencyMultiConverter x:Key="localizeCurrencyMultiConverter"/>
...
<GridViewColumn HeaderContainerStyle="{StaticResource leftAlignedColumnHeader}" Header="Price" Width="60">
<!--DisplayMemberBinding="{Binding Price, StringFormat='{}{0:C}', ConverterCulture={Binding Currency.LocalizedCultureName}}"-->
<GridViewColumn.DisplayMemberBinding>
<MultiBinding Converter="{StaticResource localizeCurrencyMultiConverter}">
<Binding Path="Price"/>
<Binding Path="Currency.LocalizedCultureName"/>
</MultiBinding>
</GridViewColumn.DisplayMemberBinding>
</GridViewColumn>
And the converter class:
public class LocalizeCurrencyMultiConverter :System.Windows.Data.IMultiValueConverter {
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
string localizedCurrency;
if (!values.Any() || values[0] == null)
throw new ArgumentException("Convert requires a minimum a price to display, and optionally a culture.");
double originalCurrency;
if (!double.TryParse(values[0].ToString(), out originalCurrency))
return values[0];
string localization = (values[1] ?? "en-CA").ToString();
try {
localizedCurrency = string.Format(System.Globalization.CultureInfo.CreateSpecificCulture(localization), "{0:c}", originalCurrency);
} catch {
localizedCurrency = string.Format(System.Globalization.CultureInfo.CreateSpecificCulture("en-CA"), "{0:c}", originalCurrency);
}
return localizedCurrency;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) {
throw new NotImplementedException();
return null;
}
}
Works like a charm.