I have a behavior that takes the display name attribute and sets the column header of a data grid when it is auto generated. I works fine when the grid is bound to a collection of one specific type. If I have a collection of some base type it will not work although if I don't use my behavior it will have no problem auto generating columns of the derived class from the base class.
When the collection type is of the base class the only attributes that are found are from the base class, I want to be able to show the attributes from the implementing collection.
Ideas?
The problem is:
DataGridAutoGeneratingColumnEventArgs.PropertyDescriptor
is giving property information for the declared value type of the collection, not the actual item type.
behavior code:
#region Setup
/// <summary>
/// Called when [attached].
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
AssociatedObject.AutoGeneratingColumn += HandleAutoGeneratingColumns;
}
/// <summary>
/// Called when [detaching].
/// </summary>
protected override void OnDetaching()
{
base.OnDetaching();
AssociatedObject.AutoGeneratingColumn -= HandleAutoGeneratingColumns;
}
#endregion
#region Helpers
/// <summary>
/// Handles the automatic generating columns.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="dataGridAutoGeneratingColumnEventArgs">The <see cref="DataGridAutoGeneratingColumnEventArgs"/> instance containing the event data.</param>
private void HandleAutoGeneratingColumns(object sender, DataGridAutoGeneratingColumnEventArgs dataGridAutoGeneratingColumnEventArgs)
{
if (AssociatedObject != null)
{
var displayName = GetPropertyDisplayName(dataGridAutoGeneratingColumnEventArgs.PropertyDescriptor);
if (!string.IsNullOrEmpty(displayName))
{
dataGridAutoGeneratingColumnEventArgs.Column.Header = displayName;
dataGridAutoGeneratingColumnEventArgs.Column.Width = new DataGridLength(1, DataGridLengthUnitType.Star);
}
else
{
dataGridAutoGeneratingColumnEventArgs.Column.Visibility = Visibility.Collapsed;
}
}
}
/// <summary>
/// Gets the display name of the property.
/// </summary>
/// <param name="descriptor">The descriptor.</param>
/// <returns></returns>
[CanBeNull]
private static string GetPropertyDisplayName(object descriptor)
{
string returnValue = null;
var propertyDescriptor = descriptor as PropertyDescriptor;
if (propertyDescriptor != null)
{
var displayName = propertyDescriptor.Attributes[typeof(DisplayNameAttribute)] as DisplayNameAttribute;
if (displayName != null && !Equals(displayName, DisplayNameAttribute.Default))
{
returnValue = displayName.DisplayName;
}
}
else
{
var propertyInfo = descriptor as PropertyInfo;
if (propertyInfo != null)
{
var attributes = propertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), true);
foreach (var attribute in attributes)
{
var displayName = attribute as DisplayNameAttribute;
if (displayName != null && !Equals(displayName, DisplayNameAttribute.Default))
{
returnValue = displayName.DisplayName;
}
}
}
}
return returnValue;
}
Sample from one binding property
public class DefaultMatchedItems : ILookupItem
{
#region Properties
/// <summary>
/// Gets or sets the first column value.
/// </summary>
[DisplayName("Long Name")]
[CanBeNull]
public string FirstColumnValue { get; set; }
/// <summary>
/// Gets or sets the second column value.
/// </summary>
[DisplayName("Short Name")]
[CanBeNull]
public string SecondColumnValue { get; set; }
/// <summary>
/// Gets or sets the third column value.
/// </summary>
[DisplayName("Abbreviation")]
[CanBeNull]
public string ThirdColumnValue { get; set; }
/// <summary>
/// Gets or sets the identifier.
/// </summary>
[Browsable(false)]
[NotNull]
public string Identifier { get; set; }
public interface ILookupItem
{
/// <summary>
/// Determines whether [contains] [the specified value].
/// </summary>
/// <param name="value">The value.</param>
/// <param name="ignoreCase">if set to <c>true</c> [ignore case].</param>
/// <returns>
/// <c>true</c> if [contains] [the specified value]; otherwise, <c>false</c>.
/// </returns>
bool Contains(string value, bool ignoreCase = true);
/// <summary>
/// Gets the display value.
/// </summary>
/// <param name="identifier">The identifier.</param>
/// <returns>The first non blank section of the matching value</returns>
string GetDisplayValue(string identifier);
}