我希望通过用户可用的ContextMenu控制DataGrid列可见性,方法是右键单击列标题。ContextMenu将显示所有可用列的名称。我使用的是MVVM设计模式。
我的问题是:如何将DataGridColumn的Visibility属性绑定到位于ContextMenu中的MenuItem的IsChecked属性。
一些模型代码:
<UserControl.Resources>
<ContextMenu x:Key="ColumnHeaderContextMenu">
<MenuItem Header="Menu Item..1" IsCheckable="True" />
</ContextMenu>
<Style x:Key="ColumnHeaderStyle"
TargetType="{x:Type toolkit:DataGridColumnHeader}">
<Setter Property="ContextMenu"
Value="{StaticResource ColumnHeaderContextMenu}" />
</Style>
<BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter" />
</UserControl.Resources> ...flaf前缀
<toolkit:DataGrid x:Name="MyGrid" AutoGenerateColumns="False"
ItemsSource="{Binding MyCollection, Mode=Default}"
EnableColumnVirtualization="True" IsReadOnly="True"
ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}">
<toolkit:DataGrid.Columns>
<toolkit:DataGridTextColumn Binding="{Binding Path=MyEntry}"
Header="MyEntry" Visibility="{Binding IsChecked, Converter=
{StaticResource booleanToVisibilityConverter}.... />
</toolkit:DataGrid.Columns>
</toolkit:DataGrid> 如果我不清楚,请让我知道,我会尝试详细说明。
干杯,
发布于 2011-01-12 06:22:26
更新2021-09-29:粘贴了我以前博客中的代码。我已经很多年没有使用WPF了,我对它一无所知。这个站点对帖子长度有限制,所以我不得不使用CodePile分享一些示例代码。有关用法,请参阅底部的链接。我相信我的想法是我制作的示例在许多情况下都有效,而不是仅仅是自动生成的列。我正在处理一个项目,在这个项目中,列直到运行时才知道,并且可以动态更改。另请注意,“眼睛”图形有一个png文件,你可能需要自己的文件。
DataGridAPs.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Markup;
using System.Windows.Media;
using Microsoft.Windows.Controls;
using Microsoft.Windows.Controls.Primitives;
namespace CanUserHideColumnDemo
{
public static class DataGridAPs
{
#region HideColumns
#region HideColumnsHeader
public static readonly DependencyProperty HideColumnsHeaderProperty =
DependencyProperty.RegisterAttached("HideColumnsHeader",
typeof(object), typeof(DataGridAPs));
public static object GetHideColumnsHeader(DataGrid obj)
{
return obj.GetValue(HideColumnsHeaderProperty);
}
public static void SetHideColumnsHeader(DataGrid obj, object value)
{
obj.SetValue(HideColumnsHeaderProperty, value);
}
#endregion HideColumnsHeader
#region HideColumnsHeaderTemplate
public static readonly DependencyProperty HideColumnsHeaderTemplateProperty =
DependencyProperty.RegisterAttached("HideColumnsHeaderTemplate",
typeof(DataTemplate), typeof(DataGridAPs));
public static DataTemplate GetHideColumnsHeaderTemplate(DataGrid obj)
{
return (DataTemplate)obj.GetValue(HideColumnsHeaderTemplateProperty);
}
public static void SetHideColumnsHeaderTemplate(DataGrid obj, DataTemplate value)
{
obj.SetValue(HideColumnsHeaderTemplateProperty, value);
}
#endregion HideColumnsHeaderTemplate
#region HideColumnsIcon
public static readonly DependencyProperty HideColumnsIconProperty =
DependencyProperty.RegisterAttached("HideColumnsIcon",
typeof(object), typeof(DataGridAPs));
public static object GetHideColumnsIcon(DataGrid obj)
{
return obj.GetValue(HideColumnsIconProperty);
}
public static void SetHideColumnsIcon(DataGrid obj, object value)
{
obj.SetValue(HideColumnsIconProperty, value);
}
#endregion HideColumnsIcon
#region CanUserHideColumns
public static readonly DependencyProperty CanUserHideColumnsProperty =
DependencyProperty.RegisterAttached("CanUserHideColumns",
typeof(bool), typeof(DataGridAPs),
new UIPropertyMetadata(false, OnCanUserHideColumnsChanged));
public static bool GetCanUserHideColumns(DataGrid obj)
{
return (bool)obj.GetValue(CanUserHideColumnsProperty);
}
public static void SetCanUserHideColumns(DataGrid obj, bool value)
{
obj.SetValue(CanUserHideColumnsProperty, value);
}
private static void OnCanUserHideColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
DataGrid dataGrid = d as DataGrid;
if (dataGrid == null)
return;
if ((bool)e.NewValue == false)
{
dataGrid.Loaded -= new RoutedEventHandler(dataGrid_Loaded);
RemoveAllItems(dataGrid);
return;
}
if (!dataGrid.IsLoaded)
{
dataGrid.Loaded -= new RoutedEventHandler(dataGrid_Loaded);
dataGrid.Loaded += new RoutedEventHandler(dataGrid_Loaded);
}
else
SetupColumnHeaders(dataGrid);
}
private static void dataGrid_Loaded(object sender, RoutedEventArgs e)
{
DataGrid dataGrid = sender as DataGrid;
if (dataGrid == null)
return;
if (BindingOperations.IsDataBound(dataGrid, DataGrid.ItemsSourceProperty))
{
Binding b = BindingOperations.GetBinding(dataGrid, DataGrid.ItemsSourceProperty);
dataGrid.TargetUpdated += new EventHandler<DataTransferEventArgs>(dataGrid_TargetUpdated);
string xaml = XamlWriter.Save(b);
Binding b2 = XamlReader.Parse(xaml) as Binding;
if (b2 != null)
{
b2.NotifyOnTargetUpdated = true;
BindingOperations.ClearBinding(dataGrid, DataGrid.ItemsSourceProperty);
BindingOperations.SetBinding(dataGrid, DataGrid.ItemsSourceProperty, b2);
}
}
else
SetupColumnHeaders(dataGrid);
}
private static void dataGrid_TargetUpdated(object sender, DataTransferEventArgs e)
{
if (e.Property != DataGrid.ItemsSourceProperty)
return;
DataGrid dataGrid = sender as DataGrid;
if (dataGrid == null)
return;
EventHandler handler = null;
handler = delegate
{
RemoveAllItems(dataGrid);
if (SetupColumnHeaders(dataGrid))
dataGrid.LayoutUpdated -= handler;
};
dataGrid.LayoutUpdated += handler;
}
private static DataGridColumnHeader[] GetColumnHeaders(DataGrid dataGrid)
{
if (dataGrid == null)
return null;
dataGrid.UpdateLayout();
DataGridColumnHeader[] columnHeaders = CustomVisualTreeHelper<DataGridColumnHeader>.FindChildrenRecursive(dataGrid);
return (from DataGridColumnHeader columnHeader in columnHeaders
where columnHeader != null && columnHeader.Column != null
select columnHeader).ToArray();
}
private static string GetColumnName(DataGridColumn column)
{
if (column == null)
return string.Empty;
if (column.Header != null)
return column.Header.ToString();
else
return string.Format("Column {0}", column.DisplayIndex);
}
private static MenuItem GenerateItem(DataGrid dataGrid, DataGridColumn column)
{
if (column == null)
return null;
MenuItem item = new MenuItem();
item.Tag = column;
item.Header = GetColumnName(column);
if (string.IsNullOrEmpty(item.Header as string))
return null;
item.ToolTip = string.Format("Toggle column '{0}' visibility.", item.Header);
item.IsCheckable = true;
item.IsChecked = column.Visibility == Visibility.Visible;
item.Checked += delegate
{
SetItemIsChecked(dataGrid, column, true);
};
item.Unchecked += delegate
{
SetItemIsChecked(dataGrid, column, false);
};
return item;
}
public static MenuItem[] GetAttachedItems(DataGridColumnHeader columnHeader)
{
if (columnHeader == null || columnHeader.ContextMenu == null)
return null;
ItemsControl itemsContainer = (from object i in columnHeader.ContextMenu.Items
where i is MenuItem && ((MenuItem)i).Tag != null && ((MenuItem)i).Tag.ToString() == "ItemsContainer"
select i).FirstOrDefault() as MenuItem;
if (itemsContainer == null)
itemsContainer = columnHeader.ContextMenu;
return (from object i in itemsContainer.Items
where i is MenuItem && ((MenuItem)i).Tag is DataGridColumn
select i).Cast<MenuItem>().ToArray();
}
private static DataGridColumn GetColumnFromName(DataGrid dataGrid, string columnName)
{
if (string.IsNullOrEmpty(columnName))
return null;
foreach (DataGridColumn column in dataGrid.Columns)
{
if (GetColumnName(column) == columnName)
return column;
}
return null;
}
private static DataGridColumnHeader GetColumnHeaderFromColumn(DataGrid dataGrid, DataGridColumn column)
{
if (dataGrid == null || column == null)
return null;
DataGridColumnHeader[] columnHeaders = GetColumnHeaders(dataGrid);
return (from DataGridColumnHeader columnHeader in columnHeaders
where columnHeader.Column == column
select columnHeader).FirstOrDefault();
}
public static void RemoveAllItems(DataGrid dataGrid)
{
if (dataGrid == null)
return;
foreach (DataGridColumn column in dataGrid.Columns)
{
RemoveAllItems(dataGrid, column);
}
}
public static void RemoveAllItems(DataGrid dataGrid, DataGridColumn column)
{
if (dataGrid == null || column == null)
return;
DataGridColumnHeader columnHeader = GetColumnHeaderFromColumn(dataGrid, column);
List<MenuItem> itemsToRemove = new List<MenuItem>();
if (columnHeader == null)
return;
// Mark items and/or items container for removal.
if (columnHeader.ContextMenu != null)
{
foreach (object item in columnHeader.ContextMenu.Items)
{
if (item is MenuItem && ((MenuItem)item).Tag != null
&& (((MenuItem)item).Tag.ToString() == "ItemsContainer" || ((MenuItem)item).Tag is DataGridColumn))
itemsToRemove.Add((MenuItem)item);
}
}
// Remove items and/or items container.
foreach (MenuItem item in itemsToRemove)
{
columnHeader.ContextMenu.Items.Remove(item);
}
}
public static void ResetupColumnHeaders(DataGrid dataGrid)
{
RemoveAllItems(dataGrid);
SetupColumnHeaders(dataGrid);
}
private static void SetItemIsChecked(DataGrid dataGrid, DataGridColumn column, bool isChecked)
{
if (dataGrid == null || column == null)
return;
// Deny request if there are no other columns visible. Otherwise,
// they'd have no way of changing the visibility of any columns
// again.
//if (!isChecked && (from DataGridColumn c in dataGrid.Columns
// where c.Visibility == Visibility.Visible
// select c).Count() < 2)
// return;
if (isChecked && column.Visibility != Visibility.Visible)
{
ShowColumn(dataGrid, column);
}
else if (!isChecked)
column.Visibility = Visibility.Hidden;
DataGridColumnHeader[] columnHeaders = GetColumnHeaders(dataGrid);
ItemsControl itemsContainer = null;
object containerHeader = GetHideColumnsHeader(dataGrid);
foreach (DataGridColumnHeader columnHeader in columnHeaders)
{
itemsContainer = null;
if (columnHeader != null)
{
if (columnHeader.ContextMenu == null)
continue;
itemsContainer = (from object i in columnHeader.ContextMenu.Items
where i is MenuItem && ((MenuItem)i).Header == containerHeader
select i).FirstOrDefault() as MenuItem;
}
if (itemsContainer == null)
itemsContainer = columnHeader.ContextMenu;
foreach (object item in itemsContainer.Items)
{
if (item is MenuItem && ((MenuItem)item).Tag != null && ((MenuItem)item).Tag is DataGridColumn
&& ((MenuItem)item).Header.ToString() == GetColumnName(column))
{
((MenuItem)item).IsChecked = isChecked;
}
}
}
}
private static void SetupColumnHeader(DataGridColumnHeader columnHeader)
{
if (columnHeader == null)
return;
DataGrid dataGrid = CustomVisualTreeHelper<DataGrid>.FindAncestor(columnHeader);
if (dataGrid == null)
return;
DataGridColumnHeader[] columnHeaders = GetColumnHeaders(dataGrid);
if (columnHeaders == null)
return;
SetupColumnHeader(dataGrid, columnHeaders, columnHeader);
}
private static void SetupColumnHeader(DataGrid dataGrid, DataGridColumnHeader[] columnHeaders, DataGridColumnHeader columnHeader)
{
if (columnHeader.ContextMenu == null)
columnHeader.ContextMenu = new ContextMenu();
ItemsControl itemsContainer = null;
itemsContainer = columnHeader.ContextMenu;
object containerHeader = GetHideColumnsHeader(dataGrid);
if (containerHeader != null)
{
MenuItem ic = (from object i in columnHeader.ContextMenu.Items
where i is MenuItem && ((MenuItem)i).Tag != null && ((MenuItem)i).Tag.ToString() == "ItemsContainer"
select i).FirstOrDefault() as MenuItem;
if (ic == null)
{
itemsContainer = new MenuItem()
{
Header = containerHeader,
HeaderTemplate = GetHideColumnsHeaderTemplate(dataGrid) as DataTemplate,
Icon = GetHideColumnsIcon(dataGrid),
Tag = "ItemsContainer"
};
columnHeader.ContextMenu.Items.Add(itemsContainer);
}
else
return;
}
foreach (DataGridColumnHeader columnHeader2 in columnHeaders)
{
if (columnHeader2 != columnHeader
&& itemsContainer is ContextMenu
&& columnHeader2.ContextMenu == itemsContainer)
{
continue;
}
itemsContainer.Items.Add(GenerateItem(dataGrid, columnHeader2.Column));
}
}
public static bool SetupColumnHeaders(DataGrid dataGrid)
{
DataGridColumnHeader[] columnHeaders = GetColumnHeaders(dataGrid);
if (columnHeaders == null || columnHeaders.Count() == 0)
return false;
RemoveAllItems(dataGrid);
columnHeaders = GetColumnHeaders(dataGrid);
foreach (DataGridColumnHeader columnHeader in columnHeaders)
{
SetupColumnHeader(dataGrid, columnHeaders, columnHeader);
}
return true;
}
/// <summary>
/// Shows a column within the datagrid, which is not straightforward
/// because the datagrid not only hides a column when you tell it to
/// do so, but it also completely destroys its associated column
/// header. Meaning we need to set it up again. Before we can do
/// so we have to turn all columns back on again so we can get a
/// complete list of their column headers, then turn them back off
/// again.
/// </summary>
/// <param name="dataGrid"></param>
/// <param name="column"></param>
private static void ShowColumn(DataGrid dataGrid, DataGridColumn column)
{
if (dataGrid == null || column == null)
return;
column.Visibility = Visibility.Visible;
// Turn all columns on, but store their original visibility so we
// can restore it after we're done.
Dictionary<DataGridColumn, Visibility> vis = new Dictionary<DataGridColumn, Visibility>();
foreach (DataGridColumn c in dataGrid.Columns)
{
vis.Add(c, c.Visibility);
c.Visibility = Visibility.Visible;
}
dataGrid.UpdateLayout();
DataGridColumnHeader columnHeader = GetColumnHeaderFromColumn(dataGrid, column);
SetupColumnHeader(columnHeader);
foreach (DataGridColumn c in vis.Keys)
{
if ((Visibility)vis[c] != Visibility.Visible)
{
c.Visibility = (Visibility)vis[c];
}
}
dataGrid.UpdateLayout();
// Now we need to uncheck items that are associated with hidden
// columns.
SyncItemsOnColumnHeader(columnHeader);
}
private static void SyncItemsOnColumnHeader(DataGridColumnHeader columnHeader)
{
bool isVisible;
foreach (MenuItem item in GetAttachedItems(columnHeader))
{
if (item.Tag is DataGridColumn)
{
isVisible = ((DataGridColumn)item.Tag).Visibility == Visibility.Visible ? true : false;
if (item.IsChecked != isVisible)
{
item.IsChecked = isVisible;
}
}
}
}
#endregion CanUserHideColumns
#region CustomVisualTreeHelper
private static class CustomVisualTreeHelper<TReturn> where TReturn : DependencyObject
{
public static TReturn FindAncestor(DependencyObject descendant)
{
DependencyObject parent = descendant;
while (parent != null && !(parent is TReturn))
{
parent = VisualTreeHelper.GetParent(parent);
}
if (parent != null)
{
return (TReturn)parent;
}
return default(TReturn);
}
public static TReturn FindChild(DependencyObject parent)
{
int childCount = VisualTreeHelper.GetChildrenCount(parent);
DependencyObject child = null;
for (int childIndex = 0; childIndex < childCount; childIndex++)
{
child = VisualTreeHelper.GetChild(parent, childIndex);
if (child is TReturn)
{
return (TReturn)(object)child;
}
}
return default(TReturn);
}
public static TReturn FindChildRecursive(DependencyObject parent)
{
int childCount = VisualTreeHelper.GetChildrenCount(parent);
DependencyObject child = null;
for (int childIndex = 0; childIndex < childCount; childIndex++)
{
child = VisualTreeHelper.GetChild(parent, childIndex);
if (child is TReturn)
{
return (TReturn)(object)child;
}
else
{
child = CustomVisualTreeHelper<TReturn>.FindChildRecursive(child);
if (child is TReturn)
{
return (TReturn)(object)child;
}
}
}
return default(TReturn);
}
public static TReturn[] FindChildren(DependencyObject parent)
{
int childCount = VisualTreeHelper.GetChildrenCount(parent);
DependencyObject child = null;
List<TReturn> children = new List<TReturn>(childCount);
for (int childIndex = 0; childIndex < childCount; childIndex++)
{
child = VisualTreeHelper.GetChild(parent, childIndex);
if (child is TReturn)
{
children[childIndex] = (TReturn)(object)child;
}
}
return children.ToArray();
}
public static TReturn[] FindChildrenRecursive(DependencyObject parent)
{
int childCount = VisualTreeHelper.GetChildrenCount(parent);
DependencyObject child = null;
List<TReturn> children = new List<TReturn>();
for (int childIndex = 0; childIndex < childCount; childIndex++)
{
child = VisualTreeHelper.GetChild(parent, childIndex);
if (child is TReturn)
{
children.Add((TReturn)(object)child);
}
children.AddRange(CustomVisualTreeHelper<TReturn>.FindChildrenRecursive(child));
}
return children.ToArray();
}
}
#endregion CustomVisualTreeHelper
#endregion HideColumns
}
}发布于 2012-07-04 20:20:40
我一直在寻找绑定到WPF DataGrid列标题的列选择器上下文菜单的通用、XAML (即,没有代码隐藏)、automatic和simple示例。我已经阅读了数百篇文章,但似乎没有一篇是完全正确的,或者它们不够通用。下面是我认为的最佳组合解决方案:
首先,将这些放到资源字典中。我将把编写可见性/布尔转换器作为练习留给读者,以确保复选框在列可见时检查,反之亦然。请注意,通过为上下文菜单资源定义x:Shared="False“,它将获得特定于实例的状态,这意味着您可以对所有数据网格使用此单个模板/资源,并且它们都将维护自己的状态。
<Converters:VisiblityToInverseBooleanConverter x:Key="VisiblityToInverseBooleanConverter"/>
<ContextMenu x:Key="ColumnChooserMenu" x:Shared="False"
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}"
ItemsSource="{Binding Columns, RelativeSource={RelativeSource AncestorType={x:Type sdk:DataGrid}}}">
<ContextMenu.ItemContainerStyle>
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="AutomationProperties.Name" Value="{Binding Header}"/>
<Setter Property="IsCheckable" Value="True" />
<Setter Property="IsChecked" Value="{Binding Visibility, Mode=TwoWay, Converter={StaticResource VisiblityToInverseBooleanConverter}}" />
</Style>
</ContextMenu.ItemContainerStyle>
</ContextMenu>
<Style x:Key="ColumnHeaderStyle" TargetType="{x:Type Primitives:DataGridColumnHeader}">
<Setter Property="ContextMenu" Value="{StaticResource ColumnChooserMenu}" />
</Style>
<ContextMenu x:Key="GridItemsContextMenu" >
<MenuItem Header="Launch Do Some other action"/>
</ContextMenu>然后按如下方式定义模型(其中OrdersQuery是视图模型公开的某个数据源):
<sdk:DataGrid ItemsSource="{Binding OrdersQuery}"
AutoGenerateColumns="True"
ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}"
ContextMenu="{StaticResource GridItemsContextMenu}">
<!-- rest of datagrid stuff goes here -->
</sdk:DataGrid>这将为您提供以下内容:
希望这篇文章能帮助那些一直在寻找这样的例子的人。
发布于 2011-07-25 18:45:51
我知道这有点老了。但我正在考虑这样做,这篇文章要简单得多:http://iimaginec.wordpress.com/2011/07/25/binding-wpf-toolkit%E2%80%99s-datagridcolumn-to-a-viewmodel-datacontext-propogation-for-datagrid-columns-the-mvvm-way-to-interact-with-datagridcolumn/
您所需要做的就是在列上设置DataContext,然后按照正常方式将可见性绑定到ViewModel!:)简单有效
https://stackoverflow.com/questions/1560871
复制相似问题