Let user select what columns to display on WPF DataGrid
My DataGrid
has a default set of开发者_开发问答 columns to display, but I'd also like to let the user select/un-select columns displayed on their application. Is there a relatively easy way to do that in WPF?
The DataGrid
is bound to a DataTable
.
Note: I may just go with a simple "Default Columns/All Columns" via RadioButton
solution if the above feature is too complicated.
The short answer is, bind the Visibility
property of each column to a boolean flag that you're able to set (via a CheckBox or some other mechanism), and use a BooleanToVisibilityConverter to make the column visibility Collapsed when the flag is unset.
Dig this similar question, and especially this answer! His blog post lists what would be my ideal solution. :)
Bind the DataGrid.Columns
to an ItemsControl
with a DataTemplate
that contains a CheckBox
for visibility-toggling, no code required except for the VisbilityToBoolConverter
:
<Window
...
DataContext="{Binding RelativeSource={RelativeSource Mode=Self}}" Loaded="Window_Loaded">
<Window.Resources>
<local:VisibilityToBoolConverter x:Key="VisibilityToBoolConv"/>
</Window.Resources>
<StackPanel Orientation="Vertical">
<DataGrid ItemsSource="{Binding Data}" Name="DGrid"/>
<ItemsControl ItemsSource="{Binding ElementName=DGrid, Path=Columns}" Grid.IsSharedSizeScope="True" Margin="5">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A"/>
<ColumnDefinition SharedSizeGroup="B"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Header}" Margin="5"/>
<CheckBox Grid.Column="1" IsChecked="{Binding Visibility, Converter={StaticResource VisibilityToBoolConv}}" Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Window>
Note: I have a TextBlock
which assumes the Column-Header to be a string, might need to be adjusted if that is not the case.
VisibilityConverter:
[ValueConversion(typeof(Visibility), typeof(bool))]
public class VisibilityToBoolConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility vis = (Visibility)value;
switch (vis)
{
case Visibility.Collapsed:
return false;
case Visibility.Hidden:
return false;
case Visibility.Visible:
return true;
}
return false;
}
public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
{
if ((bool)value) return Visibility.Visible;
else return Visibility.Collapsed;
}
#endregion
}
I prefer to extend DataGrid
class and add this functionality.
public class DataGridEx : DataGrid
{
public DataGridEx() : base()
{
// Create event for right click on headers
var style = new Style { TargetType = typeof(DataGridColumnHeader) };
var eventSetter = new EventSetter(MouseRightButtonDownEvent, new MouseButtonEventHandler(HeaderClick));
style.Setters.Add(eventSetter);
ColumnHeaderStyle = style;
}
private void HeaderClick(object sender, MouseButtonEventArgs e)
{
ContextMenu menu = new ContextMenu();
// Fill context menu with column names and checkboxes
var visibleColumns = this.Columns.Where(c => c.Visibility == Visibility.Visible).Count();
foreach (var column in this.Columns)
{
var menuItem = new MenuItem
{
Header = column.Header.ToString(),
IsChecked = column.Visibility == Visibility.Visible,
IsCheckable = true,
// Don't allow user to hide all columns
IsEnabled = visibleColumns > 1 || column.Visibility != Visibility.Visible
};
// Bind events
menuItem.Checked += (object a, RoutedEventArgs ea)
=> column.Visibility = Visibility.Visible;
menuItem.Unchecked += (object b, RoutedEventArgs eb)
=> column.Visibility = Visibility.Collapsed;
menu.Items.Add(menuItem);
}
// Open it
menu.IsOpen = true;
}
}
Now you can use this control instead of original DataGrid
:
<dg:DataGridEx ItemsSource="{Binding OfflineData, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False">
<dg:DataGridEx.Columns>
<DataGridTextColumn Binding="{Binding PortID}" Header="ID" Width="50" Visibility="Collapsed"/>
<DataGridTextColumn Binding="{Binding SwitchIP}" Header="Switch IP" Width="100" Visibility="Visible"/>
<DataGridTextColumn Binding="{Binding Port}" Header="Port" Width="50" Visibility="Visible"/>
</dg:DataGridEx.Columns>
</dg:DataGridEx>
Now user can right-click on column header to open menu with checkboxes:
精彩评论