绑定数据网格列可见性MVVM

51

.Net 3.5

我知道列不会继承数据上下文,通过阅读其他帖子,我认为这个方法可以实现:

Visibility="{Binding RelativeSource={x:Static RelativeSource.Self},
                     Path=(FrameworkElement.DataContext).IsColumnNameVisible,
                     Converter={StaticResource boolToVisConverter}}"

当然,它并没有报错。输出窗口没有提示问题,似乎我找到了资源,但是viewmodel属性被新命名了。

这是完整的DG:

<tk:DataGrid                                        
            VirtualizingStackPanel.IsVirtualizing="False"                                        
            Grid.Column="0"
            AlternationCount="2"
            AreRowDetailsFrozen="True"
            AutoGenerateColumns="False"
            Background="Transparent"
            BorderThickness="0"
            CanUserAddRows="False"
            CanUserReorderColumns="True"
            CanUserResizeRows="False"
            GridLinesVisibility="None"
            ItemsSource="{Binding Employees}"
            SelectionMode="Single"
            ColumnHeaderStyle="{StaticResource columnHeaderStyle}"
            RowHeaderStyle="{StaticResource rowHeaderStyle}"
            CellStyle="{StaticResource cellStyle}"
            RowStyle="{StaticResource rowStyle}" 
            ContextMenu="{StaticResource columnHeaderContextMenu}">
    <tk:DataGrid.Resources>
        <ContextMenu x:Key="columnHeaderContextMenu" ItemsSource="{Binding ColumnHeaderContextMenuItems}" />
        <Style TargetType="{x:Type ScrollBar}">
            <Setter Property="Background" Value="Transparent"/>
        </Style>                                    
        <Style TargetType="{x:Type tk:DataGridColumnHeader}">
            <Setter Property="Background" Value="Transparent"/>
        </Style>
    </tk:DataGrid.Resources>
    <tk:DataGrid.Triggers>
        <EventTrigger RoutedEvent="tk:DataGridRow.MouseDoubleClick">
            <EventTrigger.Actions>
                <BeginStoryboard Storyboard="{StaticResource showDetailGrid}"/>
            </EventTrigger.Actions>
        </EventTrigger>
    </tk:DataGrid.Triggers>
    <tk:DataGrid.Columns>
        <tk:DataGridTextColumn IsReadOnly="True" Header="test" Binding="{Binding Name, Mode=OneWay}" Visibility="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(FrameworkElement.DataContext).IsColumnNameVisible, Converter={StaticResource boolToVisConverter}}"  />
    </tk:DataGrid.Columns>
</tk:DataGrid>

我已经阅读了几乎所有解决此问题的方法,但都不起作用。


如果我理解正确的话,您是想根据绑定的ViewModel属性使数据列可见或不可见吗? - ChrisBD
你在<tk:DataGrid.Resources>中更改了你的ContextMenu - 难怪你的窗口DataContext无法访问。 - Felix C
@ChrisBD:是的,那就是想法。VM属性是通过数据上下文设置的。 - jrb
@ Felix:你是什么意思?这只是一个上下文菜单,为什么会影响数据上下文的可用性? - jrb
1个回答

109

DataGridColumn不属于视觉树,因此它们与DataGrid的数据上下文没有关联。

如果要让它们连接在一起,请使用代理元素方法,如下所示...

  1. 在祖先面板的Resources中添加一个代理FrameworkElement

  2. 将其托管到一个不可见的ContentControl中,并绑定到其Content上。

  3. 在可见性绑定中使用此ProxyElement作为数据上下文源的StaticResource

 <StackPanel>
     <StackPanel.Resources>
        <local:BooleanToVisibilityConverter
               x:Key="BooleanToVisibilityConverter" />

        <FrameworkElement x:Key="ProxyElement"
                          DataContext="{Binding}"/>
     </StackPanel.Resources>
     <ContentControl Visibility="Collapsed"
                 Content="{StaticResource ProxyElement}"/>
     <DataGrid AutoGenerateColumns="False">
         <DataGrid.Columns>
             <DataGridTextColumn
                    Visibility="{Binding DataContext.IsTextColumnVisibile,
                                         Source={StaticResource ProxyElement},
                                         Converter={StaticResource
                                             BooleanToVisibilityConverter}}"
                    Binding="{Binding Text}"/>
         </DataGrid.Columns>
     </DataGrid>
 </StackPanel> 

除了 DataGridColumn,上述方法也可以很好地将 DataContext 连接到 PopupContextMenu(即任何与可视树没有连接的元素)。

Silverlight 用户

不幸的是,在 Silverlight 中,不允许使用任何框架元素设置内容控件。因此,解决方法是(这只是针对 Silverlight 的指导代码)...

  1. 将框架元素资源更改为轻量级的东西,比如 Textblock。(Silverlight 不允许指定 FrameworkElement 类型的静态资源。)

 <StackPanel.Resources>
     <TextBlock x:Key="MyTextBlock" />
编写一个附加属性,以将文本块保留在内容控件中。
 <ContentControl Visibility="Collapsed" 
                 local:MyAttachedBehavior.ProxyElement="{StaticResource MyTextBlock}" />
在附加的依赖属性更改事件处理程序中,将内容控件的数据上下文与文本块绑定。
  private static void OnProxyElementPropertyChanged(
      DependencyObject depObj, DependencyPropertyChangedEventArgs e)
  {
        if (depObj is ContentControl && e.NewValue is TextBlock)
        {
            var binding = new Binding("DataContext");
            binding.Source = depObj;
            binding.Mode = OneWay;
            BindingOperations.SetBinding(
                (TextBlock)e.NewValue, TextBlock.DataContextProperty, binding);
        }
  }

这种方式可能使文本块与可视树不相连,但可能会意识到数据上下文的变化。


由于某些原因,这仅适用于“可见性”列,尽管我不明白为什么。例如,绑定“宽度”根本没有任何效果。绑定“DisplayIndex”会抛出异常,告诉我-1超出范围 - 即使在绑定中是正确的。 - ShadowChaser
3
请问为什么即使我们使用 ProxyElement,这里仍然需要使用 ContentControl?这个解决方案与此类似,但是不需要使用 ContentControl - monstr
3
@monstr,“Freezable”类会篡改“DataContext”……在我们的情况下,“proxyelement”不是可冻结的,因此它唯一能获取“DataContext”的方式是当它被托管在“VisualTree”中时,这是可能的,如果它是“ContentControl”的“Content”。另外,在你提供的第二个解决方案中,“NameScoping”在“Template”中并未解决,比如“DataGridColumnHeaderTemplate”,所以“ElementName”无法工作。我提供的解决方案可以解决这两种情况。 - WPF-it
你好,WPF-it。你的方法完美地运行了,我能够以MVVM方式隐藏/显示列。然而,当我隐藏列时,在末尾会显示一个额外的空白列。我已将AutoGenerateColumns设置为False,HeaderVisibility设置为Column。所有列宽都设置为自动(*无效)。你有关于这个额外列的解决方案或建议吗?谢谢。 - RDV
我犯了一个错误,认为DataGridColumn的Visibility的DataBindings是绑定在DataGrid中作为ItemsSource的项目的子集。但是,<DataGrid ItemsSource="{Binding...与DataGridColumn中Visibility的绑定无关。DataGridColumn中Visibility的绑定是绑定到DataContext(例如通过ProxyBinding指示)的。 - peter70
显示剩余9条评论

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