David Green David Green - 18 days ago 6
C# Question

Hiding DataGrid Column with Visibility Converter Not working

I have a window with a DataGrid that I want to hide certain columns based on the contents of the ObservableCollection that is the ItemSource for the DataGrid.

Based on this question:
Conditional element in xaml depending on the binding content

I wrote a VisibilityConverter:

public class StringLengthVisiblityConverter : IValueConverter
{
public StringLengthVisiblityConverter() { }
public Object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value == null || value.ToString().Length == 0)
{
return Visibility.Collapsed;
}
else
{
return Visibility.Visible;
}
}

public Object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}


Here is the XAML:

<DataGrid.Resources>
<local:StringLengthVisiblityConverter x:Key="VisConverter"/>
</DataGrid.Resources>
<DataGrid.Columns>

<DataGridTextColumn Header="Switch Port" Binding="{Binding FCPort}"/>
<DataGridTextColumn Width="*" Header="WWPN" Binding="{Binding Path=WWPN}"
Visibility="{Binding Path=WWPN, Converter={StaticResource VisConverter}}"/>
<DataGridTextColumn Header="FCID" Binding="{Binding Path=FCID}"
Visibility="{Binding Path=FCID, Converter={StaticResource VisConverter}}"/>

</DataGrid.Columns>
</DataGrid>


I loaded the collection with instances of a class where the WWPN and FCID are both null. I expected those columns to be hidden in the datagrid, but they were still visible. I added a breakpoint to the VisbilityConverter and ran it through a debugger but it doesn't look like it's getting called.

Answer

You need to check each value in the DataGridTextColumn and set Column.Visibility accordingly. To wrap all the logic I've derived from DataGrid.

public class MyDataGrid : DataGrid
{
    private void ValidateColumnVisibility()
    {
        if (Items.Count == 0 || Items[0] == CollectionView.NewItemPlaceholder) return;
        var itemClass = Items[0].GetType();
        foreach (var column in Columns)
        {
            if (column.GetType() == typeof(DataGridTextColumn))
            {
                // find the property that this column is bound to
                DataGridBoundColumn boundColumn = column as DataGridBoundColumn;
                Binding binding = boundColumn.Binding as Binding;
                string boundPropertyName = binding.Path.Path;
                var prop = itemClass.GetProperty(boundPropertyName);

                // Validating Column.Visibility when first value is null
                // when all values are null -> Visibility.Collapsed
                // bound value not a string -> Visibility.Visible
                // all bound strings empty  -> Visibility.Collapsed
                if (prop.GetValue(Items[0]) == null)
                {
                    column.Visibility = Visibility.Collapsed;
                    foreach (var item in Items)
                    {
                        if (item != CollectionView.NewItemPlaceholder)
                        {
                            if (prop.GetValue(item) != null)
                            {
                                if (prop.GetValue(item).GetType() != typeof(String))
                                    column.Visibility = Visibility.Visible;
                                else if (String.IsNullOrEmpty(prop.GetValue(item) as String) == false)
                                    column.Visibility = Visibility.Visible;
                            }
                        }
                    }
                }

                // First value not null and all bound strings empty -> Visibility.Collapsed
                else if (prop.GetValue(Items[0]).GetType() == typeof(String))
                {
                    column.Visibility = Visibility.Collapsed;
                    foreach (var item in Items)
                    {
                        if (item != CollectionView.NewItemPlaceholder)
                        {
                            if (String.IsNullOrEmpty(prop.GetValue(item) as String) == false)
                                column.Visibility = Visibility.Visible;
                        }
                    }
                }
            }
        }
    }

    protected override void OnItemsSourceChanged(
        IEnumerable oldValue, IEnumerable newValue)
    {
        base.OnItemsSourceChanged(oldValue, newValue);
        ValidateColumnVisibility();
    }

    protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
    {
        base.OnItemsChanged(e);
        ValidateColumnVisibility();
    }
}

Using MyDataGrid you no longer need to set Column.Visibility in XAML as it happens automagically.

Comments