MVVM DataGridTemplateColumn MVVM下的可见性绑定

问题描述:

我有一个DataGrid绑定到我的ViewModel中的ICollectionView。 DataGrid位于UserControl中,用于几种不同的数据场景,其中一些需要某些DataGrid列,而另一些则不需要。MVVM DataGridTemplateColumn MVVM下的可见性绑定

我只想将DataGridTemplateColumn的Visibility属性绑定到内部标签的Content属性,因此如果没有任何行包含值,它将被隐藏。我有一个String to Visibility转换器集,但无法弄清楚如何找到内部标签的Content属性。

<DataGridTemplateColumn Header="Groups" Width="*" CanUserSort="True" SortMemberPath="Groups" Visibility="{Binding ElementName=lbl, Path=Content, Converter={StaticResource StringToVisibilityConverter}}"> 
    <DataGridTemplateColumn.CellTemplate> 
     <DataTemplate> 
      <Label Name="lbl" Content="{Binding Path=Groups}" /> 
     </DataTemplate> 
    </DataGridTemplateColumn.CellTemplate> 
</DataGridTemplateColumn> 

有什么建议吗?

+0

任何正在寻找答案的人,请参阅http:// stackoverflow。com/questions/7955318/bind-datagridtemplatecolumn-visibility – 2015-02-11 15:09:36

通过ViewModel上的Groups属性可以更好地实现;因为这最终是Label正在使用的。

<DataGridTemplateColumn Header="Groups" Width="*" CanUserSort="True" SortMemberPath="Groups" Visibility="{Binding Groups, Converter={StaticResource SomeConverter}}"> 
    <DataGridTemplateColumn.CellTemplate> 
     <DataTemplate> 
      <Label Name="lbl" Content="{Binding Path=Groups}" /> 
     </DataTemplate> 
    </DataGridTemplateColumn.CellTemplate> 
</DataGridTemplateColumn> 
+0

是的,同样的。 – Will 2011-03-17 15:53:04

+0

嗨,伙计们,我已经尝试过,并在运行时得到这个错误:System.Windows.Data错误:2:无法找到目标元素的治理FrameworkElement或FrameworkContentElement。 BindingExpression:路径=组;的DataItem = NULL;目标元素是'DataGridTemplateColumn'(HashCode = 38907373);目标属性是'可见性'(类型'可见性') – Aaron 2011-03-17 16:04:26

+0

令人沮丧的是,当我使用在XAML中定义的CollectionViewSource时,通过将可见性绑定源设置为ColletionViewSource,这实际上起作用。我不得不将CollectionViewSource移动到ViewModel来处理排序/过滤,并且破坏了这个Visibility绑定。任何其他想法? – Aaron 2011-03-17 16:12:59

你不能这样做。绑定/名称解析不能以这种方式工作。为什么不,StringToVisibilityConverter创建一个CollectionToVisibilityConverter检查数据源(可能传入要检查的列/属性),查看该列/属性是否完全为空,然后将其转换为可见性?

我在Stack Overflow的某处(无法找到确切的帖子)DataGridColumn没有分配数据上下文,因为它们不是FrameworkElement。为了解决这个问题,我不得不使用代码类同此:

<DataGridTemplateColumn 
     Header="Groups" 
     Width="*" 
     CanUserSort="True" 
     SortMemberPath="Groups" 
     Visibility"{Binding RelativeSource={x:Static RelativeSource.Self}, 
         Path=(FrameworkElement.DataContext).IsGroupsVisible, 
         Converter={StaticResource booleanToVisiblityConverter}}"> 
     <DataGridTemplateColumn.CellTemplate>   
       <DataTemplate>    
        <Label Name="lbl" Content="{Binding Path=Groups}" />   
       </DataTemplate>  
     </DataGridTemplateColumn.CellTemplate> 
    </DataGridTemplateColumn> 

Where 

<UserControl.Resources> 
    <BooleanToVisibilityConverter x:Key="booleanToVisibilityConverter" /> 
</UserControl.Resources> 
+2

嗨安迪,有趣。现在没有得到任何绑定错误,但它仍然无法正常工作。转换器甚至没有被调用... – Aaron 2011-03-17 17:24:48

+0

是在ViewGodel上DataGrid绑定的属性?或者它是DataGrid中的行绑定到的单个项目的属性?我认为它是两个,因为你想要显示单个单元格中的文本,并且网格中的列的可见性只能绑定到一个属性。 – 2011-03-17 17:36:20

+0

它是一个ObservableCollection中的单个项目的属性,它被设置为CollectionViewSource的源,并作为ICollectionView返回到DataGrid。我也尝试将它绑定到ViewModel上的一个名为IsGroupsVisible的属性上,该属性不那么优雅,但它也没有选择它。 – Aaron 2011-03-17 17:39:10

要使用RelativeSource.Self作为RelativeSourceDataGridTemplateColumn结合 - 你需要的DataGridContextHelper添加到您的应用程序。对于WPF 4 DataGrid,这仍然是必需的。

<DataGridTemplateColumn 
     Header="Groups" 
     Width="*" 
     CanUserSort="True" 
     SortMemberPath="Groups" 
     Visibility"{Binding RelativeSource={x:Static RelativeSource.Self}, 
         Path=(FrameworkElement.DataContext).IsGroupsVisible, 
         Converter={StaticResource booleanToVisiblityConverter}}"> 
     <DataGridTemplateColumn.CellTemplate>   
       <DataTemplate>    
        <Label Name="lbl" Content="{Binding Path=Groups}" />   
       </DataTemplate>  
     </DataGridTemplateColumn.CellTemplate> 
    </DataGridTemplateColumn> 

一百感谢SliverNinja和本文DataGridContextHelper。我的应用程序使用DataGrid和AutoGenerateColumns = False并使用DataGridTemplateColumn,因此在将列添加到网格之前设置了DataContext。

下面是附加属性类:

public sealed class DataGridColumnDataContextForwardBehavior 
{ 
    private DataGrid dataGrid = null; 

    public DataGridColumnDataContextForwardBehavior(DataGrid dataGrid) 
    { 
     this.dataGrid = dataGrid; 

     dataGrid.Columns.CollectionChanged += DataGridColumns_CollectionChanged; 
    } 

    private void DataGridColumns_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) 
    { 
     var IsDataContextForwardingEnabled = GetIsDataContextForwardingEnabled(dataGrid); 

     if (IsDataContextForwardingEnabled && dataGrid.DataContext != null) 
     { 
      if (e.Action == NotifyCollectionChangedAction.Add) 
      { 
       foreach (DataGridColumn column in e.NewItems) 
       { 
        column.SetValue(FrameworkElement.DataContextProperty, dataGrid.DataContext); 
       } 
      } 
     } 
    } 

    static DataGridColumnDataContextForwardBehavior() 
    { 
     FrameworkElement.DataContextProperty.AddOwner(typeof(DataGridColumn)); 
     FrameworkElement.DataContextProperty.OverrideMetadata(typeof(DataGrid), 
      new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnDataContextChanged))); 
    } 

    public static readonly DependencyProperty IsDataContextForwardingEnabledProperty = 
     DependencyProperty.RegisterAttached("IsDataContextForwardingEnabled", typeof(bool), typeof(DataGridColumnDataContextForwardBehavior), 
      new FrameworkPropertyMetadata(false, OnIsDataContextForwardingEnabledChanged)); 

    public static void OnDataContextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
    { 
     DataGrid dataGrid = obj as DataGrid; 
     if (dataGrid == null) return; 

     var IsDataContextForwardingEnabled = GetIsDataContextForwardingEnabled(dataGrid); 
     if (IsDataContextForwardingEnabled) 
     { 
      foreach (DataGridColumn col in dataGrid.Columns) 
      { 
       col.SetValue(FrameworkElement.DataContextProperty, e.NewValue); 
      } 
     } 
    } 

    static void OnIsDataContextForwardingEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e) 
    { 
     var dataGrid = obj as DataGrid; 
     if (dataGrid == null) return; 

     new DataGridColumnDataContextForwardBehavior(dataGrid); 

     if (!(e.NewValue is bool)) return; 

     if ((bool)e.NewValue && dataGrid.DataContext != null) 
      OnDataContextChanged(obj, new DependencyPropertyChangedEventArgs(FrameworkElement.DataContextProperty, dataGrid.DataContext, dataGrid.DataContext)); 
    } 

    public static bool GetIsDataContextForwardingEnabled(DependencyObject dataGrid) 
    { 
     return (bool)dataGrid.GetValue(IsDataContextForwardingEnabledProperty); 
    } 

    public static void SetIsDataContextForwardingEnabled(DependencyObject dataGrid, bool value) 
    { 
     dataGrid.SetValue(IsDataContextForwardingEnabledProperty, value); 
    } 
} 

另一个非明显的事情是正确的使用如何为DataGridTemplateColumn结合:

<DataGrid bhv:DataGridColumnDataContextForwardBehavior.IsDataContextForwardingEnabled="True"> 
<DataGrid.Columns> 
<DataGridTemplateColumn Visibility="{Binding Path=DataContext.Mode, RelativeSource={RelativeSource Self}, Converter={StaticResource SharedFilesModeToVisibilityConverter}, ConverterParameter={x:Static vmsf:SharedFilesMode.SharedOut}}"/> 

它使用路径是重要= DataContext.MyPropRelativeSource Self

Only thing i不喜欢在当前实现 - 处理DataGrid.Columns.CollectionChanged事件我创建我的类的实例,不保留它的参考。因此理论上GC可能会在这段时间内杀死它,不确定如何在目前正确处理它,将会考虑并稍后更新我的帖子。任何想法和批评都是受欢迎的。