WPF 4 DataGrid:显示和显示隐藏列

发布于 2024-10-23 18:26:58 字数 3813 浏览 1 评论 0原文

我正在尝试为 DataGrid 实现列选择器功能,如果我尝试将列标题的内容定义为不仅仅是字符串,则会遇到问题。下面是一个非常简化的示例,其中所有样式、视图模型、绑定等均被删除。

有 3 列:

第一列使用字符串作为标题。 第二列尝试将标题内容设置为带有工具提示的标签。 第三列将标题内容设置为带有工具提示的 TextBlock。

单击 A 列的“切换可见性”按钮效果很好。 B 列和 C 列的“切换可见性”按钮都会导致 InvalidOperationException,并显示消息“指定的元素已经是另一个元素的逻辑子元素。首先断开它的连接。”

<Window x:Class="DataGridColumnChoosing.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <StackPanel Orientation="Horizontal" Margin="0,10">
        <TextBlock Margin="15, 0">Toggle Visibility:</TextBlock>
        <Button Click="ToggleA">Column A</Button>
        <Button Click="ToggleB">Column B</Button>
        <Button Click="ToggleC">Column C</Button>
    </StackPanel>
    <!-- Main Fuel Mileage Datagrid -->
    <DataGrid  x:Name="mySampleDataGrid" Grid.Row="1"
                    AutoGenerateColumns="False" CanUserSortColumns="False" CanUserResizeRows="False" CanUserAddRows="False"
                    GridLinesVisibility="All" RowHeaderWidth="0">
        <DataGrid.Columns>
            <DataGridTemplateColumn x:Name="colA" Width="40*" IsReadOnly="True" Header="Column A">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>


            <DataGridTemplateColumn x:Name="colB" Width="40*" IsReadOnly="True" >
                <DataGridTemplateColumn.Header>
                    <Label Content="Column B" ToolTip="A short explanation of Column B"/>
                </DataGridTemplateColumn.Header>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

            <DataGridTemplateColumn x:Name="colC" Width="40*" IsReadOnly="True" >
                <DataGridTemplateColumn.Header>
                    <TextBlock Text="Column C" ToolTip="A short explanation of Column C " />
                </DataGridTemplateColumn.Header>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock  />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

在此示例中切换可见性的按钮的简单单击事件处理程序只是修改列的可见性。

    private void ToggleA(object sender, RoutedEventArgs e)
    {
        colA.Visibility = colA.Visibility == System.Windows.Visibility.Visible ? System.Windows.Visibility.Hidden : System.Windows.Visibility.Visible;
    }

    private void ToggleB(object sender, RoutedEventArgs e)
    {
        colB.Visibility = colB.Visibility == System.Windows.Visibility.Visible ? System.Windows.Visibility.Hidden : System.Windows.Visibility.Visible;
    }

    private void ToggleC(object sender, RoutedEventArgs e)
    {
        colC.Visibility = colC.Visibility == System.Windows.Visibility.Visible ? System.Windows.Visibility.Hidden : System.Windows.Visibility.Visible;
    }

谢谢大家。

I'm trying to implement column chooser functionality for a DataGrid and am running into problems if I try to define the content of the header for the column as something more than just a string. Below is a very simplified example with all styles, view models, binding, etc all stripped out.

There are 3 columns:

The first column uses a string for the header.
The second column tries to set the header content to a Label with a ToolTip.
The third column ties to set the header content to a TextBlock with a ToolTip.

Clicking the Toggle Visibility button for Column A works fine. The Toggle Visibility buttons for both Columns B and C cause an InvalidOperationException with the message "Specified element is already the logical child of another element. Disconnect it first."

<Window x:Class="DataGridColumnChoosing.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow" Height="350" Width="525">
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <StackPanel Orientation="Horizontal" Margin="0,10">
        <TextBlock Margin="15, 0">Toggle Visibility:</TextBlock>
        <Button Click="ToggleA">Column A</Button>
        <Button Click="ToggleB">Column B</Button>
        <Button Click="ToggleC">Column C</Button>
    </StackPanel>
    <!-- Main Fuel Mileage Datagrid -->
    <DataGrid  x:Name="mySampleDataGrid" Grid.Row="1"
                    AutoGenerateColumns="False" CanUserSortColumns="False" CanUserResizeRows="False" CanUserAddRows="False"
                    GridLinesVisibility="All" RowHeaderWidth="0">
        <DataGrid.Columns>
            <DataGridTemplateColumn x:Name="colA" Width="40*" IsReadOnly="True" Header="Column A">
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>


            <DataGridTemplateColumn x:Name="colB" Width="40*" IsReadOnly="True" >
                <DataGridTemplateColumn.Header>
                    <Label Content="Column B" ToolTip="A short explanation of Column B"/>
                </DataGridTemplateColumn.Header>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>

            <DataGridTemplateColumn x:Name="colC" Width="40*" IsReadOnly="True" >
                <DataGridTemplateColumn.Header>
                    <TextBlock Text="Column C" ToolTip="A short explanation of Column C " />
                </DataGridTemplateColumn.Header>
                <DataGridTemplateColumn.CellTemplate>
                    <DataTemplate>
                        <TextBlock  />
                    </DataTemplate>
                </DataGridTemplateColumn.CellTemplate>
            </DataGridTemplateColumn>
        </DataGrid.Columns>
    </DataGrid>
</Grid>

The simple click event handlers for the buttons that are toggling the visibility in this example are simply modifying the visibility of the columns.

    private void ToggleA(object sender, RoutedEventArgs e)
    {
        colA.Visibility = colA.Visibility == System.Windows.Visibility.Visible ? System.Windows.Visibility.Hidden : System.Windows.Visibility.Visible;
    }

    private void ToggleB(object sender, RoutedEventArgs e)
    {
        colB.Visibility = colB.Visibility == System.Windows.Visibility.Visible ? System.Windows.Visibility.Hidden : System.Windows.Visibility.Visible;
    }

    private void ToggleC(object sender, RoutedEventArgs e)
    {
        colC.Visibility = colC.Visibility == System.Windows.Visibility.Visible ? System.Windows.Visibility.Hidden : System.Windows.Visibility.Visible;
    }

Thanks all.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

一曲爱恨情仇 2024-10-30 18:26:58

当我在资源中定义了一个控件并尝试在多个控件的内容区域中使用它时,我曾经遇到过这个问题。这不起作用,因为该控件只能属于一个父级。

相反,我需要定义某种包含我想要的控件的模板,并设置对象的模板而不是直接设置内容。

你对@Gimno 的回答的评论让我认为情况确实如此。

尝试更改它,而不是直接在 DataGrid.Header 的内容中设置 Label/TextBox,而是将 DataGrid.HeaderTemplate 设置为包含 Label 或 TextBox 的 DataTemplate。

编辑

这是一些示例代码

<DataGridTemplateColumn x:Name="colB" Width="40*" IsReadOnly="True" >
    <DataGridTemplateColumn.HeaderTemplate>
        <DataTemplate>
            <Label Content="Column B" ToolTip="A short explanation of Column B"/>
        </DataTemplate>
    </DataGridTemplateColumn.HeaderTemplate>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>

I had this issue once when I had a control defined in my Resources, and was trying to use it within multiple control's Content areas. That does not work because the control can only belong to one parent.

Instead, I needed to define a Template of some kind which contained the control I wanted, and set the Template of my object instead of the content directly.

Your comment on @Gimno's answer makes me think this is the case.

Try changing it so instead of setting a Label/TextBox in DataGrid.Header's content directly, set DataGrid.HeaderTemplate to a DataTemplate which contains the Label or TextBox.

EDIT

Here's some example code

<DataGridTemplateColumn x:Name="colB" Width="40*" IsReadOnly="True" >
    <DataGridTemplateColumn.HeaderTemplate>
        <DataTemplate>
            <Label Content="Column B" ToolTip="A short explanation of Column B"/>
        </DataTemplate>
    </DataGridTemplateColumn.HeaderTemplate>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
看轻我的陪伴 2024-10-30 18:26:58

我认为如果您只使用 DataGridTemplateColumn.HeaderStyle 而不是 DataGridTemplateColumn.Header

作为列 c 的示例:

<DataGridTemplateColumn x:Name="colC" Width="40*" IsReadOnly="True" >
    <DataGridTemplateColumn.HeaderStyle>
        <Style TargetType="DataGridColumnHeader">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <TextBlock Text="Column C"  ToolTip="A short explanation of Column C "/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </DataGridTemplateColumn.HeaderStyle>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock  />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
  </DataGridTemplateColumn>
</DataGridTemplateColumn.HeaderStyle>

I think it would be easiest if you just use DataGridTemplateColumn.HeaderStyle instead of DataGridTemplateColumn.Header

As example for column c:

<DataGridTemplateColumn x:Name="colC" Width="40*" IsReadOnly="True" >
    <DataGridTemplateColumn.HeaderStyle>
        <Style TargetType="DataGridColumnHeader">
            <Setter Property="ContentTemplate">
                <Setter.Value>
                    <DataTemplate>
                        <TextBlock Text="Column C"  ToolTip="A short explanation of Column C "/>
                    </DataTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </DataGridTemplateColumn.HeaderStyle>
    <DataGridTemplateColumn.CellTemplate>
        <DataTemplate>
            <TextBlock  />
        </DataTemplate>
    </DataGridTemplateColumn.CellTemplate>
  </DataGridTemplateColumn>
</DataGridTemplateColumn.HeaderStyle>
不知在何时 2024-10-30 18:26:58

我喜欢 CodePlex 的列选择器解决方案:
DataGrid 行为

我清理代码并删除不必要的代码:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using Microsoft.Xaml.Behaviors;

namespace Behaviors
{
    public class WpfDataGridConfigurationBehavior : Behavior<DependencyObject>
    {

        #region "public Properties"


        public bool DontPersistVisibleColumns { get; set; }
        public bool DontPersistColumnsOrder { get; set; }

        public string ContextMenuChoices { get; set; } = "Alphabetical Menu,Show All Columns";

        public string DataGridName { get; set; }

        #endregion "public Properties"

        #region "private Properties"

        private DataGrid dataGrid;

        private ContextMenu theContextMenu; // Context Menu for the field chooser.

        private string AllColumnsHeaders { get; set; }
        private string AllColumnDisplayIndexes { get; set; }

        private int nBaseItems = 5;
        private MenuItem mnuAlpha;
        private MenuItem mnuShowAll;

        #endregion "Private Properties"

        protected override void OnAttached()
        {
            base.OnAttached();

            dataGrid = this.AssociatedObject as DataGrid;
            if (DataGridName == null) DataGridName = dataGrid.Name;

            ContextMenu_BuildStaticMenu();

            dataGrid.Loaded += (sender, e) => { DataGrid_Loaded(); };
            dataGrid.AutoGeneratedColumns += DataGrid_AutoGeneratedColumns;

            dataGrid.ColumnReordered += (sender, e) => { DataGrid_ColumnReordered(sender); };

            dataGrid.ContextMenuOpening += (sender, e) => { DataGrid_ContextMenuOpening(e); };

            dataGrid.LoadingRow += (sender, e) => { e.Row.Header = (e.Row.GetIndex() + 1).ToString(); };

            dataGrid.RowHeaderWidth = 0;
            var ns = new Style(typeof(DataGridRowHeader)) { BasedOn = dataGrid.RowHeaderStyle };
            ns.Setters.Add(new Setter(Control.FontWeightProperty, FontWeights.Bold));
            ns.Setters.Add(new Setter(Control.FontSizeProperty, 11.0));
            ns.Setters.Add(new Setter(Control.PaddingProperty, new Thickness(4, 0, 4, 0)));
            dataGrid.RowHeaderStyle = ns;
        }

        private void DataGrid_AutoGeneratedColumns(object sender, EventArgs e)
        {
            DataGrid_Loaded();
        }


        #region "DataGrid Events"


        private void DataGrid_ContextMenuOpening(ContextMenuEventArgs e)
        {
            var dep = (DependencyObject)e.OriginalSource;

            // iteratively traverse the visual tree
            while ((dep != null) && !(dep is DataGridCell) && !(dep is DataGridColumnHeader))
                dep = VisualTreeHelper.GetParent(dep);

            if (dep == null)
                return;

            if (dep is DataGridColumnHeader)
            {
                // do something
            }

            if (dep is DataGridCell)
            {
                // navigate further up the tree
                while ((dep != null) && !(dep is DataGridRow))
                    dep = VisualTreeHelper.GetParent(dep);

                var row = dep as DataGridRow;

                dataGrid.ItemContainerGenerator.IndexFromContainer(row);
            }
        }

        private void DataGrid_ColumnReordered(object sender)
        {
            Settings_SaveDisplayIndexes(sender);

            ContextMenu_BuildMenu();
        }

        private void DataGrid_Loaded()
        {
            ContextMenu_BuildMenu(false);

            VisibleColumns_Initialize();
        }

        #endregion "DataGrid Events"

        #region "ContextMenu Methods and Events"

        private void ContextMenu_BuildStaticMenu()
        {
            theContextMenu = new ContextMenu { FontSize = 11, StaysOpen = true };

            mnuAlpha = new MenuItem
            {
                Header = ContextMenuChoices.Split(',')[0],
                FontWeight = FontWeights.Bold,
                IsCheckable = true,
                StaysOpenOnClick = true
            };
            mnuAlpha.Click += (sender, e) => { ContextMenu_BuildMenu(); };

            mnuShowAll = new MenuItem
            {
                Header = ContextMenuChoices.Split(',')[1],
                FontWeight = FontWeights.Bold,
                IsCheckable = true,
                StaysOpenOnClick = true
            };
            mnuShowAll.Checked += (sender, e) => { VisibleColumns = AllColumnsHeaders; };
            mnuShowAll.Click += (sender, e) =>
            {
                if (mnuShowAll.IsChecked == false) VisibleColumns_Initialize();
            };


            theContextMenu.Items.Add(mnuShowAll);
            theContextMenu.Items.Add(mnuAlpha);
        }

        private void ContextMenu_BuildMenu(bool pbRebuild = true)
        {
            for (int i = theContextMenu.Items.Count - 1; i > 0; i--)
                if (((MenuItem)theContextMenu.Items[i]).FontWeight != FontWeights.Bold)
                    theContextMenu.Items.Remove(theContextMenu.Items[i]);

            nBaseItems = theContextMenu.Items.Count;

            // Attach the context menu to the DataGrid ColumnHeaders
            var headersPresenter = WpfDataGridConfigurationBehaviorFinder.FindChild<DataGridColumnHeadersPresenter>(dataGrid);
            ContextMenuService.SetContextMenu(headersPresenter, theContextMenu);

            if (VisibleColumns == null)
                throw (new SettingsPropertyNotFoundException("User's VisibleColumns setting not found."));

            // Get the current column ordering from user.config

            if (DisplayIndexes == null)
                throw (new SettingsPropertyNotFoundException("User's DisplayIndexes setting not found."));

            AllColumnDisplayIndexes = DisplayIndexes;
            string[] colIndexes = AllColumnDisplayIndexes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

            var dataGridColumns = dataGrid.Columns.OrderBy(x => x.DisplayIndex);

            // Sort the columns in display index order so menu header order matchs display column order
            if (pbRebuild == false)
                // Initially the datagrid column order is such that display indexes are the same as the col indexes
                if (colIndexes.Length > 0)
                    dataGridColumns = dataGrid.Columns.OrderBy(x => Convert.ToInt16(colIndexes[x.DisplayIndex]));

            if (mnuAlpha.IsChecked)
                dataGridColumns = dataGrid.Columns.OrderBy(x => x.Header.ToString());

            AllColumnsHeaders = "";
            foreach (var col in dataGridColumns)
            {
                // All column name to a list of all column headers for later use.
                AllColumnsHeaders = $"{col.Header.ToString().Replace("\n", " ").Replace("\r", " ")};{AllColumnsHeaders}";

                // Add new menu item in display order.
                ContextMenu_AddNewMenuItem(col);
            }

            string sTemp = VisibleColumns;
            VisibleColumns = null;
            VisibleColumns = sTemp;

        }

        private void ContextMenu_AddNewMenuItem(DataGridColumn col)
        {
            var menuItem = new MenuItem { Header = col.Header.ToString().Replace("\n", " ").Replace("\r", " "), StaysOpenOnClick = true };
            var saVisibleColumns = new List<string> { string.Empty };
            if (VisibleColumns != null)
            {
                saVisibleColumns = VisibleColumns.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList();
            }

            menuItem.IsChecked = (saVisibleColumns.Contains(menuItem.Header));
            menuItem.Click += (sender, e) => { ContextMenu_ColumnName_Click(sender); };

            theContextMenu.Items.Add(menuItem);
        }

        private void ContextMenu_ColumnName_Click(object sender)
        {
            var mi = sender as MenuItem;

            // Get the column name that was clicked
            string colName = mi.Header.ToString();


            // Capture new visible columns list
            Settings_SaveVisibleColumns(mi, colName);
        }

        #endregion "ContextMenu Methods and Events"

        #region "Settings Methods"

        private void Settings_SaveVisibleColumns(MenuItem mi, string colName)
        {
            if (theContextMenu.Items.Count - nBaseItems < dataGrid.Columns.Count)
                return;

            // Put the visible column names into an array
            var saVisibleColumns = VisibleColumns.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList();
            if (saVisibleColumns != null && saVisibleColumns.Count > 0 &&
                saVisibleColumns[saVisibleColumns.Count - 1].StartsWith("-"))
                saVisibleColumns.RemoveAt(saVisibleColumns.Count - 1);

            // If the menu item is unchecked (column is not visible)
            if (!mi.IsChecked)
                // Make the column visible by adding its name to the Visible Columns list
                saVisibleColumns.Add(colName);

            else
            // Hide the column by removing its name from the VisibleColumns list
            if (saVisibleColumns.Contains(colName) && saVisibleColumns.Count > 1)
                saVisibleColumns.Remove(colName);

            VisibleColumns = string.Join(";", saVisibleColumns) + ";";
        }

        private void Settings_SaveDisplayIndexes(object sender)
        {
            // Capture the new column order
            AllColumnDisplayIndexes = "";
            foreach (DataGridColumn col in ((DataGrid)sender).Columns)
            {
                AllColumnDisplayIndexes +=
                    (AllColumnDisplayIndexes.Length > 0 ? ";" : "") + col.DisplayIndex;
            }

            DisplayIndexes = AllColumnDisplayIndexes;
        }

        #endregion "Settings Methods"

        #region DisplayIndexes (DependencyProperty)

        public string DisplayIndexes
        {
            get { return (string)GetValue(DisplayIndexesProperty); }
            set { SetValue(DisplayIndexesProperty, value); }
        }

        public static readonly DependencyProperty DisplayIndexesProperty =
            DependencyProperty.Register("DisplayIndexes", typeof(string), typeof(WpfDataGridConfigurationBehavior), new PropertyMetadata("", OnDisplayIndexesChanged));

        private static void OnDisplayIndexesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((WpfDataGridConfigurationBehavior)d).DisplayIndexesChanged(e);
        }

        public void DisplayIndexesChanged(DependencyPropertyChangedEventArgs e)
        {
            // Persist the new column order
            if (dataGrid != null && !DontPersistColumnsOrder)
                Settings_Save(DataGridName + "DisplayIndexes", AllColumnDisplayIndexes);
        }

        #endregion "DisplayIndexes (DependencyProperty)"

        #region VisibleColumns (DependencyProperty)

        /// <summary>
        /// 
        /// Gets or sets a value indicating the names of columns 
        /// (as they appear in the column header) to be visible, seperated by a semicolon.
        /// 
        /// Columns whose names are not here will be hidden.
        /// </summary>

        public string VisibleColumns
        {
            get { return (string)GetValue(VisibleColumnsProperty); }
            set { SetValue(VisibleColumnsProperty, value); }
        }

        private void VisibleColumns_Initialize()
        {
            // Get saved VisibleColumns from app.config
            // Initialize VisibleColumns
            VisibleColumns = string.IsNullOrEmpty(VisibleColumns.Replace("\n", " ").Replace("\r", " ")) ? AllColumnsHeaders : VisibleColumns;
        }


        public static readonly DependencyProperty VisibleColumnsProperty =
            DependencyProperty.Register("VisibleColumns", typeof(string), typeof(WpfDataGridConfigurationBehavior), new PropertyMetadata("", OnVisibleColumnsChanged));

        private static void OnVisibleColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((WpfDataGridConfigurationBehavior)d).VisibleColumnsChanged(e);
        }



        /// <summary>
        /// 
        /// Updates the display
        /// 
        /// </summary>
        /// <param name="e"></param>

        public void VisibleColumnsChanged(DependencyPropertyChangedEventArgs e)
        {
            if (theContextMenu == null)
                return;

            if (e.NewValue != null)
            {
                var showTheseColumns = e.NewValue.ToString().Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                var saContextMenuVisibleItems = new List<string>();

                int iCol = 0;

                foreach (MenuItem menuItem in theContextMenu.Items)
                {
                    // Show/Hide the Context Menu item's checkmark.
                    if (menuItem.FontWeight == FontWeights.Bold) continue;

                    menuItem.IsChecked = showTheseColumns.Contains(menuItem.Header.ToString());
                    if (menuItem.IsChecked) saContextMenuVisibleItems.Add(menuItem.Header.ToString());

                    mnuShowAll.IsChecked = mnuShowAll.IsChecked && menuItem.IsChecked;

                    // Assign menu item's column's DisplayIndex in display order, (i.e. in menu item order), looking up each column by header name.)
                    if (mnuAlpha.IsChecked == false)
                        dataGrid.Columns.First(x => x.Header.ToString().Replace("\n", " ").Replace("\r", " ") == menuItem.Header.ToString()).DisplayIndex = iCol++;
                }

                // Show the columns
                foreach (var col in dataGrid.Columns)
                    col.Visibility =
                        showTheseColumns.Contains(col.Header.ToString().Replace("\n", " ").Replace("\r", " "))
                        && (saContextMenuVisibleItems.Contains(col.Header.ToString().Replace("\n", " ")
                            .Replace("\r", " ")))
                            ? Visibility.Visible
                            : Visibility.Collapsed;

                // Persist the new visible columns list

                if (dataGrid != null && !DontPersistVisibleColumns)
                    Settings_Save(DataGridName + "VisibleColumns", VisibleColumns);

            }
        }

        #endregion "VisibleColumns"

        public static void Settings_Save(string propertyName, string propertyValue)
        {
            foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
            {
                if (propertyName == property.Name)
                {
                    property.PropertyValue = propertyValue;
                    Settings.Default.Save();
                }
            }
        }

    }
    
     public static class WpfDataGridConfigurationBehaviorFinder
    {
        public static T FindChild<T>(DependencyObject depObj) where T : DependencyObject
        {
            // Confirm obj is valid. 
            if (depObj == null) return null;

            // success case
            if (depObj is T) return depObj as T;

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                T obj = FindChild<T>(VisualTreeHelper.GetChild(depObj, i));
                if (obj != null) return obj;
            }
            return null;
        }

        public interface IBreakVisualParenting
        {
            DependencyObject Parent { get; }
        }

        public static T LastVisualAncestorOfType<T>(this DependencyObject element) where T : DependencyObject
        {
            T item = null;

            var parent = VisualTreeHelper.GetParent(element);
            while (parent != null)
            {
                if (parent is T)
                    item = (T)parent;
                if (parent is IBreakVisualParenting)
                {
                    parent = ((IBreakVisualParenting)parent).Parent;
                }
                else
                    parent = VisualTreeHelper.GetParent(parent);
            }

            return item;
        }

    }
}

I liked the column chooser solution from CodePlex:
DataGrid Behavior

I clean up the code and remove unnecessary code:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
using Microsoft.Xaml.Behaviors;

namespace Behaviors
{
    public class WpfDataGridConfigurationBehavior : Behavior<DependencyObject>
    {

        #region "public Properties"


        public bool DontPersistVisibleColumns { get; set; }
        public bool DontPersistColumnsOrder { get; set; }

        public string ContextMenuChoices { get; set; } = "Alphabetical Menu,Show All Columns";

        public string DataGridName { get; set; }

        #endregion "public Properties"

        #region "private Properties"

        private DataGrid dataGrid;

        private ContextMenu theContextMenu; // Context Menu for the field chooser.

        private string AllColumnsHeaders { get; set; }
        private string AllColumnDisplayIndexes { get; set; }

        private int nBaseItems = 5;
        private MenuItem mnuAlpha;
        private MenuItem mnuShowAll;

        #endregion "Private Properties"

        protected override void OnAttached()
        {
            base.OnAttached();

            dataGrid = this.AssociatedObject as DataGrid;
            if (DataGridName == null) DataGridName = dataGrid.Name;

            ContextMenu_BuildStaticMenu();

            dataGrid.Loaded += (sender, e) => { DataGrid_Loaded(); };
            dataGrid.AutoGeneratedColumns += DataGrid_AutoGeneratedColumns;

            dataGrid.ColumnReordered += (sender, e) => { DataGrid_ColumnReordered(sender); };

            dataGrid.ContextMenuOpening += (sender, e) => { DataGrid_ContextMenuOpening(e); };

            dataGrid.LoadingRow += (sender, e) => { e.Row.Header = (e.Row.GetIndex() + 1).ToString(); };

            dataGrid.RowHeaderWidth = 0;
            var ns = new Style(typeof(DataGridRowHeader)) { BasedOn = dataGrid.RowHeaderStyle };
            ns.Setters.Add(new Setter(Control.FontWeightProperty, FontWeights.Bold));
            ns.Setters.Add(new Setter(Control.FontSizeProperty, 11.0));
            ns.Setters.Add(new Setter(Control.PaddingProperty, new Thickness(4, 0, 4, 0)));
            dataGrid.RowHeaderStyle = ns;
        }

        private void DataGrid_AutoGeneratedColumns(object sender, EventArgs e)
        {
            DataGrid_Loaded();
        }


        #region "DataGrid Events"


        private void DataGrid_ContextMenuOpening(ContextMenuEventArgs e)
        {
            var dep = (DependencyObject)e.OriginalSource;

            // iteratively traverse the visual tree
            while ((dep != null) && !(dep is DataGridCell) && !(dep is DataGridColumnHeader))
                dep = VisualTreeHelper.GetParent(dep);

            if (dep == null)
                return;

            if (dep is DataGridColumnHeader)
            {
                // do something
            }

            if (dep is DataGridCell)
            {
                // navigate further up the tree
                while ((dep != null) && !(dep is DataGridRow))
                    dep = VisualTreeHelper.GetParent(dep);

                var row = dep as DataGridRow;

                dataGrid.ItemContainerGenerator.IndexFromContainer(row);
            }
        }

        private void DataGrid_ColumnReordered(object sender)
        {
            Settings_SaveDisplayIndexes(sender);

            ContextMenu_BuildMenu();
        }

        private void DataGrid_Loaded()
        {
            ContextMenu_BuildMenu(false);

            VisibleColumns_Initialize();
        }

        #endregion "DataGrid Events"

        #region "ContextMenu Methods and Events"

        private void ContextMenu_BuildStaticMenu()
        {
            theContextMenu = new ContextMenu { FontSize = 11, StaysOpen = true };

            mnuAlpha = new MenuItem
            {
                Header = ContextMenuChoices.Split(',')[0],
                FontWeight = FontWeights.Bold,
                IsCheckable = true,
                StaysOpenOnClick = true
            };
            mnuAlpha.Click += (sender, e) => { ContextMenu_BuildMenu(); };

            mnuShowAll = new MenuItem
            {
                Header = ContextMenuChoices.Split(',')[1],
                FontWeight = FontWeights.Bold,
                IsCheckable = true,
                StaysOpenOnClick = true
            };
            mnuShowAll.Checked += (sender, e) => { VisibleColumns = AllColumnsHeaders; };
            mnuShowAll.Click += (sender, e) =>
            {
                if (mnuShowAll.IsChecked == false) VisibleColumns_Initialize();
            };


            theContextMenu.Items.Add(mnuShowAll);
            theContextMenu.Items.Add(mnuAlpha);
        }

        private void ContextMenu_BuildMenu(bool pbRebuild = true)
        {
            for (int i = theContextMenu.Items.Count - 1; i > 0; i--)
                if (((MenuItem)theContextMenu.Items[i]).FontWeight != FontWeights.Bold)
                    theContextMenu.Items.Remove(theContextMenu.Items[i]);

            nBaseItems = theContextMenu.Items.Count;

            // Attach the context menu to the DataGrid ColumnHeaders
            var headersPresenter = WpfDataGridConfigurationBehaviorFinder.FindChild<DataGridColumnHeadersPresenter>(dataGrid);
            ContextMenuService.SetContextMenu(headersPresenter, theContextMenu);

            if (VisibleColumns == null)
                throw (new SettingsPropertyNotFoundException("User's VisibleColumns setting not found."));

            // Get the current column ordering from user.config

            if (DisplayIndexes == null)
                throw (new SettingsPropertyNotFoundException("User's DisplayIndexes setting not found."));

            AllColumnDisplayIndexes = DisplayIndexes;
            string[] colIndexes = AllColumnDisplayIndexes.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);

            var dataGridColumns = dataGrid.Columns.OrderBy(x => x.DisplayIndex);

            // Sort the columns in display index order so menu header order matchs display column order
            if (pbRebuild == false)
                // Initially the datagrid column order is such that display indexes are the same as the col indexes
                if (colIndexes.Length > 0)
                    dataGridColumns = dataGrid.Columns.OrderBy(x => Convert.ToInt16(colIndexes[x.DisplayIndex]));

            if (mnuAlpha.IsChecked)
                dataGridColumns = dataGrid.Columns.OrderBy(x => x.Header.ToString());

            AllColumnsHeaders = "";
            foreach (var col in dataGridColumns)
            {
                // All column name to a list of all column headers for later use.
                AllColumnsHeaders = 
quot;{col.Header.ToString().Replace("\n", " ").Replace("\r", " ")};{AllColumnsHeaders}";

                // Add new menu item in display order.
                ContextMenu_AddNewMenuItem(col);
            }

            string sTemp = VisibleColumns;
            VisibleColumns = null;
            VisibleColumns = sTemp;

        }

        private void ContextMenu_AddNewMenuItem(DataGridColumn col)
        {
            var menuItem = new MenuItem { Header = col.Header.ToString().Replace("\n", " ").Replace("\r", " "), StaysOpenOnClick = true };
            var saVisibleColumns = new List<string> { string.Empty };
            if (VisibleColumns != null)
            {
                saVisibleColumns = VisibleColumns.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList();
            }

            menuItem.IsChecked = (saVisibleColumns.Contains(menuItem.Header));
            menuItem.Click += (sender, e) => { ContextMenu_ColumnName_Click(sender); };

            theContextMenu.Items.Add(menuItem);
        }

        private void ContextMenu_ColumnName_Click(object sender)
        {
            var mi = sender as MenuItem;

            // Get the column name that was clicked
            string colName = mi.Header.ToString();


            // Capture new visible columns list
            Settings_SaveVisibleColumns(mi, colName);
        }

        #endregion "ContextMenu Methods and Events"

        #region "Settings Methods"

        private void Settings_SaveVisibleColumns(MenuItem mi, string colName)
        {
            if (theContextMenu.Items.Count - nBaseItems < dataGrid.Columns.Count)
                return;

            // Put the visible column names into an array
            var saVisibleColumns = VisibleColumns.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries).ToList();
            if (saVisibleColumns != null && saVisibleColumns.Count > 0 &&
                saVisibleColumns[saVisibleColumns.Count - 1].StartsWith("-"))
                saVisibleColumns.RemoveAt(saVisibleColumns.Count - 1);

            // If the menu item is unchecked (column is not visible)
            if (!mi.IsChecked)
                // Make the column visible by adding its name to the Visible Columns list
                saVisibleColumns.Add(colName);

            else
            // Hide the column by removing its name from the VisibleColumns list
            if (saVisibleColumns.Contains(colName) && saVisibleColumns.Count > 1)
                saVisibleColumns.Remove(colName);

            VisibleColumns = string.Join(";", saVisibleColumns) + ";";
        }

        private void Settings_SaveDisplayIndexes(object sender)
        {
            // Capture the new column order
            AllColumnDisplayIndexes = "";
            foreach (DataGridColumn col in ((DataGrid)sender).Columns)
            {
                AllColumnDisplayIndexes +=
                    (AllColumnDisplayIndexes.Length > 0 ? ";" : "") + col.DisplayIndex;
            }

            DisplayIndexes = AllColumnDisplayIndexes;
        }

        #endregion "Settings Methods"

        #region DisplayIndexes (DependencyProperty)

        public string DisplayIndexes
        {
            get { return (string)GetValue(DisplayIndexesProperty); }
            set { SetValue(DisplayIndexesProperty, value); }
        }

        public static readonly DependencyProperty DisplayIndexesProperty =
            DependencyProperty.Register("DisplayIndexes", typeof(string), typeof(WpfDataGridConfigurationBehavior), new PropertyMetadata("", OnDisplayIndexesChanged));

        private static void OnDisplayIndexesChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((WpfDataGridConfigurationBehavior)d).DisplayIndexesChanged(e);
        }

        public void DisplayIndexesChanged(DependencyPropertyChangedEventArgs e)
        {
            // Persist the new column order
            if (dataGrid != null && !DontPersistColumnsOrder)
                Settings_Save(DataGridName + "DisplayIndexes", AllColumnDisplayIndexes);
        }

        #endregion "DisplayIndexes (DependencyProperty)"

        #region VisibleColumns (DependencyProperty)

        /// <summary>
        /// 
        /// Gets or sets a value indicating the names of columns 
        /// (as they appear in the column header) to be visible, seperated by a semicolon.
        /// 
        /// Columns whose names are not here will be hidden.
        /// </summary>

        public string VisibleColumns
        {
            get { return (string)GetValue(VisibleColumnsProperty); }
            set { SetValue(VisibleColumnsProperty, value); }
        }

        private void VisibleColumns_Initialize()
        {
            // Get saved VisibleColumns from app.config
            // Initialize VisibleColumns
            VisibleColumns = string.IsNullOrEmpty(VisibleColumns.Replace("\n", " ").Replace("\r", " ")) ? AllColumnsHeaders : VisibleColumns;
        }


        public static readonly DependencyProperty VisibleColumnsProperty =
            DependencyProperty.Register("VisibleColumns", typeof(string), typeof(WpfDataGridConfigurationBehavior), new PropertyMetadata("", OnVisibleColumnsChanged));

        private static void OnVisibleColumnsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((WpfDataGridConfigurationBehavior)d).VisibleColumnsChanged(e);
        }



        /// <summary>
        /// 
        /// Updates the display
        /// 
        /// </summary>
        /// <param name="e"></param>

        public void VisibleColumnsChanged(DependencyPropertyChangedEventArgs e)
        {
            if (theContextMenu == null)
                return;

            if (e.NewValue != null)
            {
                var showTheseColumns = e.NewValue.ToString().Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                var saContextMenuVisibleItems = new List<string>();

                int iCol = 0;

                foreach (MenuItem menuItem in theContextMenu.Items)
                {
                    // Show/Hide the Context Menu item's checkmark.
                    if (menuItem.FontWeight == FontWeights.Bold) continue;

                    menuItem.IsChecked = showTheseColumns.Contains(menuItem.Header.ToString());
                    if (menuItem.IsChecked) saContextMenuVisibleItems.Add(menuItem.Header.ToString());

                    mnuShowAll.IsChecked = mnuShowAll.IsChecked && menuItem.IsChecked;

                    // Assign menu item's column's DisplayIndex in display order, (i.e. in menu item order), looking up each column by header name.)
                    if (mnuAlpha.IsChecked == false)
                        dataGrid.Columns.First(x => x.Header.ToString().Replace("\n", " ").Replace("\r", " ") == menuItem.Header.ToString()).DisplayIndex = iCol++;
                }

                // Show the columns
                foreach (var col in dataGrid.Columns)
                    col.Visibility =
                        showTheseColumns.Contains(col.Header.ToString().Replace("\n", " ").Replace("\r", " "))
                        && (saContextMenuVisibleItems.Contains(col.Header.ToString().Replace("\n", " ")
                            .Replace("\r", " ")))
                            ? Visibility.Visible
                            : Visibility.Collapsed;

                // Persist the new visible columns list

                if (dataGrid != null && !DontPersistVisibleColumns)
                    Settings_Save(DataGridName + "VisibleColumns", VisibleColumns);

            }
        }

        #endregion "VisibleColumns"

        public static void Settings_Save(string propertyName, string propertyValue)
        {
            foreach (SettingsPropertyValue property in Settings.Default.PropertyValues)
            {
                if (propertyName == property.Name)
                {
                    property.PropertyValue = propertyValue;
                    Settings.Default.Save();
                }
            }
        }

    }
    
     public static class WpfDataGridConfigurationBehaviorFinder
    {
        public static T FindChild<T>(DependencyObject depObj) where T : DependencyObject
        {
            // Confirm obj is valid. 
            if (depObj == null) return null;

            // success case
            if (depObj is T) return depObj as T;

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
            {
                T obj = FindChild<T>(VisualTreeHelper.GetChild(depObj, i));
                if (obj != null) return obj;
            }
            return null;
        }

        public interface IBreakVisualParenting
        {
            DependencyObject Parent { get; }
        }

        public static T LastVisualAncestorOfType<T>(this DependencyObject element) where T : DependencyObject
        {
            T item = null;

            var parent = VisualTreeHelper.GetParent(element);
            while (parent != null)
            {
                if (parent is T)
                    item = (T)parent;
                if (parent is IBreakVisualParenting)
                {
                    parent = ((IBreakVisualParenting)parent).Parent;
                }
                else
                    parent = VisualTreeHelper.GetParent(parent);
            }

            return item;
        }

    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文