WPF DataGridTemplateColumn Visibility Binding under MVVM

别来无恙 提交于 2020-01-21 09:03:46

问题


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 certain DataGrid columns while others don't.

I just want to bind the DataGridTemplateColumn's Visibility property to the inner label's Content property so if none of the rows contain a value, it will be hidden. I have a String to Visibility converter set, but can't figure out how to find the inner lable's Content property.

<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>

Any suggestions?


回答1:


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>



回答2:


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> 



回答3:


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>



回答4:


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?




回答5:


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.



来源:https://stackoverflow.com/questions/5341403/wpf-datagridtemplatecolumn-visibility-binding-under-mvvm

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!