WPF:如何使用XAML隐藏GridViewColumn?

32
我在App.xaml中有以下对象。
<Application.Resources>
        <ResourceDictionary>
            <GridView x:Key="myGridView" x:Shared="false">
                             <GridViewColumn Header="Created" DisplayMemberBinding="{Binding Path=Created}"/>

... more code ...

我在多个地方使用这个网格视图。例如:

<ListView x:Name="detailList"   View="{StaticResource myGridView}" ...>
在其中一种用法中(例如上面的detailList),我想隐藏Created列,可能使用XAML实现?
有任何想法吗?
11个回答

21

实际上,我发现最简单的解决方案是使用附加属性:

public class GridViewColumnVisibilityManager
{       
    static void UpdateListView(ListView lv)
    {
        GridView gridview = lv.View as GridView;
        if (gridview == null || gridview.Columns == null) return;
        List<GridViewColumn> toRemove = new List<GridViewColumn>();
        foreach (GridViewColumn gc in gridview.Columns)
        {
            if (GetIsVisible(gc) == false)
            {
                toRemove.Add(gc);
            }
        }
        foreach (GridViewColumn gc in toRemove)
        {
            gridview.Columns.Remove(gc);
        }
    }

    public static bool GetIsVisible(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsVisibleProperty);
    }

    public static void SetIsVisible(DependencyObject obj, bool value)
    {
        obj.SetValue(IsVisibleProperty, value);
    }

    public static readonly DependencyProperty IsVisibleProperty =
        DependencyProperty.RegisterAttached("IsVisible", typeof(bool), typeof(GridViewColumnVisibilityManager), new UIPropertyMetadata(true));


    public static bool GetEnabled(DependencyObject obj)
    {
        return (bool)obj.GetValue(EnabledProperty);
    }

    public static void SetEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(EnabledProperty, value);
    }

    public static readonly DependencyProperty EnabledProperty =
        DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(GridViewColumnVisibilityManager), new UIPropertyMetadata(false,
            new PropertyChangedCallback(OnEnabledChanged)));

        private static void OnEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
    {
        ListView view = obj as ListView;
        if (view != null)
        {
            bool enabled = (bool)e.NewValue;
            if (enabled)
            {
                view.Loaded += (sender, e2) =>
                {
                    UpdateListView((ListView)sender);
                };
                view.TargetUpdated += (sender, e2) =>
                {
                    UpdateListView((ListView)sender);
                };
                view.DataContextChanged += (sender, e2) =>
                {
                    UpdateListView((ListView)sender);
                };
            }
        }
    }
}

然后,它可以像这样使用:

<ListView foo:GridViewColumnVisibilityManager.Enabled="True">
...
<GridViewColumn Header="Status" foo:GridViewColumnVisibilityManager.IsVisible="{Binding ShowStatusColumn}">
                        <GridViewColumn.CellTemplate>
                            <DataTemplate> ...

2
我的实现期望在加载时列的可见性是静态的 - 如果您期望它会改变,那么您可能需要修复UpdateListView()以执行其他操作来隐藏和显示该列(Width=0)。 - Ben McMillan
这是我目前发现的最佳解决方案,而且我已经成功地将其与 Visible 属性动态更改的功能结合使用。请查看我的答案。如果您想默认隐藏并稍后显示该列,则它特别有用。 - surfen
这个解决方案对我不起作用。当我进行调试时,我发现先调用了UpdateListView方法,然后才获取IsVisible属性值。我猜这是我的情况,但我不知道为什么?有人能解释一下为什么会发生这种情况吗? - Pegieo

13

在Ben McMillan的答案基础上,但支持动态更改可见属性。我通过删除IsEnabled属性进一步简化了他的解决方案。

public class GridViewColumnVisibilityManager
{
    static Dictionary<GridViewColumn, double> originalColumnWidths = new Dictionary<GridViewColumn, double>();

    public static bool GetIsVisible(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsVisibleProperty);
    }

    public static void SetIsVisible(DependencyObject obj, bool value)
    {
        obj.SetValue(IsVisibleProperty, value);
    }

    public static readonly DependencyProperty IsVisibleProperty =
        DependencyProperty.RegisterAttached("IsVisible", typeof(bool), typeof(GridViewColumnVisibilityManager), new UIPropertyMetadata(true, OnIsVisibleChanged));

    private static void OnIsVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        GridViewColumn gc = d as GridViewColumn;
        if (gc == null)
            return;

        if (GetIsVisible(gc) == false)
        {
            originalColumnWidths[gc] = gc.Width;
            gc.Width = 0;
        }
        else
        {
            if (gc.Width == 0)
                gc.Width = originalColumnWidths[gc];
        }
    }
}

这样不会泄漏内存吗?您正在将控件本身存储在静态字典中... - Rashack
1
我认为你是正确的。避免这种情况的一种方法是使用ConditionalWeakTable而不是Dictionary:http://msdn.microsoft.com/en-us/library/dd287757%28v=vs.100%29.aspx。另一个选择是创建第二个附加属性来保留原始宽度。 - surfen
1
将列宽设置为0还有另一个副作用:该列的调整手柄会隐藏左侧列的调整手柄。因此,要调整左侧的列宽,用户必须先调整此列以到达左侧列的手柄。 - pogosama
1
无法工作...用户可以调整列的大小以查看内容。 - SINGULARITY
我曾经遇到过resize的同样问题,但是我找到了解决方法。请参见相关话题 - A.Pissicat

6

您最好的选择可能是通过继承 GridView 类创建一个 自定义控件,添加所需的列,并公开一个有意义的属性以显示/隐藏特定列。您的自定义 GridView 类可能如下所示:

using System;
using System.Windows.Controls;

namespace MyProject.CustomControls
{
    public class CustomGridView : GridView
    {
        private GridViewColumn _fixedColumn;
        private GridViewColumn _optionalColumn;

        public CustomGridView()
        {
            this._fixedColumn = new GridViewColumn() { Header = "Fixed Column" };
            this._optionalColumn = new GridViewColumn() { Header = "Optional Column" };

            this.Columns.Add(_fixedColumn);
            this.Columns.Add(_optionalColumn);
        }

        public bool ShowOptionalColumn
        {
            get { return _optionalColumn.Width > 0; }
            set
            {
                // When 'False' hides the entire column
                // otherwise its width will be set to 'Auto'
                _optionalColumn.Width = (!value) ? 0 : Double.NaN;
            }
        }

    }
}

然后,您可以像以下示例一样从XAML中设置该属性:
<Window x:Class="WpfApplication1.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:cc="clr-namespace:MyProject.CustomControls"
        Title="Window1"
        Height="300"
        Width="300">
    <StackPanel>
        <ListView>
            <ListView.View>
                <cc:CustomGridView ShowOptionalColumn="False" />
            </ListView.View>
        </ListView>

        <ListView>
            <ListView.View>
                <cc:CustomGridView ShowOptionalColumn="True" />
            </ListView.View>
        </ListView>
    </StackPanel>
</Window>

你可以选择将 'CustomGridView.ShowOptionalColumn' 作为 DependencyProperty,以便将其用作绑定目标。


4

Taken from here

<ListView Grid.Column="1" Grid.Row="1"  Name="FicheList" >
            <ListView.Resources>
                <ResourceDictionary>
                    <Style x:Key="hiddenStyle" TargetType="GridViewColumnHeader">
                        <Setter Property="Visibility" Value="Collapsed"/>
                    </Style>
                </ResourceDictionary>
            </ListView.Resources>
            <ListView.View>
                <GridView>
                    <GridViewColumn DisplayMemberBinding="{Binding Code}" Header="Code" Width="0" HeaderContainerStyle="{StaticResource hiddenStyle}" />
                    <GridViewColumn DisplayMemberBinding="{Binding FicheTitle}" Header="Title" Width="100" />
                    <GridViewColumn DisplayMemberBinding="{Binding CategoryName}" Header="Category" Width="100" />
                    <GridViewColumn DisplayMemberBinding="{Binding UpdateDate}" Header="Update Date" Width="100" />

                </GridView>
            </ListView.View>
        </ListView>

1
好的,只隐藏表头,使单元格可见。 - bohdan_trotsenko
2
虽然这不是 OP 所要求的,但了解一下也很好。(个人认为最好将列调整得非常窄,以便仍然可以调整列大小) - Qwertie

2

我有一个比使用Attached Behavior更简单的解决方案。

你只需要将GridViewColumn的宽度属性绑定到ViewModel中的布尔值上。然后创建一个简单的转换器,比如BooleanToWidthConverter,它接受一个布尔值并返回一个double类型的值,如果为false则返回0,如果为true则返回指定的宽度。

希望这能够帮助你,并让你的生活更轻松。

XAML:

<GridViewColumn x:Name="MyHiddenGridViewColumn"
                Width={Binding Path=IsColumnVisibleProperty, Converter={StaticResource BooleanToWidthConverter}}">
   <!-- GridViewColumn.HeaderTemplate etc. goes here. -->
</GridViewColumn>

转换器:

public class BooleanToWidthConverter : IValueConverter
    {
        private const double Column_Width = 40.0;

        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            if (value != null && value != DependencyProperty.UnsetValue)
            {
                bool isVisible = (bool) value;

                return isVisible ? Column_Width : 0;
            }
            return Column_Width;
        }

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

1

这是我的代码,在我的项目中运行得非常好。如果您不喜欢添加一些外部代码。

    /// <summary>
    /// show/hide datagrid column
    /// </summary>
    /// <param name="datagrid"></param>
    /// <param name="header"></param>
    private void ToggleDataGridColumnsVisible()
    {
        if (IsNeedToShowHideColumn())
        {
            foreach (GridViewColumn column in ((GridView)(this.ListView1.View)).Columns)
            {
                GridViewColumnHeader header = column.Header as GridViewColumnHeader;
                if (header != null)
                {
                    string headerstring = header.Tag.ToString();

                    if (!IsAllWaysShowingHeader(headerstring ) )
                    {
                        if (IsShowingHeader())
                        {

                        }
                        else
                        {
                            //hide it
                            header.Template = null;
                            column.CellTemplate = null;
                            column.Width = 0;
                        }
                    }
                }

            }

        }
    }

0
有点晚了,但我认为这可能仍然有用:
根据A.Pissicat、Surfen和Ben McMillan的代码(和注释),我已经更新了GridViewColumnVisibilityManager,如下所示:
    /// <summary>
    /// Used to hide the attached <see cref="GridViewColumn"/> and prevent the user
    /// from being able to access the column's resize "gripper" by
    /// temporarily setting both the column's width and style to
    /// a value of 0 and null (respectively) when IsVisible
    /// is set to false. The prior values will be restored
    /// once IsVisible is set to true.
    /// </summary>
    public static class GridViewColumnVisibilityManager
    {
        public static readonly DependencyProperty IsVisibleProperty =
            DependencyProperty.RegisterAttached(
                "IsVisible",
                typeof(bool),
                typeof(GridViewColumnVisibilityManager),
                new UIPropertyMetadata(true, OnIsVisibleChanged));

        private static readonly ConditionalWeakTable<GridViewColumn, ColumnValues> OriginalColumnValues = new ConditionalWeakTable<GridViewColumn, ColumnValues>();

        public static bool GetIsVisible(DependencyObject obj) => (bool)obj.GetValue(IsVisibleProperty);

        public static void SetIsVisible(DependencyObject obj, bool value) => obj.SetValue(IsVisibleProperty, value);

        private static void OnIsVisibleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (!(d is GridViewColumn gridViewColumn))
            {
                return;
            }

            if (!GetIsVisible(gridViewColumn))
            {
                // Use GetOrCreateValue to ensure
                // we have a place to cache our values.
                // Any previous values will be overwritten.
                var columnValues = OriginalColumnValues.GetOrCreateValue(gridViewColumn);
                columnValues.Width = gridViewColumn.Width;
                columnValues.Style = gridViewColumn.HeaderContainerStyle;

                // Hide the column by
                // setting the Width to 0.
                gridViewColumn.Width = 0;

                // By setting HeaderContainerStyle to null,
                // this should remove the resize "gripper" so
                // the user won't be able to resize the column
                // and make it visible again.
                gridViewColumn.HeaderContainerStyle = null;
            }
            else if (gridViewColumn.Width == 0
                     && OriginalColumnValues.TryGetValue(gridViewColumn, out var columnValues))
            {
                // Revert back to the previously cached values.
                gridViewColumn.HeaderContainerStyle = columnValues.Style;
                gridViewColumn.Width = columnValues.Width;
            }
        }

        private class ColumnValues
        {
            public Style Style { get; set; }

            public double Width { get; set; }
        }
    }


如果您希望在隐藏列时使用特定的HeaderContainerStyle Style而不是null,请将以下代码替换为:

gridViewColumn.HeaderContainerStyle = columnValues.Style;

改为:

gridViewColumn.HeaderContainerStyle = Application.Current.TryFindResource(@"disabledColumn") as Style;

并将@"disabledColumn"更改为您想要使用的任何名称。


0
<GridViewColumn Width="{Binding Tag, RelativeSource={RelativeSource AncestorType=ListView}, Converter={converters:BooleanToWidthConverter}, ConverterParameter=100}">
                            <GridViewColumn.HeaderContainerStyle>
                                <Style TargetType="{x:Type GridViewColumnHeader}" BasedOn="{StaticResource ColumnHeaderStyle}">
                                    <Setter Property="IsEnabled" Value="False"/>
                                    <Style.Triggers>
                                        <DataTrigger Binding="{Binding Tag, RelativeSource={RelativeSource AncestorType=ListView}}" Value="true">
                                            <Setter Property="IsEnabled" Value="True"/>
                                        </DataTrigger>
                                    </Style.Triggers>
                                </Style>
                            </GridViewColumn.HeaderContainerStyle>
                            <GridViewColumn.Header>
                                <StackPanel Tag="columnHeader" Orientation="Horizontal">
                                </StackPanel>
                            </GridViewColumn.Header>
                            <GridViewColumn.CellTemplate>
                                <DataTemplate>
                                    <ContentControl>
                                        <TextBlock Text="test" />
                                    </ContentControl>
                                </DataTemplate>
                            </GridViewColumn.CellTemplate>
                        </GridViewColumn>

布尔转宽度转换器类:IValueConverter {

    public object Convert (object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is bool b)
        {
            return b ? parameter : 0;
        }
        return 0;
    }

    public object ConvertBack (object value, Type targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}

很遗憾,在GridViewColumn中没有“IsVisible”属性。

但是,我有一个简单且完整的解决方案可以解决这个问题:

  1. 将宽度设置为0。许多开发人员在此步骤停止,因为它似乎已被隐藏,但实际上不是。我们仍然可以通过调整列大小使其可见,因此需要执行更多步骤2。
  2. 通过将GridViewColumnHeader IsEnabled = false禁用调整大小的GridViewColumn。

以上为示例代码:


0
我建议在父级上使用自定义属性(或劫持现有属性),然后在gridviewcolumnheader上使用自定义样式引用该祖先属性。像这样:
<Window.Resources>
    <Style TargetType="{x:Type GridViewColumnHeader}">
        <Setter Property="Visibility" Value="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=Tag}"/>
    </Style>
    <GridView x:Key="myGridView" x:Shared="false">                             
        <GridViewColumn Header="Created" DisplayMemberBinding="{Binding Path=Created}"/>    
    </GridView>
</Window.Resources>
<Grid x:Name="LayoutRoot">
    <StackPanel>
        <ListView x:Name="detailList"   View="{StaticResource myGridView}"/>
        <ListView x:Name="detailListHide" Tag="{x:Static Member=Visibility.Hidden}" View="{StaticResource myGridView}"/>
    </StackPanel>
</Grid>

我刚意识到这只隐藏了列标题,这可能不是您想要做的。除了制作第二个 GridView 外,我找不到其他仅使用 xaml 隐藏整个列的方法。 - Ben Reierson
3
将 GridViewColumn 的宽度设置为 0 将会有效地隐藏它。 - Enrico Campidoglio
1
我尝试过了,但它并没有真正消失。你仍然可以用鼠标展开它。 - Ben Reierson

0

这对我有用
需要在标题和内容上绑定可见性
在这种情况下,它在结尾处,所以我不担心宽度
但是用户没有UI钩子来重置宽度,因此如果将宽度设置为零,则会消失

<GridViewColumn Width="60">
    <GridViewColumnHeader HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch"
                            Visibility="{Binding Source={x:Static Application.Current}, Path=MyGabeLib.CurUser.IsInRoleSysAdmin, Converter={StaticResource bvc}}">
        <TextBlock>WS<LineBreak/>Count</TextBlock>
    </GridViewColumnHeader>
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Path=WordScoreCount, StringFormat={}{0:N0}}" HorizontalAlignment="Right"
                        Visibility="{Binding Source={x:Static Application.Current}, Path=MyGabeLib.CurUser.IsInRoleSysAdmin, Converter={StaticResource bvc}}"/>
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接