WPF DataGridTemplateColumn Visibility Binding under MVVM

后端 未结 5 995
忘掉有多难
忘掉有多难 2021-01-15 00:58

I have a DataGrid bound to an ICollectionView in my ViewModel. The DataGrid is inside a UserControl which is used in a few different data scenarios, some of which require ce

相关标签:
5条回答
  • 2021-01-15 01:33

    You can't do this. Binding/name resolution doesn't work this way. Why not, instead of a StringToVisibilityConverter create a CollectionToVisibilityConverter which examines the data source (possibly passing in the column/property to examine), looks to see if that column/property is completely empty, and then convert that to a Visibility?

    0 讨论(0)
  • 2021-01-15 01:38

    I read somewhere on Stack Overflow(can't find exact post) that the DataGridColumn's aren't assigned a data context because they aren't a FrameworkElement. To get around this, I had to use code similiar to this:

        <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>
    
    0 讨论(0)
  • 2021-01-15 01:42

    To use RelativeSource.Self as a RelativeSource binding for a DataGridTemplateColumn - you need to add the DataGridContextHelper to your application. This is still required for the 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> 
    
    0 讨论(0)
  • 2021-01-15 01:42

    This would be better achieved going through the Groups property on the ViewModel; since that is ultimately what the Label is using anyways.

    <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 讨论(0)
  • 2021-01-15 01:57

    One hundred thanks to SliverNinja and this article DataGridContextHelper. Links to source code already not working and was not able download, so i wrote my own Attached Proeprty to make it work for all possible cases (DataContext changed, Attached Property value changed, Column added)

    My application use DataGrid with AutoGenerateColumns=False and use DataGridTemplateColumn, so DataContext was set before columns added to grid.

    Here is Attached Property class:

    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);
        }
    }
    

    Another non obvious things is how to properly use binding for 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}}"/>
    

    It is important to use Path=DataContext.MyProp and RelativeSource Self

    Only thing i don't like in current implementation - to handle DataGrid.Columns.CollectionChanged event i create instance of my class and do not keep reference for it. So in theory GC may kill it within the time, not sure how to handle it correctly at present moment, will think on it and update my post later. Any ideas and critique are welcome.

    0 讨论(0)
提交回复
热议问题