Reputation: 127
I have googled around but with very limited luck. I have a question regarding editable WPF DataGrid; in a CellEditingTemplate a ComboBox is shown, but in CellTemplate a TextBox with corresponding ComboBox value is shown. My code looks something like this:
<DataGridTemplateColumn Header="Unit">
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox Name="comboBoxUnit" ItemsSource="{Binding ...}" SelectedValue="{Binding UnitId, ValidatesOnDataErrors=True}" SelectedValuePath="Id">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id}" />
<TextBlock Text=" " />
<TextBlock Text="{Binding Name}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="<would like to have selected Unit's Id and Name here>" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
How can I achieve this? Separate property in a class (to have UnitId and UnitName properties) is not a problem, I can add it, but how to bind both to ComboBox then? Can I access CellEditingTemplate ComboBox in CellTemplate? It seems like they are in "different namespaces" since I can name controls in both with same names...
Any ideas, pointers? Thanks in advance, DB
Upvotes: 3
Views: 4205
Reputation: 84656
The easiest way to achieve the same thing is to use a DataGridComboBoxColumn
.
However, in my current project we had so many problems with the DataGridComboBoxColumn
that we don't use it anymore. Instead we use a DataGridTemplateColumn
with a ComboBox
in the CellEditingTemplate
and a TextBlock
in the CellTemplate
(just like you're doing).
To be able to display data based on an Id (to get the same functionality in the TextBlock
as in the ComboBox
) we use a converter called CodeToDescriptionConverter
. It's usable like this
<TextBlock>
<TextBlock.Text>
<MultiBinding>
<MultiBinding.Converter>
<con:CodeToDescriptionConverter CodeAttribute="Id"
StringFormat="{}{0} - {1}">
<con:CodeToDescriptionConverter.DescriptionAttributes>
<sys:String>Id</sys:String>
<sys:String>Name</sys:String>
</con:CodeToDescriptionConverter.DescriptionAttributes>
</con:CodeToDescriptionConverter>
</MultiBinding.Converter>
<Binding Path="UnitId"/>
<Binding Path="Units"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Binding
is the value we look for (Id)Binding
is the IList
we look inCodeAttribute
is the name of the property we want to compare to the id (first Binding
)DescriptionAttributes
are the properties we want to return formatted as StringFormat
And in your case: Find the instance in Units
where the property Id
has the same value as UnitId
and for this instance return the values of Id
and Name
formatted as {0} - {1}
CodeToDescriptionConverter
uses reflection to achieve this
public class CodeToDescriptionConverter : IMultiValueConverter
{
public string CodeAttribute { get; set; }
public string StringFormat { get; set; }
public List<string> DescriptionAttributes { get; set; }
public CodeToDescriptionConverter()
{
StringFormat = "{0}";
DescriptionAttributes = new List<string>();
}
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 2 ||
values[0] == DependencyProperty.UnsetValue ||
values[1] == DependencyProperty.UnsetValue ||
values[0] == null ||
values[1] == null)
{
return null;
}
string code = values[0].ToString();
IList sourceCollection = values[values.Length - 1] as IList;
object[] returnDescriptions = new object[DescriptionAttributes.Count];
foreach (object obj in sourceCollection)
{
PropertyInfo codePropertyInfo = obj.GetType().GetProperty(CodeAttribute);
if (codePropertyInfo == null)
{
throw new ArgumentException("Code Property " + CodeAttribute + " not found");
}
string codeValue = codePropertyInfo.GetValue(obj, null).ToString();
if (code == codeValue)
{
for (int i = 0; i < DescriptionAttributes.Count; i++)
{
string descriptionAttribute = DescriptionAttributes[i];
PropertyInfo descriptionPropertyInfo = obj.GetType().GetProperty(descriptionAttribute);
if (descriptionPropertyInfo == null)
{
throw new ArgumentException("Description Property " + descriptionAttribute + " not found");
}
object descriptionObject = descriptionPropertyInfo.GetValue(obj, null);
string description = "";
if (descriptionObject != null)
{
description = descriptionPropertyInfo.GetValue(obj, null).ToString();
}
returnDescriptions[i] = description;
}
break;
}
}
// Ex. string.Format("{0} - {1} - {2}", arg1, arg2, arg3);
return string.Format(StringFormat, returnDescriptions);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
I uploaded a sample application here: CodeToDescriptionSample.zip.
It includeds a DataGridTemplateColumn
with CodeToDescriptionConverter
and a DataGridComboBoxColumn
that does the same thing. Hope this helps
Upvotes: 3