首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >WPF DataGrid:将DataGridColumn可见性绑定到ContextMenu MenuItems IsChecked (MVVM)

WPF DataGrid:将DataGridColumn可见性绑定到ContextMenu MenuItems IsChecked (MVVM)
EN

Stack Overflow用户
提问于 2009-10-13 15:12:11
回答 6查看 24.4K关注 0票数 10

我希望通过用户可用的ContextMenu控制DataGrid列可见性,方法是右键单击列标题。ContextMenu将显示所有可用列的名称。我使用的是MVVM设计模式。

我的问题是:如何将DataGridColumnVisibility属性绑定到位于ContextMenu中的MenuItemIsChecked属性。

一些模型代码:

代码语言:javascript
运行
复制
<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前缀

代码语言:javascript
运行
复制
<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>  

如果我不清楚,请让我知道,我会尝试详细说明。

干杯,

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2011-01-12 06:22:26

更新2021-09-29:粘贴了我以前博客中的代码。我已经很多年没有使用WPF了,我对它一无所知。这个站点对帖子长度有限制,所以我不得不使用CodePile分享一些示例代码。有关用法,请参阅底部的链接。我相信我的想法是我制作的示例在许多情况下都有效,而不是仅仅是自动生成的列。我正在处理一个项目,在这个项目中,列直到运行时才知道,并且可以动态更改。另请注意,“眼睛”图形有一个png文件,你可能需要自己的文件。

DataGridAPs.cs

代码语言:javascript
运行
复制
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
    }
}

Window1.xaml Window1.xaml.cs

票数 20
EN

Stack Overflow用户

发布于 2012-07-04 20:20:40

我一直在寻找绑定到WPF DataGrid列标题的列选择器上下文菜单的通用、XAML (即,没有代码隐藏)、automatic和simple示例。我已经阅读了数百篇文章,但似乎没有一篇是完全正确的,或者它们不够通用。下面是我认为的最佳组合解决方案:

首先,将这些放到资源字典中。我将把编写可见性/布尔转换器作为练习留给读者,以确保复选框在列可见时检查,反之亦然。请注意,通过为上下文菜单资源定义x:Shared="False“,它将获得特定于实例的状态,这意味着您可以对所有数据网格使用此单个模板/资源,并且它们都将维护自己的状态。

代码语言:javascript
运行
复制
<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是视图模型公开的某个数据源):

代码语言:javascript
运行
复制
<sdk:DataGrid ItemsSource="{Binding OrdersQuery}"
              AutoGenerateColumns="True" 
              ColumnHeaderStyle="{StaticResource ColumnHeaderStyle}"
              ContextMenu="{StaticResource GridItemsContextMenu}">

  <!-- rest of datagrid stuff goes here -->

</sdk:DataGrid>

这将为您提供以下内容:

  1. 绑定到列标题的上下文菜单,用作列选择器。
  2. 绑定到网格中的项的上下文菜单(对项本身执行操作-同样,绑定操作是读者的练习)。

希望这篇文章能帮助那些一直在寻找这样的例子的人。

票数 17
EN

Stack Overflow用户

发布于 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!:)简单有效

票数 6
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/1560871

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档