A colleague of mine has a need to display a field in an Infragistics XamDataGrid that concatenates two other fields. He is binding to an ObservableCollection and wants the know the 'best' way to do this. I came up with three solutions.
1. Add a readonly property to the collection.
2. Write a multi-value converter and persuade the XamDataGrid to use it
3. Use a template field and a stackpanel
As usual, everything that isn't easy with an Infragistics control is very difficult.
Let's start by looking at solution 1. All I did was add a read-only property to the collection so that it looks like this.
public class cResult
{
public string Warehouse { get; set; }
public string StockNumber { get; set; }
public string WarehouseStock
{
get { return string.Format("{0}-{1}", Warehouse, StockNumber); }
}
}
And here is the XAML.
<igDP:TextField Label="Warehouse/Stock #" Name="WarehouseStock" Width="auto" />
But it seems much more fun to me to write a StringFormat multivalue converter and use that. Well, it would be if it wasn't such a pain to persuade the XamDataGrid to use it.
Here is the converter...
public class StringFormatConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return string.Format(parameter.ToString(), values);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
And there is the XAML, quite a mouthful, isn't it?
<igDP:TextField Label="Warehouse/Stock #" BindingType="Unbound" EditAsType="sys:String" Width="auto">
<igDP:TextField.Settings>
<igDP:FieldSettings>
<igDP:FieldSettings.EditorStyle>
<Style TargetType="igEditors:XamTextEditor">
<Setter Property="Value">
<Setter.Value>
<MultiBinding Converter="{StaticResource StringFormatConverter}" ConverterParameter='{}{0}-{1}'>
<Binding Path="DataItem.Warehouse"/>
<Binding Path="DataItem.StockNumber"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</igDP:FieldSettings.EditorStyle>
</igDP:FieldSettings>
</igDP:TextField.Settings>
</igDP:TextField>
Option 3 (template field) might be the most flexible if you don't need complex formatting . It certainly doesn't require as much XAML. Note the use of AlternativeBinding - that was the tricky part to figure out. Also note the use of TextBlock which has a smaller memory and CPU footprint than Label.
<igDP:TemplateField Label="Warehouse/Stock #" BindingType="UseAlternateBinding" AlternateBinding="{Binding}" Width="auto">
<igDP:TemplateField.DisplayTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Warehouse}"/>
<TextBlock Text="-"/>
<TextBlock Text="{Binding StockNumber}"/>
</StackPanel>
</DataTemplate>
</igDP:TemplateField.DisplayTemplate>
</igDP:TemplateField>
Here is a full example showing the two techniques side-by-side.
<Window x:Class="XamDataGridMultiBind.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:XamDataGridMultiBind"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:igDP="http://infragistics.com/DataPresenter"
xmlns:ig="http://schemas.infragistics.com/xaml"
xmlns:igEditors="http://infragistics.com/Editors"
mc:Ignorable="d"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<local:StringFormatConverter x:Key="StringFormatConverter" />
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<igDP:XamDataGrid Theme="LunaSilver" DataSource="{Binding Results}" Grid.Column="0" GroupByAreaLocation="None">
<igDP:XamDataGrid.FieldLayoutSettings>
<igDP:FieldLayoutSettings SelectionTypeRecord="Single" AutoGenerateFields="False" AllowFieldMoving="No"/>
</igDP:XamDataGrid.FieldLayoutSettings>
<igDP:XamDataGrid.FieldSettings>
<igDP:FieldSettings AllowSummaries="False" SummaryUIType="SingleSelect" LabelTextAlignment="Center" AllowEdit="False"/>
</igDP:XamDataGrid.FieldSettings>
<igDP:XamDataGrid.FieldLayouts>
<igDP:FieldLayout Description="Results">
<igDP:FieldLayout.Fields>
<igDP:TextField Label="Warehouse" Name="Warehouse" Width="auto"/>
<igDP:TextField Label="Stock #" Name="StockNumber" Width="auto"/>
<igDP:TextField Label="Warehouse/Stock #" BindingType="Unbound" EditAsType="sys:String" Width="auto">
<igDP:TextField.Settings>
<igDP:FieldSettings>
<igDP:FieldSettings.EditorStyle>
<Style TargetType="igEditors:XamTextEditor">
<Setter Property="Value">
<Setter.Value>
<MultiBinding Converter="{StaticResource StringFormatConverter}" ConverterParameter='{}{0}-{1}'>
<Binding Path="DataItem.Warehouse"/>
<Binding Path="DataItem.StockNumber"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</igDP:FieldSettings.EditorStyle>
</igDP:FieldSettings>
</igDP:TextField.Settings>
</igDP:TextField>
</igDP:FieldLayout.Fields>
</igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>
</igDP:XamDataGrid>
<igDP:XamDataGrid Theme="LunaSilver" DataSource="{Binding Results}" Grid.Column="1" GroupByAreaLocation="None">
<igDP:XamDataGrid.FieldLayoutSettings>
<igDP:FieldLayoutSettings SelectionTypeRecord="Single" AutoGenerateFields="False" AllowFieldMoving="No"/>
</igDP:XamDataGrid.FieldLayoutSettings>
<igDP:XamDataGrid.FieldSettings>
<igDP:FieldSettings AllowSummaries="False" SummaryUIType="SingleSelect" LabelTextAlignment="Center" AllowEdit="False"/>
</igDP:XamDataGrid.FieldSettings>
<igDP:XamDataGrid.FieldLayouts>
<igDP:FieldLayout Description="Results">
<igDP:FieldLayout.Fields>
<igDP:TextField Label="Warehouse" Name="Warehouse" Width="auto"/>
<igDP:TextField Label="Stock #" Name="StockNumber" Width="auto"/>
<igDP:TextField Label="Warehouse/Stock #" Name="WarehouseStock" Width="auto" />
</igDP:FieldLayout.Fields>
</igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>
</igDP:XamDataGrid>
<igDP:XamDataGrid Theme="LunaSilver" DataSource="{Binding Results}" Grid.Column="2" GroupByAreaLocation="None">
<igDP:XamDataGrid.FieldLayoutSettings>
<igDP:FieldLayoutSettings SelectionTypeRecord="Single" AutoGenerateFields="False" AllowFieldMoving="No"/>
</igDP:XamDataGrid.FieldLayoutSettings>
<igDP:XamDataGrid.FieldSettings>
<igDP:FieldSettings AllowSummaries="False" SummaryUIType="SingleSelect" LabelTextAlignment="Center" AllowEdit="False"/>
</igDP:XamDataGrid.FieldSettings>
<igDP:XamDataGrid.FieldLayouts>
<igDP:FieldLayout Description="Results">
<igDP:FieldLayout.Fields>
<igDP:TextField Label="Warehouse" Name="Warehouse" Width="auto"/>
<igDP:TextField Label="Stock #" Name="StockNumber" Width="auto"/>
<igDP:TemplateField Label="Warehouse/Stock #" BindingType="UseAlternateBinding" AlternateBinding="{Binding}" Width="auto">
<igDP:TemplateField.DisplayTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Warehouse}"/>
<TextBlock Text="-"/>
<TextBlock Text="{Binding StockNumber}"/>
</StackPanel>
</DataTemplate>
</igDP:TemplateField.DisplayTemplate>
</igDP:TemplateField>
</igDP:FieldLayout.Fields>
</igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>
</igDP:XamDataGrid>
</Grid>
</Window>
--------------------------------------------------------------------------------------
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Data;
namespace XamDataGridMultiBind
{
public class cResult
{
public string Warehouse { get; set; }
public string StockNumber { get; set; }
public string WarehouseStock
{
get { return string.Format("{0}-{1}", Warehouse, StockNumber); }
}
}
public partial class MainWindow : Window, INotifyPropertyChanged
{
private ObservableCollection<cResult> _Results = new ObservableCollection<cResult>() {
new cResult() { Warehouse = "Warehouse 1", StockNumber = "Stock Number 1" },
new cResult() { Warehouse = "Warehouse 2", StockNumber = "Stock Number 2" }
};
public ObservableCollection<cResult> Results
{
get { return _Results; }
set { SetProperty(ref _Results, value); }
}
public MainWindow()
{
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
public void SetProperty<T>(ref T storage, T value, [CallerMemberName] string name = "")
{
if (!Object.Equals(storage, value))
{
storage = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
public class StringFormatConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return string.Format(parameter.ToString(), values);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}